Compare commits
1243 Commits
master
...
release-6.2.6
| Author | SHA1 | Date | |
|---|---|---|---|
| 026805e4c1 | |||
| a7dad09f44 | |||
| e4aa7c068b | |||
| cc211ab3bd | |||
| cb72e6344a | |||
| bf3ddd9384 | |||
| 8a6ece4a6d | |||
| d2662c17f7 | |||
| 18c2652f67 | |||
| da0d97c108 | |||
| f672a323b0 | |||
| a59dd32dd1 | |||
| d4d5a355e3 | |||
| f4a0bcfc96 | |||
| aadae6334b | |||
| 7590bd86dd | |||
| 6b25e5299f | |||
| b30555f5a4 | |||
| d4d77611eb | |||
| 662ed03f0a | |||
| 3c01dcb30c | |||
| aa439cacda | |||
| e643f2a0ba | |||
| a32d38f7ea | |||
| 864994a56e | |||
| 3405c5001b | |||
| 4d8732d925 | |||
| 4eb9a54fa5 | |||
| 71ca90d74b | |||
| fb42472f31 | |||
| b6695b6953 | |||
| 1eab26c0f6 | |||
| 0543c620eb | |||
| 646e5f173d | |||
| b5594517bd | |||
| 246dfba8ac | |||
| 7d189a6fb8 | |||
| 71242430da | |||
| 4ee4f296fc | |||
| b9e4950e36 | |||
| ba52f9235e | |||
| 33ccf490ab | |||
| 03ba8c9e14 | |||
| 13b57c7638 | |||
| a8b297903d | |||
| 89b5774106 | |||
| 774f201514 | |||
| 12e2aa679b | |||
| 1978930fa2 | |||
| c4e4f9579b | |||
| 9782f66507 | |||
| e9938afc83 | |||
| fcda9821d0 | |||
| bdc234b18a | |||
| 908afbd264 | |||
| c548ad6a98 | |||
| 99b2a2da38 | |||
| 15ed458ad3 | |||
| 6ce303a918 | |||
| 62d3dc78fa | |||
| 3374f9e023 | |||
| 2675992d82 | |||
| ce29f54663 | |||
| 7261162d8a | |||
| b5f900e789 | |||
| fdab6d5de5 | |||
| ff94eba3a7 | |||
| e200e2fc48 | |||
| 22dde5a421 | |||
| 7f510c107a | |||
| 2ebfd8f3d4 | |||
| fd7930256b | |||
| c24775adb0 | |||
| 217f591db0 | |||
| d453aedd0d | |||
| 4693fb23c8 | |||
| 7848a3095d | |||
| 388eea95e5 | |||
| 6167d1584a | |||
| bfd0c5e309 | |||
| 43669e62b4 | |||
| 6b128ea7d4 | |||
| d5993a4783 | |||
| 86c2a6e8a9 | |||
| 874f464a2b | |||
| b3c41e051f | |||
| a88637910f | |||
| 431e93000e | |||
| 1a821b4073 | |||
| a89b9e9da6 | |||
| 8917701fa3 | |||
| 68b17bff08 | |||
| 7a12060c37 | |||
| 525280d466 | |||
| 496beca8f2 | |||
| 637a22293d | |||
| 3c8517edcb | |||
| 45e6127d00 | |||
| 6eb5d3f7ef | |||
| e6986432df | |||
| 80103e6155 | |||
| 392634eb76 | |||
| 259bc863f0 | |||
| 582bafdefd | |||
| 9924ef2381 | |||
| 859fbaaa05 | |||
| 72991334c4 | |||
| c659d5b85e | |||
| a4bf4b0977 | |||
| bef6ce66f1 | |||
| a18a2198a8 | |||
| 7cf7ac687c | |||
| 7ce0272169 | |||
| acb934e3f1 | |||
| 3aa486a463 | |||
| 5aa0621c90 | |||
| 625b8f7e65 | |||
| e27163f3a7 | |||
| 65ea6f94d3 | |||
| 9158ea1ace | |||
| e8f7f77bb6 | |||
| 75e003185a | |||
| 9d12e1c7df | |||
| b8575ef158 | |||
| d3bb750f98 | |||
| 02eff33d29 | |||
| b43f39a0cb | |||
| d0cb27d1c1 | |||
| 16a1720bf7 | |||
| ea32776174 | |||
| 320b53ae3c | |||
| d987dfb5a2 | |||
| 74806984b3 | |||
| c8c278c7bc | |||
| 13e8e972eb | |||
| a8e4c3eb9a | |||
| d631039c0d | |||
| 113b866485 | |||
| 6c6ef8d144 | |||
| 95b9a46b88 | |||
| 99250cefbb | |||
| dc651a1c42 | |||
| 8fb446960c | |||
| 4e9fd5a223 | |||
| e2c36d7212 | |||
| 62887607ff | |||
| 695cc07912 | |||
| f325794523 | |||
| 3ec6d2b82f | |||
| 46f57a3115 | |||
| d697c590e7 | |||
| a55cd67fd6 | |||
| afa81be102 | |||
| ddd0e8abb5 | |||
| e07f573683 | |||
| 1f88960b3f | |||
| d8edf67b82 | |||
| 365a8e85e9 | |||
| 5f9dddafd8 | |||
| 836297df58 | |||
| 78613823ae | |||
| 937e840104 | |||
| 313d77eb91 | |||
| 4e5019d5af | |||
| da0b935604 | |||
| b91a06ab98 | |||
| d61fb5d94d | |||
| 3b424274ee | |||
| b763c2ab2f | |||
| 16d98488e4 | |||
| 5a862f5c64 | |||
| caf41b74b8 | |||
| e858f4ff1e | |||
| 851961ef2b | |||
| c139c5d93e | |||
| c4603ee5cd | |||
| 1e9d1007b6 | |||
| f23af85253 | |||
| b0c5d16ba7 | |||
| cd5581bef6 | |||
| 7d8da56dcb | |||
| 0501b181ce | |||
| 3134668cec | |||
| a8cc8fa688 | |||
| cf2217cfe1 | |||
| e88e809e29 | |||
| c5559d7721 | |||
| 771e216813 | |||
| d33cc29f98 | |||
| b98fbbc75d | |||
| aba1862811 | |||
| 2f53698bc5 | |||
| e0e12d5773 | |||
| 2e96de5449 | |||
| c5bce3b8cc | |||
| b4286a7d4e | |||
| 31e018c045 | |||
| 50b71faff9 | |||
| 033fd1b7ff | |||
| 2621b25fd3 | |||
| 61055380c5 | |||
| adf09d925f | |||
| 12170fd787 | |||
| c32cae16db | |||
| 437d1b759b | |||
| f46811ff67 | |||
| 6d50b46bd8 | |||
| a51bf232ec | |||
| fd19411e65 | |||
| 7ac7bb9714 | |||
| 021cac9ca5 | |||
| 949447c25f | |||
| 99d7bf91b7 | |||
| a92c232178 | |||
| 30901446e1 | |||
| d772336c0f | |||
| f673cb024a | |||
| c4046ecb32 | |||
| 7823609500 | |||
| a88cb3f724 | |||
| 536b8ee921 | |||
| 3bf1041041 | |||
| fee23fac0a | |||
| cf6775ec31 | |||
| 1a13366cc9 | |||
| 595b039ebe | |||
| e6406e9757 | |||
| bf65bf5bab | |||
| 306d83cbd6 | |||
| 1da901be96 | |||
| d953966248 | |||
| 2f679a52f4 | |||
| ad8ab9f46e | |||
| 9b344a5a69 | |||
| 0a28bdafc2 | |||
| 7aa1f480c9 | |||
| 998f031877 | |||
| ac7cb8febe | |||
| 6e166bdba7 | |||
| 1f2e46a195 | |||
| bb22f5a283 | |||
| dcac5d4709 | |||
| 0e4e12d0ba | |||
| 9118701669 | |||
| 58758e7a41 | |||
| 4a516f7da9 | |||
| bf8add4f8d | |||
| 9940b78a57 | |||
| 8371105c10 | |||
| 53c0bd6938 | |||
| a768fe5bb3 | |||
| d9c66cd4b3 | |||
| 5b693f9310 | |||
| 40ebc1295c | |||
| 51660d918c | |||
| 15aa6fc4b8 | |||
| b9088c9ac0 | |||
| 49cf0f98ce | |||
| d8a3e00440 | |||
| c309e18f13 | |||
| cde3c42cec | |||
| f13ddddc16 | |||
| 89235f707c | |||
| a569796adb | |||
| 81600f0f3c | |||
| 74ccd79408 | |||
| 424ff02734 | |||
| 80bd8d962b | |||
| 956364decd | |||
| 6133e8f8c2 | |||
| bcb7561e74 | |||
| 83f4c0262e | |||
| 15222e176e | |||
| 84dd7dc61a | |||
| ff5dadde53 | |||
| ded82611a7 | |||
| 6a8cb5e123 | |||
| 09428d703c | |||
| a0541b000e | |||
| 668935548d | |||
| 14141fcaa5 | |||
| 7c5942b05f | |||
| 8b4d4e3189 | |||
| 4f008f5848 | |||
| 1a40ccc838 | |||
| e80963093d | |||
| 0749124f45 | |||
| bef970437a | |||
| dda36f0147 | |||
| caad775399 | |||
| 7a7720c460 | |||
| b2aeb835d5 | |||
| 8dd950bd52 | |||
| e2881e07d1 | |||
| 71de4dc7de | |||
| e22476f54a | |||
| 20ae16b2d7 | |||
| 9e466971af | |||
| 3c6029c969 | |||
| a4a0de3503 | |||
| 217267bf94 | |||
| 4748366766 | |||
| ab00782d84 | |||
| d19d1b3a18 | |||
| 30cfa4c0ae | |||
| d028ee2c3e | |||
| a60e2318ad | |||
| f642a7fd63 | |||
| d1d54d8e84 | |||
| bbeedb6c1b | |||
| 4745995f92 | |||
| c66aca2d41 | |||
| af148a4544 | |||
| d7ea8e02d6 | |||
| 58814d4ac2 | |||
| bd1dc4f88d | |||
| 67c1cc44c5 | |||
| b680f87c12 | |||
| 8db6633c91 | |||
| 3e5128147c | |||
| b5329ace3b | |||
| e1a134f587 | |||
| bbe6194b06 | |||
| d76fd7d25e | |||
| e020989577 | |||
| 7258964bb7 | |||
| bcc2424577 | |||
| 221bfa5f3d | |||
| 85a74e4fa7 | |||
| a8858ecfdd | |||
| 0dc00a62e2 | |||
| 3b26834484 | |||
| aee10348e5 | |||
| d0fc3ddba5 | |||
| 3aadca167f | |||
| 3a92c26a7f | |||
| ff21379547 | |||
| aed4c1faed | |||
| 48bbf1ecb2 | |||
| c500d29029 | |||
| 2c8fe5a130 | |||
| d737afa327 | |||
| ec6cb4520c | |||
| 3626d5b79d | |||
| 980e8e84a6 | |||
| fb764641c5 | |||
| b94cee68e7 | |||
| b67fbbcbd8 | |||
| 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 | |||
| b7f7775b89 | |||
| a1067e676f | |||
| ce649f54d7 | |||
| d159792256 | |||
| 606b5aa60a | |||
| a206652a0d | |||
| 545040fdf0 | |||
| 40d1a09609 | |||
| 2e25bc8fdb | |||
| 1618ac86b8 | |||
| a4adbca12c | |||
| 8728e2f918 | |||
| cd53682ddb | |||
| e2d5fbf545 | |||
| d339e7c814 | |||
| 078deb704b | |||
| 6858eb3330 | |||
| e267b7700a | |||
| 366697f4eb | |||
| 987644379e | |||
| 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 | |||
| 067268a96e | |||
| 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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET release=6.1.6
|
||||
SET release=6.2.6
|
||||
SET comment=
|
||||
SET version=%release%
|
||||
|
||||
|
||||
@@ -299,6 +299,10 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
|
||||
<PropertyGroup>
|
||||
<NewVersion>$(BUILD_RELEASE)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
|
||||
</PropertyGroup>
|
||||
<!-- Match & replace 3 and 4 digit version numbers and -beta (if it's there) -->
|
||||
<FileUpdate
|
||||
Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
@@ -308,5 +312,9 @@
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Regex="CurrentComment { get { return "([a-zA-Z]+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.targets"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -2,9 +2,9 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>6.2.0</version>
|
||||
<version>6.2.3</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Morten Christensen</authors>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
@@ -15,17 +15,23 @@
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="4.0" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="4.0" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.20710.0, 3.0.0)" />
|
||||
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.6, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.6.5]" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.7.0, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0, 2.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.8.2.1, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[4.5.11, 6.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -35,7 +41,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
@@ -66,5 +71,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.XmlSerializers.dll" target="lib\umbraco.XmlSerializers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -2,9 +2,9 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms</id>
|
||||
<version>6.1.2</version>
|
||||
<version>6.2.3</version>
|
||||
<title>Umbraco Cms</title>
|
||||
<authors>Morten Christensen</authors>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
@@ -19,11 +19,9 @@
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="Content\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="Content\bin\x86" />
|
||||
<file src="..\_BuildOutput\Configs\**" target="Content\config" exclude="..\_BuildOutput\Configs\Web.config.transform" />
|
||||
<file src="..\_BuildOutput\WebApp\css\**" target="Content\css" />
|
||||
<file src="..\_BuildOutput\WebApp\macroScripts\**" target="Content\macroScripts" />
|
||||
<file src="..\_BuildOutput\WebApp\macroScripts\**" target="Content\macroScripts" exclude="..\_BuildOutput\WebApp\macroScripts\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\masterpages\**" target="Content\masterpages" />
|
||||
<file src="..\_BuildOutput\WebApp\media\**" target="Content\media" />
|
||||
<file src="..\_BuildOutput\WebApp\scripts\**" target="Content\scripts" />
|
||||
@@ -32,15 +30,15 @@
|
||||
<file src="..\_BuildOutput\WebApp\xslt\**" target="Content\xslt" />
|
||||
<file src="..\_BuildOutput\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="Content\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Code\**" target="UmbracoFiles\App_Code" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Data\**" target="UmbracoFiles\App_Data" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Plugins\**" target="UmbracoFiles\App_Plugins" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Plugins\**" target="UmbracoFiles\App_Plugins" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\config\splashes" />
|
||||
<file src="..\_BuildOutput\WebApp\install\**" target="UmbracoFiles\install" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="UmbracoFiles\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="tools\install.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\Readme.txt" target="tools\Readme.txt" />
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<UmbracoVersion>6.2.6</UmbracoVersion>
|
||||
</PropertyGroup>
|
||||
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
|
||||
<PropertyGroup>
|
||||
<UmbracoFilesFolder>..\packages\UmbracoCms.$(UmbracoVersion)\UmbracoFiles\</UmbracoFilesFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include="$(UmbracoFilesFolder)**\*" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="%(UmbracoFiles.FullPath)" DestinationFiles="%(RecursiveDir)%(Filename)%(Extension)" Condition="!Exists('%(RecursiveDir)%(Filename)%(Extension)')" />
|
||||
</Target>
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<!-- With help from: http://blog.samstephens.co.nz/2010-10-18/msbuild-including-extra-files-multiple-builds/ -->
|
||||
<ItemGroup>
|
||||
<CustomFilesToInclude Include=".\App_Browsers\**\*">
|
||||
<Dir>App_Browsers</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\App_Code\**\*">
|
||||
<Dir>App_Code</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\App_Plugins\**\*">
|
||||
<Dir>App_Plugins</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\bin\amd64\**\*">
|
||||
<Dir>bin\amd64</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\bin\x86\**\*">
|
||||
<Dir>bin\x86</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\Config\Splashes\**\*">
|
||||
<Dir>Config\Splashes</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\umbraco\**\*">
|
||||
<Dir>umbraco</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\umbraco_client\**\*">
|
||||
<Dir>umbraco_client</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\Global.asax">
|
||||
<Dir>.</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<FilesForPackagingFromProject Include="@(CustomFilesToInclude)">
|
||||
<DestinationRelativePath>%(CustomFilesToInclude.Dir)\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="AddUmbracoFilesToOutput">
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include=".\umbraco\**\*;" />
|
||||
<UmbracoClientFiles Include=".\umbraco_client\**\*;" />
|
||||
<AppBrowsersFiles Include=".\App_Browsers\**\*" />
|
||||
<FilesForPackagingFromProject Include="%(UmbracoFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(UmbracoClientFiles.Identity)">
|
||||
<DestinationRelativePath>umbraco_client\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
<FilesForPackagingFromProject Include="%(AppBrowsersFiles.Identity)">
|
||||
<DestinationRelativePath>App_Browsers\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,8 +1,17 @@
|
||||
A note about running Umbraco from Visual Studio.
|
||||
|
||||
When upgrading your website using nuget a backup of config files and web.config will be created. Only the web.config will
|
||||
be overwritten by default to ensure that it has the necessary settings from the current release. This means that you'll
|
||||
have to merge the files if you made any changes to the previous config files.
|
||||
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
|
||||
file (and config files in the config folder).
|
||||
We will overwrite Web.config anyway but we keep a backup in the App_Data\NuGetBackup folder. There you'll
|
||||
find a folder with the current date and time in which the backup has been placed. Make sure to merge the
|
||||
new file with the old backup files before you proceed.
|
||||
The web.config will be overwritten by default to ensure that it has the necessary settings from the current release.
|
||||
|
||||
We've also overwritten all the files in the Umbraco and Umbraco_Client folder, these have also been backed up in
|
||||
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or a package
|
||||
might have added. Only the existing files were overwritten. If you customized anything then make sure to do a compare
|
||||
and merge with the NuGetBackup folder.
|
||||
|
||||
The config files found in the config folder will usually not be changed for patch releases, so they can usually be skipped,
|
||||
but the web.config will have to have its previous "umbracoConfigurationStatus"-appsetting and "umbracoDbDSN" connection string
|
||||
copied over (as a minimum).
|
||||
@@ -10,8 +19,8 @@ copied over (as a minimum).
|
||||
This nuget package includes build targets that extends the creation of a deploy package, which is generated by
|
||||
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
|
||||
Publish this won't affect you.
|
||||
These folders will now be automatically included when creating a deploy package or publishing to the file system:
|
||||
umbraco and umbraco_client.
|
||||
These things will now be automatically included when creating a deploy package or publishing to the file system:
|
||||
umbraco, umbraco_client, config\splashes and global.asax.
|
||||
|
||||
Please read the release notes on our.umbraco.org:
|
||||
http://our.umbraco.org/contribute/releases
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
param($rootPath, $toolsPath, $package, $project)
|
||||
|
||||
if ($project) {
|
||||
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
|
||||
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
|
||||
$copyLogsPath = Join-Path $backupPath "CopyLogs"
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
|
||||
# Create backup folder and logs folder if it doesn't exist yet
|
||||
New-Item -ItemType Directory -Force -Path $backupPath
|
||||
New-Item -ItemType Directory -Force -Path $copyLogsPath
|
||||
|
||||
# After backing up, remove all dlls from bin folder in case dll files are included in the VS project
|
||||
# See: http://issues.umbraco.org/issue/U4-4930
|
||||
$umbracoBinFolder = Join-Path $projectDestinationPath "bin"
|
||||
if(Test-Path $umbracoBinFolder) {
|
||||
$umbracoBinBackupPath = Join-Path $backupPath "bin"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
|
||||
|
||||
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
|
||||
Remove-Item $umbracoBinFolder\*.dll -Force -Confirm:$false
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,47 @@
|
||||
param($rootPath, $toolsPath, $package, $project)
|
||||
|
||||
if ($project) {
|
||||
# Create a backup of extisting umbraco config files
|
||||
$configPath = Join-Path (Split-Path $project.FullName -Parent) "\config"
|
||||
$backupPath = Join-Path $configPath "\backup"
|
||||
Get-ChildItem -path $configPath |
|
||||
Where -filterscript {($_.Name.EndsWith("config"))} | Foreach-Object {
|
||||
$newFileName = Join-Path $backupPath $_.Name.replace(".config",".config.backup")
|
||||
New-Item -ItemType File -Path $newFileName -Force
|
||||
Copy-Item $_.FullName $newFileName -Force
|
||||
}
|
||||
|
||||
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
|
||||
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
|
||||
$copyLogsPath = Join-Path $backupPath "CopyLogs"
|
||||
|
||||
# Create backup folder and logs folder if it doesn't exist yet
|
||||
New-Item -ItemType Directory -Force -Path $backupPath
|
||||
New-Item -ItemType Directory -Force -Path $copyLogsPath
|
||||
|
||||
# Create a backup of original web.config
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
$webConfigSource = Join-Path $projectDestinationPath "web.config"
|
||||
$webConfigDestination = Join-Path $projectDestinationPath "web.config.backup"
|
||||
Copy-Item $webConfigSource $webConfigDestination
|
||||
|
||||
# Copy umbraco files from package to project folder
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
$umbracoFilesPath = Join-Path $rootPath "UmbracoFiles\*"
|
||||
Copy-Item $umbracoFilesPath $projectDestinationPath -recurse -force
|
||||
$webConfigSource = Join-Path $projectDestinationPath "Web.config"
|
||||
Copy-Item $webConfigSource $backupPath -Force
|
||||
|
||||
# Copy Web.config from package to project folder
|
||||
$umbracoFilesPath = Join-Path $rootPath "UmbracoFiles\Web.config"
|
||||
Copy-Item $umbracoFilesPath $projectDestinationPath -Force
|
||||
|
||||
# Copy umbraco and umbraco_files from package to project folder
|
||||
# This is only done when these folders already exist because we
|
||||
# only want to do this for upgrades
|
||||
$umbracoFolder = Join-Path $projectDestinationPath "Umbraco"
|
||||
if(Test-Path $umbracoFolder) {
|
||||
$umbracoFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco"
|
||||
|
||||
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
|
||||
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
|
||||
|
||||
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
|
||||
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
|
||||
}
|
||||
|
||||
$umbracoClientFolder = Join-Path $projectDestinationPath "Umbraco_Client"
|
||||
if(Test-Path $umbracoClientFolder) {
|
||||
$umbracoClientFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco_Client"
|
||||
|
||||
$umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
|
||||
New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
|
||||
|
||||
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
|
||||
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
|
||||
}
|
||||
# Open readme.txt file
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
|
||||
}
|
||||
@@ -80,6 +80,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Sql\Total.sql" />
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Sql\Version4_1_Upgrade.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core
|
||||
|
||||
+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,6 @@ namespace Umbraco.Core.Cache
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
|
||||
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
|
||||
}
|
||||
}
|
||||
@@ -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,70 @@
|
||||
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 IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeyExpression(regexString);
|
||||
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,204 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
{
|
||||
// prefix cache keys so we know which one are ours
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
// manupulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
// read-write lock the underlying cache
|
||||
protected abstract IDisposable ReadLock { get; }
|
||||
protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
using (WriteLock)
|
||||
{
|
||||
RemoveEntry(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
// run predicate on the 'public key' part only, ie without prefix
|
||||
&& predicate(((string)x.Key).Substring(plen), (T)value);
|
||||
}))
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
Lazy<object> result;
|
||||
using (ReadLock)
|
||||
{
|
||||
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache provider that caches items in the HttpContext.Items
|
||||
/// </summary>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
{
|
||||
// context provider
|
||||
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
||||
// that is initialized with a method that returns the "current" context.
|
||||
// NOTE
|
||||
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
||||
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
||||
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
||||
//private readonly Func<HttpContextBase> _context;
|
||||
|
||||
// NOTE
|
||||
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
||||
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
||||
// _context1 is null?
|
||||
//private readonly HttpContextBase _context1;
|
||||
//private readonly Func<HttpContext> _context2;
|
||||
private readonly HttpContextBase _context;
|
||||
|
||||
private IDictionary ContextItems
|
||||
{
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
{
|
||||
//_context2 = context;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return ContextItems.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
ContextItems.Remove(key);
|
||||
}
|
||||
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
return ContextItems[key];
|
||||
}
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
// there's no difference between ReadLock and WriteLock here
|
||||
get { return WriteLock; }
|
||||
}
|
||||
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
// NOTE
|
||||
// could think about just overriding base.Locker to return a different
|
||||
// object but then we'd create a ReaderWriterLockSlim per request,
|
||||
// which is less efficient than just using a basic monitor lock.
|
||||
|
||||
get
|
||||
{
|
||||
return new MonitorLock(ContextItems.SyncRoot);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (WriteLock)
|
||||
{
|
||||
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
ContextItems[cacheKey] = result;
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,355 +2,202 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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 CacheProvider that wraps the logic of the HttpRuntime.Cache
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : RuntimeCacheProviderBase
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
{
|
||||
// locker object that supports upgradeable read locking
|
||||
// does not need to support recursion if we implement the cache correctly and ensure
|
||||
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
/// <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 IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
var cacheEnumerator = _cache.GetEnumerator();
|
||||
while (cacheEnumerator.MoveNext())
|
||||
{
|
||||
_cache.Remove(cacheEnumerator.Key.ToString());
|
||||
}
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return _cache.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
public override void ClearCacheItem(string key)
|
||||
protected override void RemoveEntry(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); ;
|
||||
}
|
||||
_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)
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (_cache[c.Key.ToString()] != null
|
||||
&& _cache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName))
|
||||
{
|
||||
_cache.Remove(c.Key.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<CacheHelper>("Cache clearing error", e);
|
||||
}
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified
|
||||
/// </summary>
|
||||
public override void ClearCacheObjectTypes<T>()
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
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);
|
||||
}
|
||||
get { return new ReadLock(_locker); }
|
||||
}
|
||||
|
||||
/// <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)
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith))
|
||||
{
|
||||
ClearCacheItem((string)c.Key);
|
||||
}
|
||||
}
|
||||
get { return new WriteLock(_locker); }
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
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;
|
||||
}
|
||||
#region Get
|
||||
|
||||
/// <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);
|
||||
}
|
||||
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
|
||||
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
|
||||
// nasty performance issues.
|
||||
|
||||
/// <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)
|
||||
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
|
||||
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
|
||||
// application lock has been released.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may produce a null value.
|
||||
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
|
||||
// So if we find an entry in the cache that already has its value created and is null,
|
||||
// pretend it was not there. If value is not already created, wait... and return null, that's
|
||||
// what prior code did.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may throw.
|
||||
|
||||
// So... the null value _will_ be in the cache but never returned
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
// Fast!
|
||||
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
|
||||
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
|
||||
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
|
||||
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
|
||||
// it's worth it, though...
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
lock (syncLock)
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
var value = result == null ? null : GetSafeLazyValue(result);
|
||||
if (value != null) return value;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as 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);
|
||||
}
|
||||
}
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
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);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
return result.TryConvertTo<TT>().Result;
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
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);
|
||||
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
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);
|
||||
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an item with a given key
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
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,67 @@ 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 IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
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,288 @@
|
||||
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 readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
{
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
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))
|
||||
{
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof (T);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
&& predicate(x.Key, (T) value);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
Lazy<object> result;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public 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)
|
||||
{
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>;
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
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,71 @@ 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 IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
var result = _staticCache[cacheKey];
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return result.TryConvertTo<T>().Result;
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, Func<T> getCacheItem)
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
return (T)_staticCache.GetOrAdd(cacheKey, getCacheItem);
|
||||
var result = StaticCache[cacheKey];
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> 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())
|
||||
{
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
_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);
|
||||
|
||||
@@ -5,10 +5,13 @@ using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
@@ -175,7 +178,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 +203,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 +215,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 +258,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 IsConfiguredMembershipProviderUsingLegacyEncoding(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 IsConfiguredMembershipProviderUsingLegacyEncoding(UmbracoSettings.DefaultBackofficeProvider);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetMembershipProvidersLegacyEncoding(UmbracoSettings.DefaultBackofficeProvider, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a setting into the configuration file.
|
||||
@@ -312,6 +349,38 @@ 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 IsConfiguredMembershipProviderUsingLegacyEncoding(string providerName)
|
||||
{
|
||||
//check if this can even be configured.
|
||||
var membershipProvider = Membership.Providers[providerName] as MembershipProviderBase;
|
||||
if (membershipProvider == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return membershipProvider.UseLegacyEncoding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to root.
|
||||
/// </summary>
|
||||
@@ -701,8 +770,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 +783,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 = "UsersMembershipProvider";
|
||||
|
||||
return defaultProvider;
|
||||
}
|
||||
@@ -841,6 +843,11 @@ namespace Umbraco.Core.Configuration
|
||||
get { return GetKeyAsNode("/settings/scheduledTasks"); }
|
||||
}
|
||||
|
||||
internal static string ScheduledTasksBaseUrl
|
||||
{
|
||||
get { return GetKey("/settings/scheduledTasks/@baseUrl"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of characters that will be replaced when generating urls
|
||||
/// </summary>
|
||||
@@ -853,6 +860,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
|
||||
@@ -1079,7 +1088,7 @@ namespace Umbraco.Core.Configuration
|
||||
bool globalPreviewEnabled = false;
|
||||
string value = GetKey("/settings/content/GlobalPreviewStorageEnabled");
|
||||
if (bool.TryParse(value, out globalPreviewEnabled))
|
||||
return !globalPreviewEnabled;
|
||||
return globalPreviewEnabled;
|
||||
// Return default
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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.6");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -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>
|
||||
@@ -89,6 +93,176 @@
|
||||
/// </summary>
|
||||
public const string Image = "Image";
|
||||
}
|
||||
|
||||
/// <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 = "umbracoMemberPasswordRetrievalQuestion";
|
||||
|
||||
public const string PasswordQuestionLabel = "Password Question";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for Members Password Answer
|
||||
/// </summary>
|
||||
public const string PasswordAnswer = "umbracoMemberPasswordRetrievalAnswer";
|
||||
|
||||
public const string PasswordAnswerLabel = "Password Answer";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Comments on a Member
|
||||
/// </summary>
|
||||
public const string Comments = "umbracoMemberComments";
|
||||
|
||||
public const string CommentsLabel = "Comments";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Approved boolean of a Member
|
||||
/// </summary>
|
||||
public const string IsApproved = "umbracoMemberApproved";
|
||||
|
||||
public const string IsApprovedLabel = "Is Approved";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Locked out boolean of a Member
|
||||
/// </summary>
|
||||
public const string IsLockedOut = "umbracoMemberLockedOut";
|
||||
|
||||
public const string IsLockedOutLabel = "Is Locked Out";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the last date the Member logged in
|
||||
/// </summary>
|
||||
public const string LastLoginDate = "umbracoMemberLastLogin";
|
||||
|
||||
public const string LastLoginDateLabel = "Last Login Date";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the last date a Member changed its password
|
||||
/// </summary>
|
||||
public const string LastPasswordChangeDate = "umbracoMemberLastPasswordChangeDate";
|
||||
|
||||
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 = "umbracoMemberLastLockoutDate";
|
||||
|
||||
public const string LastLockoutDateLabel = "Last Lockout Date";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the number of failed login attemps
|
||||
/// </summary>
|
||||
public const string FailedPasswordAttempts = "umbracoMemberFailedPasswordAttempts";
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the constants used for Umbraco packages in the package.config xml
|
||||
/// </summary>
|
||||
public static class Packaging
|
||||
{
|
||||
public const string UmbPackageNodeName = "umbPackage";
|
||||
public const string DataTypesNodeName = "DataTypes";
|
||||
public const string PackageXmlFileName = "package.xml";
|
||||
public const string UmbracoPackageExtention = ".umb";
|
||||
public const string DataTypeNodeName = "DataType";
|
||||
public const string LanguagesNodeName = "Languages";
|
||||
public const string FilesNodeName = "Files";
|
||||
public const string StylesheetsNodeName = "Stylesheets";
|
||||
public const string TemplatesNodeName = "Templates";
|
||||
public const string OrgnameNodeName = "orgName";
|
||||
public const string NameNodeName = "Name";
|
||||
public const string TemplateNodeName = "Template";
|
||||
public const string AliasNodeName = "Alias";
|
||||
public const string DictionaryitemsNodeName = "DictionaryItems";
|
||||
public const string MacrosNodeName = "macros";
|
||||
public const string DocumentSetNodeName = "DocumentSet";
|
||||
public const string DocumentTypesNodeName = "DocumentTypes";
|
||||
public const string DocumentTypeNodeName = "DocumentType";
|
||||
public const string FileNodeName = "file";
|
||||
public const string OrgPathNodeName = "orgPath";
|
||||
public const string GuidNodeName = "guid";
|
||||
public const string StylesheetNodeName = "styleSheet";
|
||||
public const string MacroNodeName = "macro";
|
||||
public const string InfoNodeName = "info";
|
||||
public const string PackageRequirementsMajorXpath = "/package/requirements/major";
|
||||
public const string PackageRequirementsMinorXpath = "/package/requirements/minor";
|
||||
public const string PackageRequirementsPatchXpath = "/package/requirements/patch";
|
||||
public const string PackageNameXpath = "/package/name";
|
||||
public const string PackageVersionXpath = "/package/version";
|
||||
public const string PackageUrlXpath = "/package/url";
|
||||
public const string PackageLicenseXpath = "/package/license";
|
||||
public const string AuthorNameXpath = "/author/name";
|
||||
public const string AuthorWebsiteXpath = "/author/website";
|
||||
public const string ReadmeXpath = "/readme";
|
||||
public const string ControlNodeName = "control";
|
||||
public const string ActionNodeName = "Action";
|
||||
public const string ActionsNodeName = "Actions";
|
||||
public const string UndoNodeAttribute = "undo";
|
||||
public const string RunatNodeAttribute = "runat";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) })
|
||||
@@ -253,28 +275,27 @@ namespace Umbraco.Core
|
||||
|
||||
//the database migration objects
|
||||
MigrationResolver.Current = new MigrationResolver(
|
||||
() => PluginManager.Current.ResolveMigrationTypes());
|
||||
|
||||
() => PluginManager.Current.ResolveTypes<IMigration>());
|
||||
|
||||
|
||||
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);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Configuration;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -28,7 +29,7 @@ namespace Umbraco.Core
|
||||
private string _providerName;
|
||||
private DatabaseSchemaResult _result;
|
||||
|
||||
internal DatabaseContext(IDatabaseFactory factory)
|
||||
public DatabaseContext(IDatabaseFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
@@ -426,6 +427,13 @@ namespace Umbraco.Core
|
||||
|
||||
if (_result == null)
|
||||
{
|
||||
|
||||
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
|
||||
&& ProviderName == "MySql.Data.MySqlClient")
|
||||
{
|
||||
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
|
||||
}
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
var dbSchema = new DatabaseSchemaCreation(database);
|
||||
_result = dbSchema.ValidateSchema();
|
||||
@@ -433,26 +441,26 @@ namespace Umbraco.Core
|
||||
return _result;
|
||||
}
|
||||
|
||||
internal Result CreateDatabaseSchemaAndDataOrUpgrade()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
{
|
||||
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"
|
||||
};
|
||||
}
|
||||
|
||||
internal Result CreateDatabaseSchemaAndData()
|
||||
{
|
||||
try
|
||||
{
|
||||
var readyForInstall = CheckReadyForInstall();
|
||||
if (readyForInstall.Success == false)
|
||||
{
|
||||
return readyForInstall.Result;
|
||||
}
|
||||
|
||||
LogHelper.Info<DatabaseContext>("Database configuration status: Started");
|
||||
|
||||
var message = string.Empty;
|
||||
string message;
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
|
||||
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
|
||||
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
|
||||
|
||||
/*
|
||||
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
if (supportsCaseInsensitiveQueries == false)
|
||||
{
|
||||
@@ -465,50 +473,75 @@ 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();
|
||||
|
||||
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
|
||||
|
||||
//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()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var readyForInstall = CheckReadyForInstall();
|
||||
if (readyForInstall.Success == false)
|
||||
{
|
||||
var configuredVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
|
||||
return readyForInstall.Result;
|
||||
}
|
||||
|
||||
LogHelper.Info<DatabaseContext>("Database upgrade started");
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
//var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
|
||||
var message = GetResultMessageForMySql();
|
||||
|
||||
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 +551,95 @@ 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()
|
||||
{
|
||||
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 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 (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
|
||||
&& ProviderName == "MySql.Data.MySqlClient")
|
||||
{
|
||||
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -36,42 +36,22 @@ namespace Umbraco.Core.Dynamics
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, IEnumerable<string> needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Any())
|
||||
{
|
||||
return needles.Any(haystack.Contains);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, params string[] needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Length > 0)
|
||||
{
|
||||
return needles.Any(haystack.Contains);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, StringComparison comparison, IEnumerable<string> needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Any())
|
||||
{
|
||||
return needles.Any(value => haystack.IndexOf(value, comparison) >= 0);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles, comparison);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, StringComparison comparison, params string[] needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Length > 0)
|
||||
{
|
||||
return needles.Any(value => haystack.IndexOf(value, comparison) >= 0);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles, comparison);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsInsensitive(this string haystack, string needle)
|
||||
|
||||
@@ -1,65 +1,50 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
internal class PropertyResult : IPublishedContentProperty, IHtmlString
|
||||
{
|
||||
{
|
||||
private readonly IPublishedContentProperty _source;
|
||||
private readonly string _alias;
|
||||
private readonly object _value;
|
||||
private readonly PropertyResultType _type;
|
||||
|
||||
internal PropertyResult(IPublishedContentProperty 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 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(); } }
|
||||
|
||||
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 Alias { get { return PropertyTypeAlias; }}
|
||||
public Guid Version { get { return Guid.Empty; }}
|
||||
|
||||
// implements IHtmlString.ToHtmlString
|
||||
public string ToHtmlString()
|
||||
{
|
||||
return ValueAsString;
|
||||
var value = Value;
|
||||
return value == null ? string.Empty : value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
@@ -13,17 +14,16 @@ 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 ArgumentNullException("source");
|
||||
if (groupSize <= 0)
|
||||
throw new ArgumentException("Must be greater than zero.", "groupSize");
|
||||
|
||||
while ((i * groupSize) < length)
|
||||
{
|
||||
yield return source.Skip(i * groupSize).Take(groupSize);
|
||||
i++;
|
||||
}
|
||||
return source
|
||||
.Select((x, i) => Tuple.Create(i / groupSize, x))
|
||||
.GroupBy(t => t.Item1, t => t.Item2);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>The distinct by.</summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="keySelector">The key selector.</param>
|
||||
@@ -96,23 +96,13 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>The flatten list.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="selectChild">The select child.</param>
|
||||
/// <typeparam name="TItem">Item type</typeparam>
|
||||
/// <param name="e">The items.</param>
|
||||
/// <param name="f">The select child.</param>
|
||||
/// <typeparam name="T">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
|
||||
public static IEnumerable<TItem> FlattenList<TItem>(this IEnumerable<TItem> items, Func<TItem, IEnumerable<TItem>> selectChild)
|
||||
public static IEnumerable<T> FlattenList<T>(this IEnumerable<T> e, Func<T, IEnumerable<T>> f)
|
||||
{
|
||||
IEnumerable<TItem> children = items != null && items.Any()
|
||||
? items.SelectMany(selectChild).FlattenList(selectChild)
|
||||
: Enumerable.Empty<TItem>();
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
return items.Concat(children);
|
||||
}
|
||||
|
||||
return null;
|
||||
return e.SelectMany(c => f(c).FlattenList(f)).Concat(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -243,23 +233,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>
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Events
|
||||
/// <remarks>
|
||||
/// This is protected so that inheritors can expose it with their own name
|
||||
/// </remarks>
|
||||
protected T EventObject { get; private set; }
|
||||
protected T EventObject { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public MoveEventArgs(TEntity eventObject, bool canCancel, int parentId) : base(eventObject, canCancel)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
public class MoveEventInfo<TEntity>
|
||||
{
|
||||
public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
|
||||
{
|
||||
Entity = entity;
|
||||
OriginalPath = originalPath;
|
||||
NewParentId = newParentId;
|
||||
}
|
||||
|
||||
public MoveEventArgs(TEntity eventObject, int parentId) : base(eventObject)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
public TEntity Entity { get; set; }
|
||||
public string OriginalPath { get; set; }
|
||||
public int NewParentId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity being moved
|
||||
/// </summary>
|
||||
public TEntity Entity
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
/// </summary>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="moveInfo">
|
||||
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
|
||||
/// </param>
|
||||
public MoveEventArgs(bool canCancel, params MoveEventInfo<TEntity>[] moveInfo)
|
||||
: base(default(TEntity), canCancel)
|
||||
{
|
||||
if (moveInfo.FirstOrDefault() == null)
|
||||
{
|
||||
throw new ArgumentException("moveInfo argument must contain at least one item");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
MoveInfoCollection = moveInfo;
|
||||
//assign the legacy props
|
||||
EventObject = moveInfo.First().Entity;
|
||||
ParentId = moveInfo.First().NewParentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
/// </summary>
|
||||
/// <param name="moveInfo">
|
||||
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
|
||||
/// </param>
|
||||
public MoveEventArgs(params MoveEventInfo<TEntity>[] moveInfo)
|
||||
: base(default(TEntity))
|
||||
{
|
||||
if (moveInfo.FirstOrDefault() == null)
|
||||
{
|
||||
throw new ArgumentException("moveInfo argument must contain at least one item");
|
||||
}
|
||||
|
||||
MoveInfoCollection = moveInfo;
|
||||
//assign the legacy props
|
||||
EventObject = moveInfo.First().Entity;
|
||||
ParentId = moveInfo.First().NewParentId;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload that specifies the MoveEventInfo object")]
|
||||
public MoveEventArgs(TEntity eventObject, bool canCancel, int parentId)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload that specifies the MoveEventInfo object")]
|
||||
public MoveEventArgs(TEntity eventObject, int parentId)
|
||||
: base(eventObject)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all MoveEventInfo objects used to create the object
|
||||
/// </summary>
|
||||
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity being moved
|
||||
/// </summary>
|
||||
[Obsolete("Retrieve the entity object from the MoveInfoCollection property instead")]
|
||||
public TEntity Entity
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Id of the object's new parent
|
||||
/// </summary>
|
||||
[Obsolete("Retrieve the ParentId from the MoveInfoCollection property instead")]
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,12 @@ namespace Umbraco.Core
|
||||
AddInt(d.GetHashCode());
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
|
||||
}
|
||||
|
||||
internal void AddCaseInsensitiveString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
|
||||
@@ -1,37 +1,50 @@
|
||||
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));
|
||||
using (var stream = fs.OpenFile(path))
|
||||
{
|
||||
fs.AddFile(newPath, stream);
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
@@ -93,8 +93,9 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideIfExists)
|
||||
{
|
||||
if (FileExists(path) && !overrideIfExists) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
var exists = FileExists(path);
|
||||
if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
EnsureDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
if (stream.CanSeek)
|
||||
|
||||
@@ -133,16 +133,23 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (_size == null)
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
if (_fs.FileExists(Path))
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
var fs = _fs.OpenFile(Path);
|
||||
var image = Image.FromStream(fs);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
fs.Close();
|
||||
image.Dispose();
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = new Size(-1, -1);
|
||||
}
|
||||
}
|
||||
return _size.Value;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -234,6 +232,7 @@ namespace Umbraco.Core.Models
|
||||
/// <remarks>
|
||||
/// This Property is kept internal until localization is introduced.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
internal string NodeName
|
||||
{
|
||||
get { return _nodeName; }
|
||||
@@ -250,6 +249,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Used internally to track if permissions have been changed during the saving process for this entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal bool PermissionsChanged
|
||||
{
|
||||
get { return _permissionsChanged; }
|
||||
@@ -316,6 +316,7 @@ namespace Umbraco.Core.Models
|
||||
PublishedState = state;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
internal PublishedState PublishedState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -335,26 +336,6 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the current entity
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent Clone()
|
||||
{
|
||||
var clone = (Content)this.MemberwiseClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.Version = Guid.NewGuid();
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
{
|
||||
property.ResetIdentity();
|
||||
property.Version = clone.Version;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current <see cref="IContent"/> entity is dirty.
|
||||
/// </summary>
|
||||
@@ -434,5 +415,51 @@ namespace Umbraco.Core.Models
|
||||
base.UpdatingEntity();
|
||||
Version = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContent Clone()
|
||||
{
|
||||
return DeepCloneWithResetIdentities();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent DeepCloneWithResetIdentities()
|
||||
{
|
||||
var clone = (Content)DeepClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.Version = Guid.NewGuid();
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
{
|
||||
property.ResetIdentity();
|
||||
property.Version = clone.Version;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Content)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IContentType)ContentType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -14,10 +15,13 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Represents an abstract class for base Content properties and methods
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}, ContentType: {ContentTypeBase.Alias}")]
|
||||
public abstract class ContentBase : Entity, IContentBase
|
||||
{
|
||||
protected IContentTypeComposition ContentTypeBase;
|
||||
|
||||
private Lazy<int> _parentId;
|
||||
private string _name;//NOTE Once localization is introduced this will be the localized Name of the Content/Media.
|
||||
private int _sortOrder;
|
||||
@@ -50,6 +54,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 +78,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 +259,17 @@ 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 +310,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>
|
||||
@@ -433,6 +448,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Returns a collection of the result of the last validation process, this collection contains all invalid properties.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal IEnumerable<Property> LastInvalidProperties
|
||||
{
|
||||
get { return _lastInvalidProperties; }
|
||||
@@ -454,5 +470,6 @@ namespace Umbraco.Core.Models
|
||||
prop.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -208,6 +222,28 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes characters that are not valide XML characters from all entity properties
|
||||
/// of type string. See: http://stackoverflow.com/a/961504/5018
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
|
||||
/// </remarks>
|
||||
/// <param name="entity"></param>
|
||||
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
|
||||
{
|
||||
entity.Name = entity.Name.ToValidXmlString();
|
||||
foreach (var property in entity.Properties)
|
||||
{
|
||||
if (property.Value is string)
|
||||
{
|
||||
var value = (string)property.Value;
|
||||
property.Value = value.ToValidXmlString();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the IContentBase has children
|
||||
/// </summary>
|
||||
@@ -363,8 +399,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 +413,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 +502,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 +541,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 +649,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 +671,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used content repository in order to add an entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentPreviewEntity<TContent> : ContentXmlEntity<TContent>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentPreviewEntity(TContent content, Func<TContent, XElement> xml)
|
||||
: base(content, xml)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Version
|
||||
{
|
||||
get { return Content.Version; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,98 +121,6 @@ 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>
|
||||
/// <returns></returns>
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
var clone = (ContentType)this.MemberwiseClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
var propertyGroups = this.PropertyGroups.Select(x => x.Clone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
clone.PropertyTypes = this.PropertyTypeCollection.Select(x => x.Clone()).ToList();
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyType in clone.PropertyTypes.Where(x => x.HasIdentity))
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
@@ -225,13 +133,41 @@ namespace Umbraco.Core.Models
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
return DeepCloneWithResetIdentities(alias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContentType DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (ContentType)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
propertyGroup.ResetDirtyProperties(false);
|
||||
}
|
||||
foreach (var propertyType in clone.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -42,6 +44,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 +56,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);
|
||||
@@ -168,7 +174,8 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value.ToSafeAlias();
|
||||
//_alias = value.ToSafeAlias();
|
||||
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
@@ -314,6 +321,16 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private 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>
|
||||
@@ -334,7 +351,9 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this ContentType
|
||||
/// </summary>
|
||||
/// <remarks>A PropertyGroup corresponds to a Tab in the UI</remarks>
|
||||
/// <remarks>
|
||||
/// A PropertyGroup corresponds to a Tab in the UI
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual PropertyGroupCollection PropertyGroups
|
||||
{
|
||||
@@ -350,7 +369,13 @@ namespace Umbraco.Core.Models
|
||||
/// List of PropertyTypes available on this ContentType.
|
||||
/// This list aggregates PropertyTypes across the PropertyGroups.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
|
||||
/// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
|
||||
/// data. We manually clone this instead.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DoNotClone]
|
||||
public virtual IEnumerable<PropertyType> PropertyTypes
|
||||
{
|
||||
get
|
||||
@@ -366,6 +391,14 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property type collection containing types that are non-groups - used for tests
|
||||
/// </summary>
|
||||
internal IEnumerable<PropertyType> NonGroupedPropertyTypes
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A boolean flag indicating if a property type has been removed from this instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -415,8 +448,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
if (PropertyTypeExists(propertyType.Alias) == false)
|
||||
{
|
||||
_propertyTypes.Add(propertyType);
|
||||
_propertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
_propertyTypes.Add(propertyType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -503,5 +535,84 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeBase)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually wire up the event handlers for the property type collections - we've ensured
|
||||
// its ignored from the auto-clone process because its return values are unions, not raw and
|
||||
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
|
||||
|
||||
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
|
||||
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
|
||||
{
|
||||
private readonly List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
private List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
internal List<int> RemovedContentTypeKeyTracker = new List<int>();
|
||||
|
||||
protected ContentTypeCompositionBase(int parentId) : base(parentId)
|
||||
@@ -217,5 +217,21 @@ namespace Umbraco.Core.Models
|
||||
.Select(x => x.Id)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeCompositionBase)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since this is an internal field and will not be automatically mapped
|
||||
clone.RemovedContentTypeKeyTracker = new List<int>();
|
||||
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Represents a POCO for setting sort order on a ContentType reference
|
||||
/// </summary>
|
||||
public class ContentTypeSort : IValueObject
|
||||
public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
{
|
||||
public ContentTypeSort()
|
||||
{
|
||||
@@ -33,5 +33,36 @@ namespace Umbraco.Core.Models
|
||||
/// Gets or sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
public string Alias { get; set; }
|
||||
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeSort)MemberwiseClone();
|
||||
var id = Id.Value;
|
||||
clone.Id = new Lazy<int>(() => id);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(ContentTypeSort other)
|
||||
{
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ContentTypeSort) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Id.GetHashCode()*397) ^ Alias.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in content/media/member repositories in order to add this type of entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentXmlEntity<TContent> : IAggregateRoot
|
||||
where TContent : IContentBase
|
||||
{
|
||||
private readonly Func<TContent, XElement> _xml;
|
||||
|
||||
public ContentXmlEntity(TContent content, Func<TContent, XElement> xml)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
_xml = xml;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public ContentXmlEntity(TContent content)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public XElement Xml
|
||||
{
|
||||
get { return _xml(Content); }
|
||||
}
|
||||
|
||||
public TContent Content { get; private set; }
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return Content.Id; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public Guid Key { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Special case, always return false, this will cause the repositories managing
|
||||
/// this object to always do an 'insert' but these are special repositories that
|
||||
/// do an InsertOrUpdate on insert since the data for this needs to be managed this way
|
||||
/// </summary>
|
||||
public bool HasIdentity
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentXmlEntity<TContent>)MemberwiseClone();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to attribute properties that have a setter and are a reference type
|
||||
/// that should be ignored for cloning when using the DeepCloneHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// This attribute must be used:
|
||||
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
|
||||
///
|
||||
/// This attribute should not be used:
|
||||
/// * when the property is virtual
|
||||
/// * when the setter performs additional required logic other than just setting the underlying field
|
||||
///
|
||||
/// </remarks>
|
||||
internal class DoNotCloneAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class DeepCloneHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to avoid constant reflection (perf)
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
public static void DeepCloneRefProperties(IDeepCloneable input, IDeepCloneable output)
|
||||
{
|
||||
var inputType = input.GetType();
|
||||
var outputType = output.GetType();
|
||||
|
||||
if (inputType != outputType)
|
||||
{
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
var refProperties = PropCache.GetOrAdd(inputType, type =>
|
||||
inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
Attribute.GetCustomAttribute(x, typeof(DoNotCloneAttribute)) == null
|
||||
//reference type but not string
|
||||
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false)
|
||||
.ToArray());
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
|
||||
{
|
||||
//this ref property is also deep cloneable so clone it
|
||||
var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
//set the cloned value to the property
|
||||
propertyInfo.SetValue(output, result.DeepClone(), null);
|
||||
}
|
||||
}
|
||||
else if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
|
||||
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
|
||||
{
|
||||
IList newList;
|
||||
if (propertyInfo.PropertyType.IsGenericType
|
||||
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||
{
|
||||
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
|
||||
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
|
||||
newList = (IList)Activator.CreateInstance(genericType);
|
||||
}
|
||||
else if (propertyInfo.PropertyType.IsArray
|
||||
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
|
||||
{
|
||||
//if its an array, we'll create a list to work with first and then convert to array later
|
||||
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
|
||||
newList = new List<object>();
|
||||
}
|
||||
else
|
||||
{
|
||||
//its a custom IEnumerable, we'll try to create it
|
||||
try
|
||||
{
|
||||
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
|
||||
//if it's an IList we can work with it, otherwise we cannot
|
||||
newList = custom as IList;
|
||||
if (newList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//could not create this type so we'll skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
|
||||
if (enumerable == null) continue;
|
||||
|
||||
var isUsableType = true;
|
||||
|
||||
//now clone each item
|
||||
foreach (var o in enumerable)
|
||||
{
|
||||
//first check if the item is deep cloneable and copy that way
|
||||
var dc = o as IDeepCloneable;
|
||||
if (dc != null)
|
||||
{
|
||||
newList.Add(dc.DeepClone());
|
||||
}
|
||||
else if (o is string || o.GetType().IsValueType)
|
||||
{
|
||||
//check if the item is a value type or a string, then we can just use it
|
||||
newList.Add(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
//this will occur if the item is not a string or value type or IDeepCloneable, in this case we cannot
|
||||
// clone each element, we'll need to skip this property, people will have to manually clone this list
|
||||
isUsableType = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if this was not usable, skip this property
|
||||
if (isUsableType == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyInfo.PropertyType.IsArray)
|
||||
{
|
||||
//need to convert to array
|
||||
var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
|
||||
for (int i = 0; i < newList.Count; i++)
|
||||
{
|
||||
arr[i] = newList[i];
|
||||
}
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, arr, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, newList, null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -97,5 +97,6 @@ namespace Umbraco.Core.Models
|
||||
if(ParentId == Guid.Empty)
|
||||
_parentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -65,5 +65,6 @@ namespace Umbraco.Core.Models
|
||||
}, _value, ValueSelector);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
@@ -93,7 +96,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /// Gets or sets the WasCancelled flag, which is used to track
|
||||
/// Gets or sets the WasCancelled flag, which is used to track
|
||||
/// whether some action against an entity was cancelled through some event.
|
||||
/// This only exists so we have a way to check if an event was cancelled through
|
||||
/// the new api, which also needs to take effect in the legacy api.
|
||||
@@ -155,6 +158,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity has an identity, eg. Id.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual bool HasIdentity
|
||||
{
|
||||
get
|
||||
@@ -226,5 +230,70 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
|
||||
return _hash.Value;
|
||||
}
|
||||
|
||||
public virtual object DeepClone()
|
||||
{
|
||||
//Memberwise clone on Entity will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var clone = (Entity)MemberwiseClone();
|
||||
//ensure the clone has it's own dictionaries
|
||||
clone.ResetChangeTrackingCollections();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
//Using data contract serializer - has issues
|
||||
|
||||
//var s = Serialize(this);
|
||||
//var d = Deserialize(s, this.GetType());
|
||||
//return d;
|
||||
|
||||
//Using binary serializer - has issues
|
||||
|
||||
//using (var memoryStream = new MemoryStream(10))
|
||||
//{
|
||||
//IFormatter formatter = new BinaryFormatter();
|
||||
//formatter.Serialize(memoryStream, this);
|
||||
//memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
//return formatter.Deserialize(memoryStream);
|
||||
//}
|
||||
}
|
||||
|
||||
// serialize/deserialize with data contracts:
|
||||
|
||||
//public static string Serialize(object obj)
|
||||
//{
|
||||
// using (var memoryStream = new MemoryStream())
|
||||
// using (var reader = new StreamReader(memoryStream))
|
||||
// {
|
||||
// var serializer = new DataContractSerializer(obj.GetType());
|
||||
// serializer.WriteObject(memoryStream, obj);
|
||||
// memoryStream.Position = 0;
|
||||
// return reader.ReadToEnd();
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static object Deserialize(string xml, Type toType)
|
||||
//{
|
||||
// using (Stream stream = new MemoryStream())
|
||||
// {
|
||||
// using (var writer = new StreamWriter(stream, Encoding.UTF8))
|
||||
// {
|
||||
// writer.Write(xml);
|
||||
// //byte[] data = Encoding.UTF8.GetBytes(xml);
|
||||
// //stream.Write(data, 0, data.Length);
|
||||
// stream.Position = 0;
|
||||
// var deserializer = new DataContractSerializer(toType);
|
||||
// return deserializer.ReadObject(stream);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// </summary>
|
||||
/// <remarks>The current database schema doesn't provide a modified date
|
||||
/// for all entities, so this will have to be changed at a later stage.</remarks>
|
||||
public interface IEntity
|
||||
public interface IEntity : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id of the entity
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,31 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for use to implement IRememberBeingDirty/ICanBeDirty
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class TracksChangesEntityBase : IRememberBeingDirty
|
||||
{
|
||||
|
||||
//TODO: This needs to go on to ICanBeDirty http://issues.umbraco.org/issue/U4-5662
|
||||
public virtual IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
return _propertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
private bool _changeTrackingEnabled = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the properties that have changed
|
||||
/// </summary>
|
||||
private readonly IDictionary<string, bool> _propertyChangedInfo = new Dictionary<string, bool>();
|
||||
private IDictionary<string, bool> _propertyChangedInfo = new Dictionary<string, bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the properties that we're changed before the last commit (or last call to ResetDirtyProperties)
|
||||
@@ -32,6 +45,9 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// <param name="propertyInfo">The property info.</param>
|
||||
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
|
||||
{
|
||||
//return if we're not tracking changes
|
||||
if (_changeTrackingEnabled == false) return;
|
||||
|
||||
_propertyChangedInfo[propertyInfo.Name] = true;
|
||||
|
||||
if (PropertyChanged != null)
|
||||
@@ -83,7 +99,9 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// </summary>
|
||||
public void ForgetPreviouslyDirtyProperties()
|
||||
{
|
||||
_lastPropertyChangedInfo.Clear();
|
||||
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_lastPropertyChangedInfo = new Dictionary<string, bool>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,7 +134,25 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
_lastPropertyChangedInfo = _propertyChangedInfo.ToDictionary(v => v.Key, v => v.Value);
|
||||
}
|
||||
|
||||
_propertyChangedInfo.Clear();
|
||||
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
|
||||
// instance as the one on the clone, so we need to create a new instance.
|
||||
_propertyChangedInfo = new Dictionary<string, bool>();
|
||||
}
|
||||
|
||||
protected void ResetChangeTrackingCollections()
|
||||
{
|
||||
_propertyChangedInfo = new Dictionary<string, bool>();
|
||||
_lastPropertyChangedInfo = new Dictionary<string, bool>();
|
||||
}
|
||||
|
||||
protected void DisableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = false;
|
||||
}
|
||||
|
||||
protected void EnableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -136,12 +172,20 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
var initVal = value;
|
||||
var newVal = setValue(value);
|
||||
|
||||
if (!Equals(initVal, newVal))
|
||||
|
||||
//don't track changes, just set the value (above)
|
||||
if (_changeTrackingEnabled == false) return false;
|
||||
|
||||
if (Equals(initVal, newVal) == false)
|
||||
{
|
||||
OnPropertyChanged(propertySelector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Linq;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,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 +34,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
return new FileInfo(Path).Name;
|
||||
if (_name == null)
|
||||
{
|
||||
_name = System.IO.Path.GetFileName(Path);
|
||||
}
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +50,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 +70,10 @@ namespace Umbraco.Core.Models
|
||||
get { return _path; }
|
||||
set
|
||||
{
|
||||
//reset
|
||||
_alias = null;
|
||||
_name = null;
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_path = value;
|
||||
@@ -90,5 +104,21 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <returns>True if file is valid, otherwise false</returns>
|
||||
public abstract bool IsValid();
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (File)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since they are readonly properties
|
||||
clone._alias = Alias;
|
||||
clone._name = Name;
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -72,5 +72,11 @@ namespace Umbraco.Core.Models
|
||||
/// Changes the Published state of the content object
|
||||
/// </summary>
|
||||
void ChangePublishedState(PublishedState state);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IContent DeepCloneWithResetIdentities();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -30,5 +29,12 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
bool RemoveTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IContentType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDeepCloneable
|
||||
{
|
||||
object DeepClone();
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <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<IPublishedContentProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property value for the property alias specified
|
||||
/// </summary>
|
||||
/// <param name="propertyAlias"></param>
|
||||
/// <returns></returns>
|
||||
object this[string propertyAlias] { 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>
|
||||
IPublishedContentProperty GetProperty(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a property on the object based on an 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>
|
||||
IPublishedContentProperty 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 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IPublishedContentProperty
|
||||
{
|
||||
string Alias { get; }
|
||||
object Value { get; }
|
||||
Guid Version { get; }
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedContentProperty
|
||||
{
|
||||
/// <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; }
|
||||
|
||||
string Alias { get; }
|
||||
Guid Version { 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,654 @@
|
||||
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 IMemberType _contentType;
|
||||
private readonly string _contentTypeAlias;
|
||||
private string _username;
|
||||
private string _email;
|
||||
private string _rawPasswordValue;
|
||||
private object _providerUserKey;
|
||||
private Type _userTypeKey;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating an empty Member object
|
||||
/// </summary>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
public Member(IMemberType contentType)
|
||||
: base("", -1, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentTypeAlias = contentType.Alias;
|
||||
_contentType = contentType;
|
||||
IsApproved = true;
|
||||
|
||||
//this cannot be null but can be empty
|
||||
_rawPasswordValue = "";
|
||||
_email = "";
|
||||
_username = "";
|
||||
}
|
||||
|
||||
/// <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)
|
||||
: this(contentType)
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
Mandate.ParameterNotNullOrEmpty(name, "name");
|
||||
|
||||
_contentTypeAlias = contentType.Alias;
|
||||
_contentType = contentType;
|
||||
IsApproved = true;
|
||||
|
||||
//this cannot be null but can be empty
|
||||
_rawPasswordValue = "";
|
||||
_email = "";
|
||||
_username = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Member object
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="contentType"></param>
|
||||
public Member(string name, string email, string username, IMemberType contentType)
|
||||
: base(name, -1, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
Mandate.ParameterNotNullOrEmpty(name, "name");
|
||||
Mandate.ParameterNotNullOrEmpty(email, "email");
|
||||
Mandate.ParameterNotNullOrEmpty(username, "username");
|
||||
|
||||
_contentTypeAlias = contentType.Alias;
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
IsApproved = true;
|
||||
|
||||
//this cannot be null but can be empty
|
||||
_rawPasswordValue = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Member object
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="rawPasswordValue">
|
||||
/// The password value passed in to this parameter should be the encoded/encrypted/hashed format of the member's password
|
||||
/// </param>
|
||||
/// <param name="contentType"></param>
|
||||
public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType)
|
||||
: base(name, -1, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentTypeAlias = contentType.Alias;
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_rawPasswordValue = rawPasswordValue;
|
||||
IsApproved = true;
|
||||
}
|
||||
|
||||
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.RawPasswordValue);
|
||||
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 raw password value
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public string RawPasswordValue
|
||||
{
|
||||
get { return _rawPasswordValue; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_rawPasswordValue = value;
|
||||
return _rawPasswordValue;
|
||||
}, _rawPasswordValue, 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>
|
||||
[DataMember]
|
||||
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 raw password answer value
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For security reasons this value should be encrypted, the encryption process is handled by the memberhip provider
|
||||
///
|
||||
/// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public string RawPasswordAnswerValue
|
||||
{
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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>
|
||||
[DataMember]
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <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.
|
||||
*/
|
||||
[IgnoreDataMember]
|
||||
internal string LongStringPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal string ShortStringPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal int IntegerropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal bool BoolPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal DateTime DateTimePropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
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;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Member)base.DeepClone();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IMemberType)ContentType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,148 @@
|
||||
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 == null ? string.Empty : 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; }
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user