Compare commits
776 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17d4258507 | |||
| 080150baa3 | |||
| c6bc5ae6aa | |||
| f84d3b435b | |||
| 99855bce11 | |||
| d0b2f10a8c | |||
| bbfd570b8a | |||
| 7b4912b1e7 | |||
| 8c6362d947 | |||
| d012d8e8b6 | |||
| 9792d8df25 | |||
| 85d10014c2 | |||
| fcf5ecc166 | |||
| 3b236ba724 | |||
| 3e294e290b | |||
| 0eafa14246 | |||
| b807b84466 | |||
| ab551b2c96 | |||
| 1520fa77d5 | |||
| d91fc6ce60 | |||
| 8461aea7d8 | |||
| 9fa76a0133 | |||
| 3242b31a8a | |||
| 2be330d237 | |||
| 4381a700ba | |||
| 99821954f1 | |||
| 10a15be8ad | |||
| 0a2830aece | |||
| bed5f8a03b | |||
| 7cd385dd6b | |||
| 5d4a97591a | |||
| 67e3ec2ae0 | |||
| b88e714050 | |||
| da8c46e426 | |||
| 62af834176 | |||
| 2dac1b3cf5 | |||
| 68fb951283 | |||
| 1ca825de62 | |||
| 13ccd86192 | |||
| 336266c0eb | |||
| 54b57f7d51 | |||
| 2f6e161120 | |||
| f2c7c1e2c6 | |||
| 9f2c428a7c | |||
| a958f946ef | |||
| 688652fb91 | |||
| 4ef1eeb8bc | |||
| d6bc0934ff | |||
| 759c6dfef2 | |||
| de9739118e | |||
| 218f523022 | |||
| 50c6fb036a | |||
| bc2115ff0a | |||
| 9eee5c2bc0 | |||
| ed3096642d | |||
| 95508c4fb7 | |||
| 4677faff38 | |||
| 725b3c5eca | |||
| 25f5179b29 | |||
| bf6d084f82 | |||
| 1a03bd9624 | |||
| 6a1d60c5a7 | |||
| 4fef17190f | |||
| 08ca75bb35 | |||
| 0512eaa7fd | |||
| e8df5f9eb4 | |||
| c46678c766 | |||
| 7cdb48b6c9 | |||
| 4ffa6bb41b | |||
| 77af16db55 | |||
| 85d85fa587 | |||
| 7ade64f855 | |||
| 346e8dcaf0 | |||
| ddfd07bf4a | |||
| a9fe46da0c | |||
| aca1f0d186 | |||
| 9eb415d398 | |||
| 8f6b728534 | |||
| 2e0c09ab39 | |||
| f211344786 | |||
| 68702b98f5 | |||
| f93caf277b | |||
| e0a7698780 | |||
| 783c74d3ba | |||
| 21963c03a2 | |||
| 879a132077 | |||
| 606ebcae92 | |||
| af10ae9e34 | |||
| a23c150738 | |||
| 085a678266 | |||
| cf7cafeb4c | |||
| 870657affa | |||
| fe11d4770d | |||
| c8fe9030c7 | |||
| 3d81c73495 | |||
| e7a540e717 | |||
| bb886c237e | |||
| 6c1e271fa8 | |||
| 4c1b807e4f | |||
| 3b22c0cdd7 | |||
| 631404743f | |||
| d5c9639170 | |||
| 94b7c36d62 | |||
| 532b177d2e | |||
| 4093fd9b79 | |||
| 3d6f91cbef | |||
| fffd508bb2 | |||
| 333b35d8b9 | |||
| 890673a197 | |||
| 27539d4aad | |||
| f7f23015d0 | |||
| e432b7061d | |||
| ab14b65078 | |||
| fc92d40b3a | |||
| dc71f9cd5e | |||
| 99ed4d6cb9 | |||
| 3646cfb47e | |||
| 1b7ce8bcde | |||
| 67c6146f96 | |||
| b8c0d4c4ea | |||
| 6c624ca08a | |||
| 8a133938e6 | |||
| c1b3f1977d | |||
| f086156d48 | |||
| 7080013dd6 | |||
| b080825bb2 | |||
| 8bb696bf9a | |||
| 55e8451513 | |||
| 7e4e2a1d5d | |||
| 79d4956b75 | |||
| 9acfd8f5d2 | |||
| 0b530ce181 | |||
| 402835d72d | |||
| 08d579ec87 | |||
| 8b92755487 | |||
| 01b48107f3 | |||
| a546803970 | |||
| 96c28c2611 | |||
| 65a22417f7 | |||
| 6c271e7af9 | |||
| a85cafe7ab | |||
| 41d91ae791 | |||
| dca5a26fe4 | |||
| 7e8f6d391f | |||
| 7aa00ee6e0 | |||
| 2e027e6195 | |||
| 337ba4ef34 | |||
| 0f32c2d680 | |||
| 96a18ec8e9 | |||
| 65c39a3117 | |||
| 15150f9a16 | |||
| 259879c964 | |||
| ed87789abe | |||
| 09492fa779 | |||
| 8122c0b352 | |||
| 28f0ab1001 | |||
| 266f82b6c5 | |||
| 813a8956d3 | |||
| 0a8fa2be4d | |||
| 6e347dd32c | |||
| 33c9e2b58d | |||
| 16f8f6036c | |||
| 76b696e3bf | |||
| 7cadbb6398 | |||
| bf22399aba | |||
| 0b9a3ef68a | |||
| f8d6c7e7d0 | |||
| 90ba9a1a3a | |||
| 6815731da1 | |||
| 52dce66598 | |||
| 255fec84bd | |||
| 9142314d39 | |||
| 27164093ea | |||
| 67312d03d0 | |||
| ad9ae867c0 | |||
| af287c387e | |||
| acf10eeef0 | |||
| 759e444624 | |||
| a68f4e284a | |||
| d554417fde | |||
| eedcfc122b | |||
| 8cf3110708 | |||
| dbe489c0f2 | |||
| a7e28a97d9 | |||
| ade3c4ee6c | |||
| 7da7c1e5a1 | |||
| 55098deac0 | |||
| 58c5618d30 | |||
| d0cbeb4a0a | |||
| a8914dcfdb | |||
| 73e219b23c | |||
| 07e0de5824 | |||
| 389b29599b | |||
| f816a63b54 | |||
| fa8f3985b8 | |||
| 175c785705 | |||
| 72aebd3ecf | |||
| 802cbb82c4 | |||
| 42f5507fe8 | |||
| 5a8e37f5b4 | |||
| a3c7b1fcfe | |||
| 02785eea42 | |||
| 3c7d3b2f63 | |||
| 49f3979d18 | |||
| d598fdce54 | |||
| 32892cf5e0 | |||
| 32d757b13c | |||
| 882d5ec7f5 | |||
| ce69497a68 | |||
| 1d43cf491b | |||
| 27283754c0 | |||
| 5b2212829c | |||
| 546209ddb5 | |||
| 86b2dac231 | |||
| 54bda6f12a | |||
| 2f67a485aa | |||
| f192f241cf | |||
| 690af8e494 | |||
| 9d06ce53a4 | |||
| daa80a7bf6 | |||
| 8152e7cb92 | |||
| adccb4161e | |||
| 72a0187295 | |||
| 24d6287918 | |||
| 55900a3464 | |||
| 36e9bb4d78 | |||
| 639d04381e | |||
| 7f905bce0e | |||
| 205ea22daa | |||
| c7b505fd90 | |||
| c5f7ac18fd | |||
| 10b8fd9fb9 | |||
| 24c053d671 | |||
| 44fc8be49e | |||
| 2a4e73c650 | |||
| f0a540eba2 | |||
| 12922f8413 | |||
| 85f054e3c1 | |||
| 470bf86ec0 | |||
| 9367f61952 | |||
| 3cabd8c318 | |||
| 4119380af5 | |||
| fc3b08e374 | |||
| a57abce96e | |||
| b260f6c6de | |||
| c96e017e65 | |||
| 4f596d20a9 | |||
| 343e462473 | |||
| db414e8045 | |||
| 54e460cf5b | |||
| ba64768d8d | |||
| 6710ac8c85 | |||
| 1f98b76da0 | |||
| 519eb9d641 | |||
| 931db6f0b1 | |||
| f24c56d1ba | |||
| 44608c3122 | |||
| 90f2fc4cd9 | |||
| 24c7dee5d0 | |||
| 12417c0c48 | |||
| d4919ff8c4 | |||
| b2acb97639 | |||
| 28debbda1b | |||
| c0a2a78f3d | |||
| 3f7bfe0b3a | |||
| e0d281bb4b | |||
| b7d37af933 | |||
| 3f9cfbb422 | |||
| 2f3d926c08 | |||
| 594c3b8a85 | |||
| 41244485b1 | |||
| ede342feba | |||
| a15bdba5d7 | |||
| 7d4a9998ca | |||
| bde439d102 | |||
| ba501dbbb8 | |||
| 0a53f7636c | |||
| 6e6d552441 | |||
| f75e0cf4ba | |||
| 0dc3bff01a | |||
| 358dd7f32b | |||
| 6531d027fc | |||
| d11e787624 | |||
| 055ae0d126 | |||
| 77969960d9 | |||
| 52b1a08912 | |||
| 1a9f336a59 | |||
| 9ba08f275e | |||
| aadd0c9129 | |||
| b3b79a792f | |||
| 59cd4b5c5c | |||
| f5f9ff29ce | |||
| a10b59d03b | |||
| e6b1ecb851 | |||
| 69d6609a3b | |||
| 2196ab3713 | |||
| 89be1cf5d2 | |||
| 2041e85b9e | |||
| 6c2f0e404f | |||
| d5e66252dc | |||
| c2359a96f8 | |||
| 1df65bc3ce | |||
| 530a80d7ea | |||
| 8be0f68390 | |||
| 05cb30d79c | |||
| c472d4975e | |||
| 0e31fcf3db | |||
| 13ed3303f5 | |||
| f200ab423d | |||
| d889d3e385 | |||
| 0bf27fa319 | |||
| 466a37aab8 | |||
| 4ab2130d7d | |||
| 256ceef875 | |||
| 2acde54880 | |||
| a92d255e4b | |||
| dc62272e6e | |||
| 60f6a60ca9 | |||
| eef8d21112 | |||
| 6a374cbfa6 | |||
| ccb8ac4cfc | |||
| 5de21734c0 | |||
| 18deb8ad99 | |||
| 3426ae3722 | |||
| d04510f5f2 | |||
| 84c0682f16 | |||
| 53bbc5f922 | |||
| 56d2230251 | |||
| 62f21d3cee | |||
| b9c90af8be | |||
| 7a661c1b48 | |||
| c871a711dd | |||
| d671caf3ab | |||
| d412d90b17 | |||
| 2f74181262 | |||
| 45fd23ff02 | |||
| 4e2fb8e5dc | |||
| afbceca184 | |||
| 1b8c68af02 | |||
| 98430fd573 | |||
| 4a547ba1c9 | |||
| 59f39fd535 | |||
| b4ca04596c | |||
| 2d94ac7a19 | |||
| 4c97c689e9 | |||
| f2f4c23c80 | |||
| 67ea15706e | |||
| 5cc75b8cc3 | |||
| 653ac66ff3 | |||
| 3f7e593bc2 | |||
| 39505cd868 | |||
| 0daaa3e785 | |||
| 4050a375bc | |||
| da27108d0d | |||
| a058f26462 | |||
| c8f0f05570 | |||
| 347ed2896a | |||
| 5c40a1f9e9 | |||
| afa39866ad | |||
| 7254db40eb | |||
| 04481c33e7 | |||
| 4c107470c3 | |||
| 4b39f490f3 | |||
| c607ceda48 | |||
| d4acd4629a | |||
| b413b7b5af | |||
| 5bc66c5b7f | |||
| 0415e910dc | |||
| 960040e51e | |||
| 90136c524c | |||
| f48ba93a01 | |||
| 4823235bf5 | |||
| 90f30c3f3c | |||
| 22397ded9b | |||
| de1da4b060 | |||
| b7240f2048 | |||
| 13f7f96101 | |||
| 006ee00527 | |||
| 3e8f85a4b4 | |||
| 8d7f496f7c | |||
| f62a9ed199 | |||
| 8305a1acd3 | |||
| 309081cccf | |||
| 1221c0e416 | |||
| c505cc689b | |||
| 86cc3f69aa | |||
| 3a216c055e | |||
| ee3a4614c3 | |||
| bb9bc73daa | |||
| 5b5e1847eb | |||
| eeb94e1668 | |||
| 14bf4bbd32 | |||
| 0e61d18b9e | |||
| 531fa9f25a | |||
| a44a670f50 | |||
| 81750c41bd | |||
| 672568d1f5 | |||
| 0179540023 | |||
| ed37b84b3b | |||
| 47c8e6854e | |||
| 1def61432e | |||
| e9b2175506 | |||
| a2a4ad3947 | |||
| d95f818e43 | |||
| 64978dbe89 | |||
| bc8b8a69b8 | |||
| 77e9643a09 | |||
| 17316363f1 | |||
| 0e57d558f6 | |||
| f23247cd85 | |||
| 93ee850bcc | |||
| 8ba6cb3abf | |||
| a266a22daf | |||
| c4d36f06f8 | |||
| 78a2a4a55b | |||
| a70f37a53e | |||
| 9de3acfae2 | |||
| 0a883ae109 | |||
| 568a4987e3 | |||
| 02bfe3ad72 | |||
| 3eba4f462f | |||
| 55f3d72cc8 | |||
| 2fa2cc1574 | |||
| 95f5773046 | |||
| 289d7a01d8 | |||
| 47dbb648fb | |||
| e499f2d148 | |||
| 62325f0a27 | |||
| ff499bff04 | |||
| 26a04ca7e9 | |||
| 6f39439e3d | |||
| 34d4b284fe | |||
| a5695b71eb | |||
| 539e7c80bf | |||
| cfc1704c64 | |||
| bbe1cb1813 | |||
| feb120cbeb | |||
| 2bc07ad4cc | |||
| 1c036b1a11 | |||
| f5fe07ffb7 | |||
| 213f02cbc6 | |||
| 2de465e8f3 | |||
| 5b714572e3 | |||
| 8e95acb4c5 | |||
| 9c40218f48 | |||
| 714b7019c5 | |||
| 935d010dd2 | |||
| 199c502396 | |||
| 19b24b5109 | |||
| 3aaa33ebe9 | |||
| 4ae7a32e29 | |||
| f2c406f168 | |||
| 2115367353 | |||
| 87814fa43c | |||
| 9e24b694b3 | |||
| 4f5bc9eb89 | |||
| f9d8754d88 | |||
| 07c2a14321 | |||
| e77bddd0f2 | |||
| 42522f5af3 | |||
| 5c3d605d34 | |||
| 3deda7efab | |||
| b441c73604 | |||
| df59596775 | |||
| cdb0c57416 | |||
| 0749c6865d | |||
| a80bb1e219 | |||
| ff2805c428 | |||
| 69c45a4b05 | |||
| 737244a65e | |||
| 7018969d14 | |||
| d1ba7f31f8 | |||
| cbfd04e950 | |||
| 981c0b83d3 | |||
| 16ae5cf634 | |||
| 4a42bfa9bb | |||
| 3e32652b24 | |||
| 957096a70a | |||
| 8bb069e996 | |||
| a0c672eb91 | |||
| eddd996ed6 | |||
| c0a2326a8d | |||
| e429f8611e | |||
| fcca95787b | |||
| 8944870d4f | |||
| b93c3d9caa | |||
| 4d2aba2577 | |||
| ce780ee4f4 | |||
| a6134bfacd | |||
| a96f48d0a8 | |||
| 7085f198a9 | |||
| 4f211aaf48 | |||
| 4d2bf19891 | |||
| dfbadb1550 | |||
| 0eeb481d85 | |||
| 66bce71a8b | |||
| b198ecbc02 | |||
| ecbe058d00 | |||
| 22f37d02dc | |||
| c8b23195f6 | |||
| 02840965d7 | |||
| 5b13ab255a | |||
| ae659e3c84 | |||
| c1ffefc6af | |||
| 1b2d75ee52 | |||
| d5b2ce9c09 | |||
| 9162575b35 | |||
| 0ff26aa442 | |||
| a06b920785 | |||
| 0441827578 | |||
| 85c2ec68ac | |||
| bf50c25854 | |||
| 24402388b4 | |||
| 7b345d6cb0 | |||
| 018f9f1776 | |||
| 71f3948de2 | |||
| ea67e21796 | |||
| a83739c269 | |||
| cc5f127dcf | |||
| e015e73abe | |||
| 0624d224a9 | |||
| 030708194a | |||
| a5b0fbb83d | |||
| 5a5be024e4 | |||
| b1b42b5262 | |||
| 07f4da9b00 | |||
| 59ace3d881 | |||
| 0426f1f17d | |||
| c2d3d057a5 | |||
| 5426c570fc | |||
| 3a6e2e6735 | |||
| ef38808ba5 | |||
| 7fe5f0c099 | |||
| 5f615d30b2 | |||
| 7ce2bbe9b1 | |||
| 4c408eb1b7 | |||
| 82a2f50d03 | |||
| e8237f6190 | |||
| 4d13622432 | |||
| c77174059d | |||
| 69b442a31a | |||
| e9724c8785 | |||
| 0a49d54a85 | |||
| 958392f09b | |||
| cb79bcd876 | |||
| c500f98ad8 | |||
| 41a8dd10de | |||
| 9283e57429 | |||
| 3bdb3f8c1e | |||
| 3aee8b069d | |||
| 3e79b65539 | |||
| e2d8a28087 | |||
| ba0ef3676d | |||
| 3f609fdf90 | |||
| 9717d03a75 | |||
| 21e2c35ab0 | |||
| 563f8fbdca | |||
| 221354a7d8 | |||
| 7495d89a79 | |||
| 15ad07f5db | |||
| c95c4c359a | |||
| 1a1dd99a89 | |||
| 71afe908b4 | |||
| e3c3acfca9 | |||
| 7e81768abc | |||
| 6d0bca471e | |||
| 8e4e7e5b19 | |||
| c667627d2f | |||
| 8b411c3d46 | |||
| db7c20d99a | |||
| f153332ad5 | |||
| 876d78b4ad | |||
| 68e871d175 | |||
| d7d4b26a66 | |||
| f1d5ccb1e3 | |||
| bf67cd5acd | |||
| e330d40005 | |||
| 1a6986cfcf | |||
| b617b9e391 | |||
| dadcbc8b31 | |||
| 07bb57fac8 | |||
| 92f5ad220a | |||
| 1a8f53d1de | |||
| 4f77caee02 | |||
| d64757f3b0 | |||
| 74a5bf5503 | |||
| ee8efbb1ab | |||
| 7ab2703388 | |||
| f753dfa2a7 | |||
| 8fa02913be | |||
| 21426610ad | |||
| 40b0a62cc3 | |||
| 3949b78c61 | |||
| 55179d7400 | |||
| 517147346e | |||
| e1e63d079f | |||
| 86744046e3 | |||
| a5f1c3e564 | |||
| 2b2734cd66 | |||
| f199b0a651 | |||
| c2d33f3301 | |||
| 8fc999f771 | |||
| be0fb51193 | |||
| 097f5355e2 | |||
| 843fad9963 | |||
| 7b5670f275 | |||
| f4410891e4 | |||
| 9da95ce28f | |||
| 33e05625ed | |||
| b8575d04ad | |||
| b64b67ce81 | |||
| 1b6a2c7d5c | |||
| a1a90d5bff | |||
| 08733d7351 | |||
| 5d0d65c624 | |||
| 6c7c333d8f | |||
| b039fbfb13 | |||
| 428bc7fbad | |||
| fe9686f215 | |||
| e4a472f902 | |||
| 900f8970f0 | |||
| dd6c24d3b8 | |||
| fb83b9dca3 | |||
| 8175e68ecd | |||
| ac0159f67e | |||
| f1c8fb7b17 | |||
| 3b43979a06 | |||
| c839285f90 | |||
| 21378a07c8 | |||
| 74e6633f2a | |||
| 8693b13e68 | |||
| a0a861370b | |||
| 7282626c0f | |||
| cf604255fc | |||
| b275850aca | |||
| 8aa36a82c4 | |||
| 06132fcdaf | |||
| f1e5b3fcfa | |||
| fba4b91bec | |||
| 0ef987207b | |||
| 2d7b5b959e | |||
| 2837861d9a | |||
| 212222c98e | |||
| 15237a6d91 | |||
| 95b2ef0f3c | |||
| c452d31f25 | |||
| 7361d1f16f | |||
| 7ffb22a40f | |||
| f78e496e69 | |||
| 48ed511ba5 | |||
| 3cff6fdd6c | |||
| 81da2e49f7 | |||
| 2cd6427958 | |||
| 061f1ae0b1 | |||
| f503cd41de | |||
| 4428877158 | |||
| 876f61ac27 | |||
| 2cc541e084 | |||
| ddd3c78fa6 | |||
| 62dffedc02 | |||
| 13e5409ab2 | |||
| 927e9cf14a | |||
| f0db4824ec | |||
| 7611829f2e | |||
| 1ff5d171e3 | |||
| b8aaebf51a | |||
| 2c9bc5135c | |||
| 248177f518 | |||
| d34901fd13 | |||
| 94b12dfb02 | |||
| 0cc88a1706 | |||
| bd3ad153d2 | |||
| 78524398b7 | |||
| f97123015a | |||
| b1e6559f4d | |||
| 2d0725fbad | |||
| 1a0ced4f15 | |||
| 9d9c36b253 | |||
| 6d9a747b34 | |||
| a28cd8a54f | |||
| 44ad842542 | |||
| 917e05a16c | |||
| e60d5c8f2c | |||
| 03f16dfb78 | |||
| c9106065fd | |||
| 2c9e96406b | |||
| 21c7d6a667 | |||
| e0f95f18d3 | |||
| b9ffbe9a57 | |||
| dc9b2b3ca8 | |||
| baafa604fe | |||
| e79bfebb76 | |||
| 131fdab675 | |||
| efc2a8e2ea | |||
| d725f1362f | |||
| e79427619b | |||
| 4887302b51 | |||
| aea735f20e | |||
| 7a34b490c7 | |||
| 8091d9a098 | |||
| e2e6440109 | |||
| caffb36ec2 | |||
| 0d054de6ac | |||
| 5397f2c53a | |||
| 61b11025bd | |||
| 8c8a0160a1 | |||
| cbd980357b | |||
| 8b37f1dc6c | |||
| 6ec7028fe8 | |||
| 14c0055899 | |||
| e0981f1c44 | |||
| 2ca7304215 | |||
| 88f8052573 | |||
| 6407c24977 | |||
| fb8d71fc63 | |||
| 8ee98ebdf9 | |||
| d74a30e4e4 | |||
| 5afeb315ba | |||
| c2eeff6e47 | |||
| 893c2a99be | |||
| ca1f43d048 | |||
| e962a48138 | |||
| 9da18d0697 | |||
| 3bb991edd3 | |||
| dd0373b855 | |||
| 48786fcae1 | |||
| 28b4945d36 | |||
| 4a5314d8b9 | |||
| c67adcc43f | |||
| 0a43fea1c0 | |||
| a20b7f9303 | |||
| 5b9b6be383 | |||
| fce0a4b0eb | |||
| 381de750e7 | |||
| 40f6de950c | |||
| 916c350352 | |||
| 4186791d9a | |||
| 02cef43815 | |||
| 8d291efedd | |||
| a5c8052439 | |||
| 704169abab | |||
| 8ee8b52832 | |||
| 81819aa3b7 | |||
| 90ffae5526 | |||
| a3c99531bf | |||
| 1b6bf2f331 | |||
| f5d18157bf | |||
| 7c7dd0736e | |||
| 0a5cd9f113 | |||
| 9b498b8471 | |||
| fee217e8d7 | |||
| c05a28a901 | |||
| fa45cd1a88 | |||
| 6cb5331918 | |||
| fca865a743 | |||
| 1f313b9487 | |||
| 85d243508c | |||
| 365da75d1b | |||
| a74feab9eb | |||
| ae591d4883 | |||
| 3489bf22bb | |||
| 9e82b8d693 | |||
| 71482f0fc9 | |||
| 8216fa1d88 | |||
| 4133574db0 | |||
| bc9a6abccf | |||
| 9f5a9f179f | |||
| 3e46105ba8 | |||
| d8eaab2aa4 | |||
| ce897509ba | |||
| 330cd7a409 | |||
| 56492cd1b2 | |||
| 9427979b1c | |||
| 9d47529d20 | |||
| eec651c9d3 | |||
| 3921f88e30 |
@@ -141,3 +141,6 @@ build/docs.zip
|
||||
build/ui-docs.zip
|
||||
build/csharp-docs.zip
|
||||
build/msbuild.log
|
||||
.vs/
|
||||
|
||||
build/tools/
|
||||
|
||||
@@ -1,42 +1,54 @@
|
||||
[](https://ci.appveyor.com/project/Umbraco/umbraco-cms-hs8dx/branch/dev-v7)
|
||||
|
||||
Umbraco CMS
|
||||
===========
|
||||
Umbraco is a free open source Content Management System built on the ASP.NET platform.
|
||||
The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 390,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
|
||||
|
||||
[](https://vimeo.com/172382998/)
|
||||
|
||||
## Umbraco CMS ##
|
||||
Umbraco is a free open source Content Management System built on the ASP.NET platform. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
|
||||
|
||||
|
||||
## Building Umbraco from source ##
|
||||
|
||||
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt vs` in `src\Umbraco.Web.UI.Client`.
|
||||
|
||||
If you're interested in making changes to Belle without running Visual Studio make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md).
|
||||
|
||||
Note that you can always [download a nightly build](http://nightly.umbraco.org/?container=umbraco-750) so you don't have to build the code yourself.
|
||||
|
||||
## Watch a introduction video ##
|
||||
## Watch an introduction video ##
|
||||
|
||||
[](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
|
||||
|
||||
## Umbraco - the simple, flexible and friendly ASP.NET CMS ##
|
||||
## Umbraco - The Friendly CMS ##
|
||||
|
||||
**More than 350,000 sites trust Umbraco**
|
||||
For the first time on the Microsoft platform, there is a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box.
|
||||
|
||||
For the first time on the Microsoft platform, there is a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box. It's a developer's dream and your users will love it too.
|
||||
Umbraco is not only loved by developers, but is a content editors dream. Enjoy intuitive editing tools, media management, responsive views and approval workflows to send your content live.
|
||||
|
||||
Used by more than 350,000 active websites including [http://daviscup.com](http://daviscup.com), [http://heinz.com](http://heinz.com), [http://peugeot.com](http://peugeot.com), [http://www.hersheys.com/](http://www.hersheys.com/) and **The Official ASP.NET and IIS.NET website from Microsoft** ([http://asp.net](http://asp.net) / [http://iis.net](http://iis.net)), you can be sure that the technology is proven, stable and scales.
|
||||
Used by more than 350,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scales. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 200,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
|
||||
|
||||
To view more examples, please visit [http://umbraco.com/why-umbraco/#caseStudies](http://umbraco.com/why-umbraco/#caseStudies)
|
||||
To view more examples, please visit [https://umbraco.com/why-umbraco/#caseStudies](https://umbraco.com/why-umbraco/#caseStudies)
|
||||
|
||||
## Why Open Source? ##
|
||||
As an Open Source platform, Umbraco is more than just a CMS. We are transparent with our roadmap for future versions, our incremental sprint planning notes are publicly accessible and community contributions and packages are available for all to use.
|
||||
|
||||
## Downloading ##
|
||||
|
||||
The downloadable Umbraco releases live at [http://our.umbraco.org/download](http://our.umbraco.org/download).
|
||||
The downloadable Umbraco releases live at [https://our.umbraco.org/download](https://our.umbraco.org/download).
|
||||
|
||||
## Forums ##
|
||||
|
||||
We have a forum running on [http://our.umbraco.org](http://our.umbraco.org). The discussions group on [Google Groups](https://groups.google.com/forum/#!forum/umbraco-dev) is for discussions on developing the core, and not on Umbraco-implementations or extensions in general. For those topics, please use [http://our.umbraco.org](http://our.umbraco.org).
|
||||
Peer-to-peer support is available 24/7 at the community forum on [https://our.umbraco.org](https://our.umbraco.org).
|
||||
|
||||
## Contribute to Umbraco ##
|
||||
|
||||
If you want to contribute back to Umbraco you should check out our [guide to contributing](http://our.umbraco.org/contribute).
|
||||
Umbraco is contribution focused and community driven. If you want to contribute back to Umbraco please check out our [guide to contributing](https://our.umbraco.org/contribute).
|
||||
|
||||
## Found a bug? ##
|
||||
|
||||
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature).
|
||||
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](https://our.umbraco.org/contribute/report-an-issue-or-request-a-feature).
|
||||
|
||||
To view existing issues, please visit [http://issues.umbraco.org](http://issues.umbraco.org).
|
||||
|
||||
+43
-1
@@ -49,10 +49,52 @@ REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might
|
||||
REM but the path setting is lost due to SETLOCAL
|
||||
path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
|
||||
|
||||
SET toolsFolder=%CD%\tools\
|
||||
IF NOT EXIST "%toolsFolder%" (
|
||||
MD tools
|
||||
)
|
||||
|
||||
SET nuGetExecutable=%CD%\tools\nuget.exe
|
||||
IF NOT EXIST "%nuGetExecutable%" (
|
||||
ECHO Getting NuGet so we can fetch some tools
|
||||
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
|
||||
)
|
||||
|
||||
:: We need 7za.exe for BuildBelle.bat
|
||||
IF NOT EXIST "%toolsFolder%7za.exe" (
|
||||
ECHO 7zip not found - fetching now
|
||||
"%nuGetExecutable%" install 7-Zip.CommandLine -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
:: We need vswhere.exe for VS2017+
|
||||
IF NOT EXIST "%toolsFolder%vswhere.exe" (
|
||||
ECHO vswhere not found - fetching now
|
||||
"%nuGetExecutable%" install vswhere -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
:: Put 7za.exe and vswhere.exe in a predictable path (not version specific)
|
||||
FOR /f "delims=" %%A in ('dir "%toolsFolder%7-Zip.CommandLine.*" /b') DO SET "sevenZipExePath=%toolsFolder%%%A\"
|
||||
MOVE "%sevenZipExePath%tools\7za.exe" "%toolsFolder%7za.exe"
|
||||
|
||||
FOR /f "delims=" %%A in ('dir "%toolsFolder%vswhere.*" /b') DO SET "vswhereExePath=%toolsFolder%%%A\"
|
||||
MOVE "%vswhereExePath%tools\vswhere.exe" "%toolsFolder%vswhere.exe"
|
||||
|
||||
ECHO.
|
||||
ECHO Making sure we have a web.config
|
||||
IF NOT EXIST %CD%\..\src\Umbraco.Web.UI\web.config COPY %CD%\..\src\Umbraco.Web.UI\web.Template.config %CD%\..\src\Umbraco.Web.UI\web.config
|
||||
|
||||
for /f "usebackq tokens=1* delims=: " %%i in (`"%CD%\tools\vswhere.exe" -latest -requires Microsoft.Component.MSBuild`) do (
|
||||
if /i "%%i"=="installationPath" set InstallDir=%%j
|
||||
)
|
||||
|
||||
SET VSWherePath="%InstallDir%\MSBuild"
|
||||
ECHO.
|
||||
ECHO Visual Studio is installed in: %InstallDir%
|
||||
|
||||
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
|
||||
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
|
||||
|
||||
ECHO.
|
||||
ECHO.
|
||||
ECHO Performing MSBuild and producing Umbraco binaries zip files
|
||||
@@ -60,7 +102,7 @@ ECHO This takes a few minutes and logging is set to report warnings
|
||||
ECHO and errors only so it might seems like nothing is happening for a while.
|
||||
ECHO You can check the msbuild.log file for progress.
|
||||
ECHO.
|
||||
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /fileLogger
|
||||
%MSBUILD% "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /p:VSWherePath=%VSWherePath%
|
||||
IF ERRORLEVEL 1 GOTO :error
|
||||
|
||||
ECHO.
|
||||
|
||||
+48
-22
@@ -1,33 +1,59 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
:: SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
|
||||
SET release=%1
|
||||
ECHO Installing Npm NuGet Package
|
||||
|
||||
SET nuGetFolder=%CD%\..\src\packages\
|
||||
ECHO Configured packages folder: %nuGetFolder%
|
||||
SET toolsFolder=%CD%\tools\
|
||||
ECHO Current folder: %CD%
|
||||
|
||||
%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet
|
||||
SET nodeFileName=node-v6.9.1-win-x86.7z
|
||||
SET nodeExtractFolder=%toolsFolder%node.js.691
|
||||
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
|
||||
IF NOT EXIST "%nodeExtractFolder%" (
|
||||
ECHO Downloading http://nodejs.org/dist/v6.9.1/%nodeFileName% to %toolsFolder%%nodeFileName%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v6.9.1/%nodeFileName%', '%toolsFolder%%nodeFileName%')"
|
||||
ECHO Extracting %nodeFileName% to %nodeExtractFolder%
|
||||
"%toolsFolder%\7za.exe" x "%toolsFolder%\%nodeFileName%" -o"%nodeExtractFolder%" -aos > nul
|
||||
)
|
||||
FOR /f "delims=" %%A in ('dir "%nodeExtractFolder%\node*" /b') DO SET "nodePath=%nodeExtractFolder%\%%A"
|
||||
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
SET buildFolder=%CD%
|
||||
SET nuGetExecutable=%CD%\tools\nuget.exe
|
||||
IF NOT EXIST "%nuGetExecutable%" (
|
||||
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
|
||||
)
|
||||
|
||||
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
CD %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
SET drive=%CD:~0,2%
|
||||
SET nuGetFolder=%drive%\packages\
|
||||
FOR /f "delims=" %%A in ('dir "%nuGetFolder%npm.*" /b') DO SET "npmPath=%nuGetFolder%%%A\"
|
||||
|
||||
ECHO Do npm install and the grunt build of Belle
|
||||
call npm cache clean --quiet
|
||||
call npm install --quiet
|
||||
call npm install -g grunt-cli --quiet
|
||||
call npm install -g bower --quiet
|
||||
call grunt build --buildversion=%release%
|
||||
IF [%npmPath%] == [] GOTO :installnpm
|
||||
IF NOT [%npmPath%] == [] GOTO :build
|
||||
|
||||
ECHO Move back to the build folder
|
||||
CD %buildFolder%
|
||||
:installnpm
|
||||
ECHO Downloading npm
|
||||
ECHO Configured packages folder: %nuGetFolder%
|
||||
ECHO Installing Npm NuGet Package
|
||||
"%nuGetExecutable%" install Npm -OutputDirectory %nuGetFolder% -Verbosity detailed
|
||||
REM Ensures that we look for the just downloaded NPM, not whatever the user has installed on their machine
|
||||
FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\"
|
||||
GOTO :build
|
||||
|
||||
:build
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH="%npmPath%";"%nodePath%";%PATH%
|
||||
SET buildFolder=%CD%
|
||||
|
||||
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
CD %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
|
||||
ECHO Do npm install and the grunt build of Belle
|
||||
call npm cache clean --quiet
|
||||
call npm install --quiet
|
||||
call npm install -g grunt-cli --quiet
|
||||
call npm install -g bower --quiet
|
||||
call grunt build --buildversion=%release%
|
||||
|
||||
ECHO Move back to the build folder
|
||||
CD "%buildFolder%"
|
||||
@@ -27,18 +27,18 @@
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.1, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
|
||||
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.69, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.4.4, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.6.4, 5.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.82, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -103,4 +103,4 @@
|
||||
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
|
||||
</files>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.4, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -43,6 +44,8 @@
|
||||
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
|
||||
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
|
||||
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
|
||||
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
|
||||
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
|
||||
<file src="build\**" target="build" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -41,16 +41,6 @@
|
||||
views/dashboard/developer/examinemanagement.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Health Check" xdt:Transform="InsertIfMissing" xdt:Locator="Match(caption)">
|
||||
<control>
|
||||
views/dashboard/developer/healthcheck.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Redirect URL Management" xdt:Transform="InsertIfMissing" xdt:Locator="Match(caption)">
|
||||
<control>
|
||||
views/dashboard/developer/redirecturls.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
@@ -80,4 +70,26 @@
|
||||
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="RedirectUrlManagement" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas>
|
||||
<area>content</area>
|
||||
</areas>
|
||||
<tab caption="Redirect URL Management">
|
||||
<control>
|
||||
views/dashboard/developer/redirecturls.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="UmbracoHealthCheck" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas>
|
||||
<area>developer</area>
|
||||
</areas>
|
||||
<tab caption="Health Check">
|
||||
<control>
|
||||
views/dashboard/developer/healthcheck.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
</dashBoard>
|
||||
@@ -319,6 +319,7 @@
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='AutoMapper')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
|
||||
@@ -334,10 +335,14 @@
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<caching xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<caches>
|
||||
<cache name="DiskCache" trimCache="false" xdt:Transform="SetAttributes(trimCache)" xdt:Locator="Match(name)" />
|
||||
</caches>
|
||||
</caching>
|
||||
@@ -71,6 +71,7 @@ if ($project) {
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll) { Remove-Item $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<processing xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="SetAttributes(preserveExifMetaData)" preserveExifMetaData="true" />
|
||||
@@ -1,2 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.5.2
|
||||
7.5.13
|
||||
@@ -49,13 +49,9 @@
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
||||
@@ -21,15 +21,15 @@ namespace SqlCE4Umbraco
|
||||
public class SqlCEInstaller : DefaultInstallerUtility<SqlCEHelper>
|
||||
{
|
||||
#region Private Constants
|
||||
|
||||
|
||||
/// <summary>The latest database version this installer supports.</summary>
|
||||
private const DatabaseVersion LatestVersionSupported = DatabaseVersion.Version4_8;
|
||||
|
||||
/// <summary>The specifications to determine the database version.</summary>
|
||||
private static readonly VersionSpecs[] m_VersionSpecs = new VersionSpecs[] {
|
||||
new VersionSpecs("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS LEFT OUTER JOIN umbracoApp ON appAlias = appAlias WHERE CONSTRAINT_NAME = 'FK_umbracoUser2app_umbracoApp'", 0, DatabaseVersion.Version4_8),
|
||||
new VersionSpecs("SELECT id FROM umbracoNode WHERE id = -21", 1, DatabaseVersion.Version4_1),
|
||||
new VersionSpecs("SELECT action FROM umbracoAppTree",DatabaseVersion.Version4),
|
||||
new VersionSpecs("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS LEFT OUTER JOIN umbracoApp ON appAlias = appAlias WHERE CONSTRAINT_NAME = 'FK_umbracoUser2app_umbracoApp'", 0, DatabaseVersion.Version4_8),
|
||||
new VersionSpecs("SELECT id FROM umbracoNode WHERE id = -21", 1, DatabaseVersion.Version4_1),
|
||||
new VersionSpecs("SELECT action FROM umbracoAppTree",DatabaseVersion.Version4),
|
||||
new VersionSpecs("SELECT description FROM cmsContentType",DatabaseVersion.Version3),
|
||||
new VersionSpecs("SELECT id FROM sysobjects",DatabaseVersion.None) };
|
||||
|
||||
@@ -43,8 +43,9 @@ namespace SqlCE4Umbraco
|
||||
public override bool CanConnect
|
||||
{
|
||||
get
|
||||
{
|
||||
SqlHelper.CreateEmptyDatabase();
|
||||
{
|
||||
using (var sqlHelper = SqlHelper)
|
||||
sqlHelper.CreateEmptyDatabase();
|
||||
return base.CanConnect;
|
||||
}
|
||||
}
|
||||
@@ -93,22 +94,22 @@ namespace SqlCE4Umbraco
|
||||
|
||||
#region DefaultInstaller Members
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sql to do a full install
|
||||
/// </summary>
|
||||
protected override string FullInstallSql
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the sql to do a full install
|
||||
/// </summary>
|
||||
protected override string FullInstallSql
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sql to do an upgrade
|
||||
/// </summary>
|
||||
protected override string UpgradeSql
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the sql to do an upgrade
|
||||
/// </summary>
|
||||
protected override string UpgradeSql
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
// We need to override this as the default way of detection a db connection checks for systables that doesn't exist
|
||||
// in a CE db
|
||||
@@ -123,8 +124,9 @@ namespace SqlCE4Umbraco
|
||||
// verify connection
|
||||
try
|
||||
{
|
||||
if (SqlCeApplicationBlock.VerifyConnection(base.SqlHelper.ConnectionString))
|
||||
return DatabaseVersion.None;
|
||||
using (var sqlHelper = SqlHelper)
|
||||
if (SqlCeApplicationBlock.VerifyConnection(sqlHelper.ConnectionString))
|
||||
return DatabaseVersion.None;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -37,13 +37,15 @@ namespace SqlCE4Umbraco
|
||||
ITable table = null;
|
||||
|
||||
// get name in correct casing
|
||||
name = SqlHelper.ExecuteScalar<string>("SELECT name FROM sys.tables WHERE name=@name",
|
||||
SqlHelper.CreateParameter("name", name));
|
||||
using (var sqlHelper = SqlHelper)
|
||||
name = sqlHelper.ExecuteScalar<string>("SELECT name FROM sys.tables WHERE name=@name",
|
||||
sqlHelper.CreateParameter("name", name));
|
||||
if (name != null)
|
||||
{
|
||||
table = new DefaultTable(name);
|
||||
|
||||
using (IRecordsReader reader = SqlHelper.ExecuteReader(
|
||||
using (var sqlHelper = SqlHelper)
|
||||
using (IRecordsReader reader = sqlHelper.ExecuteReader(
|
||||
@"SELECT c.name AS Name, st.name AS DataType, c.max_length, c.is_nullable, c.is_identity
|
||||
FROM sys.tables AS t
|
||||
JOIN sys.columns AS c ON t.object_id = c.object_id
|
||||
@@ -51,7 +53,7 @@ namespace SqlCE4Umbraco
|
||||
JOIN sys.types AS ty ON ty.user_type_id = c.user_type_id
|
||||
JOIN sys.types st ON ty.system_type_id = st.user_type_id
|
||||
WHERE t.name = @name
|
||||
ORDER BY c.column_id", SqlHelper.CreateParameter("name", name)))
|
||||
ORDER BY c.column_id", sqlHelper.CreateParameter("name", name)))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
@@ -112,7 +114,8 @@ namespace SqlCE4Umbraco
|
||||
|
||||
// create query
|
||||
StringBuilder createTableQuery = new StringBuilder();
|
||||
createTableQuery.AppendFormat("CREATE TABLE [{0}] (", SqlHelper.EscapeString(table.Name));
|
||||
using (var sqlHelper = SqlHelper)
|
||||
createTableQuery.AppendFormat("CREATE TABLE [{0}] (", sqlHelper.EscapeString(table.Name));
|
||||
|
||||
// add fields
|
||||
while (hasNext)
|
||||
@@ -136,7 +139,8 @@ namespace SqlCE4Umbraco
|
||||
// execute query
|
||||
try
|
||||
{
|
||||
SqlHelper.ExecuteNonQuery(createTableQuery.ToString());
|
||||
using (var sqlHelper = SqlHelper)
|
||||
sqlHelper.ExecuteNonQuery(createTableQuery.ToString());
|
||||
}
|
||||
catch (Exception executeException)
|
||||
{
|
||||
@@ -154,13 +158,15 @@ namespace SqlCE4Umbraco
|
||||
Debug.Assert(table != null && field != null);
|
||||
|
||||
StringBuilder addColumnQuery = new StringBuilder();
|
||||
addColumnQuery.AppendFormat("ALTER TABLE [{0}] ADD [{1}] {2}",
|
||||
SqlHelper.EscapeString(table.Name),
|
||||
SqlHelper.EscapeString(field.Name),
|
||||
SqlHelper.EscapeString(GetDatabaseType(field)));
|
||||
using (var sqlHelper = SqlHelper)
|
||||
addColumnQuery.AppendFormat("ALTER TABLE [{0}] ADD [{1}] {2}",
|
||||
sqlHelper.EscapeString(table.Name),
|
||||
sqlHelper.EscapeString(field.Name),
|
||||
sqlHelper.EscapeString(GetDatabaseType(field)));
|
||||
try
|
||||
{
|
||||
SqlHelper.ExecuteNonQuery(addColumnQuery.ToString());
|
||||
using (var sqlHelper = SqlHelper)
|
||||
sqlHelper.ExecuteNonQuery(addColumnQuery.ToString());
|
||||
}
|
||||
catch (Exception executeException)
|
||||
{
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2016")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.5.2")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.2")]
|
||||
[assembly: AssemblyFileVersion("7.5.13")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.13")]
|
||||
@@ -64,7 +64,8 @@ namespace Umbraco.Core.Cache
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
[Obsolete("No longer used and will be removed in v8")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
|
||||
{
|
||||
if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider))
|
||||
throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider));
|
||||
|
||||
InnerProvider = innerProvider;
|
||||
}
|
||||
|
||||
@@ -105,9 +108,11 @@ namespace Umbraco.Core.Cache
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
//Clone/reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
//Clone/reset to go out of the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal enum ContentXmlStorage
|
||||
{
|
||||
Default,
|
||||
AspNetTemp,
|
||||
EnvironmentTemp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal static class CoreDebugExtensions
|
||||
{
|
||||
private static CoreDebug _coreDebug;
|
||||
|
||||
public static CoreDebug CoreDebug(this UmbracoConfig config)
|
||||
{
|
||||
return _coreDebug ?? (_coreDebug = new CoreDebug());
|
||||
}
|
||||
}
|
||||
|
||||
internal class CoreDebug
|
||||
{
|
||||
public CoreDebug()
|
||||
{
|
||||
var appSettings = System.Configuration.ConfigurationManager.AppSettings;
|
||||
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// when true, the Logger creates a minidump of w3wp in ~/App_Data/MiniDump whenever it logs
|
||||
// an error due to a ThreadAbortException that is due to a timeout.
|
||||
public bool DumpOnTimeoutThreadAbort { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -520,12 +520,25 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
|
||||
internal static bool ContentCacheXmlStoredInCodeGen
|
||||
{
|
||||
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
|
||||
}
|
||||
|
||||
internal static ContentXmlStorage ContentCacheXmlStorageLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
//defaults to false
|
||||
return ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp")
|
||||
&& bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"]); //default to false
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
|
||||
{
|
||||
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
|
||||
}
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
|
||||
{
|
||||
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
|
||||
? ContentXmlStorage.AspNetTemp
|
||||
: ContentXmlStorage.Default;
|
||||
}
|
||||
return ContentXmlStorage.Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
|
||||
internal class ContentElement : ConfigurationElement, IContentSection
|
||||
internal class ContentElement : UmbracoConfigurationElement, IContentSection
|
||||
{
|
||||
[ConfigurationProperty("imaging")]
|
||||
internal ContentImagingElement Imaging
|
||||
@@ -22,25 +21,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
[ConfigurationProperty("ResolveUrlsFromTextString")]
|
||||
internal InnerTextConfigurationElement<bool> ResolveUrlsFromTextString
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["ResolveUrlsFromTextString"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement<bool>("ResolveUrlsFromTextString", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("UploadAllowDirectories")]
|
||||
internal InnerTextConfigurationElement<bool> UploadAllowDirectories
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["UploadAllowDirectories"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("UploadAllowDirectories", true); }
|
||||
}
|
||||
|
||||
public IEnumerable<IContentErrorPage> Error404Collection
|
||||
@@ -63,121 +50,61 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
[ConfigurationProperty("ensureUniqueNaming")]
|
||||
internal InnerTextConfigurationElement<bool> EnsureUniqueNaming
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["ensureUniqueNaming"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("ensureUniqueNaming", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("TidyEditorContent")]
|
||||
internal InnerTextConfigurationElement<bool> TidyEditorContent
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["TidyEditorContent"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("TidyEditorContent", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("TidyCharEncoding")]
|
||||
internal InnerTextConfigurationElement<string> TidyCharEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["TidyCharEncoding"],
|
||||
//set the default
|
||||
"UTF8");
|
||||
}
|
||||
get { return GetOptionalTextElement("TidyCharEncoding", "UTF8"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("XmlCacheEnabled")]
|
||||
internal InnerTextConfigurationElement<bool> XmlCacheEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["XmlCacheEnabled"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("XmlCacheEnabled", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("ContinouslyUpdateXmlDiskCache")]
|
||||
internal InnerTextConfigurationElement<bool> ContinouslyUpdateXmlDiskCache
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["ContinouslyUpdateXmlDiskCache"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("ContinouslyUpdateXmlDiskCache", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("XmlContentCheckForDiskChanges")]
|
||||
internal InnerTextConfigurationElement<bool> XmlContentCheckForDiskChanges
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["XmlContentCheckForDiskChanges"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("XmlContentCheckForDiskChanges", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("EnableSplashWhileLoading")]
|
||||
internal InnerTextConfigurationElement<bool> EnableSplashWhileLoading
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["EnableSplashWhileLoading"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("EnableSplashWhileLoading", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("PropertyContextHelpOption")]
|
||||
internal InnerTextConfigurationElement<string> PropertyContextHelpOption
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["PropertyContextHelpOption"],
|
||||
//set the default
|
||||
"text");
|
||||
}
|
||||
get { return GetOptionalTextElement("PropertyContextHelpOption", "text"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("UseLegacyXmlSchema")]
|
||||
internal InnerTextConfigurationElement<bool> UseLegacyXmlSchema
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["UseLegacyXmlSchema"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("UseLegacyXmlSchema", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("ForceSafeAliases")]
|
||||
internal InnerTextConfigurationElement<bool> ForceSafeAliases
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["ForceSafeAliases"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("ForceSafeAliases", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("PreviewBadge")]
|
||||
@@ -185,123 +112,63 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["PreviewBadge"],
|
||||
//set the default
|
||||
@"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={2}""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
|
||||
return GetOptionalTextElement("PreviewBadge", @"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={2}""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty("UmbracoLibraryCacheDuration")]
|
||||
internal InnerTextConfigurationElement<int> UmbracoLibraryCacheDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<int>(
|
||||
(InnerTextConfigurationElement<int>)this["UmbracoLibraryCacheDuration"],
|
||||
//set the default
|
||||
1800);
|
||||
|
||||
}
|
||||
get { return GetOptionalTextElement("UmbracoLibraryCacheDuration", 1800); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("MacroErrors")]
|
||||
internal InnerTextConfigurationElement<MacroErrorBehaviour> MacroErrors
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
return new OptionalInnerTextConfigurationElement<MacroErrorBehaviour>(
|
||||
(InnerTextConfigurationElement<MacroErrorBehaviour>)this["MacroErrors"],
|
||||
//set the default
|
||||
MacroErrorBehaviour.Inline);
|
||||
}
|
||||
get { return GetOptionalTextElement("MacroErrors", MacroErrorBehaviour.Inline); }
|
||||
}
|
||||
|
||||
[Obsolete("This is here so that if this config element exists we won't get a YSOD, it is not used whatsoever and will be removed in future versions")]
|
||||
[ConfigurationProperty("DocumentTypeIconList")]
|
||||
internal InnerTextConfigurationElement<IconPickerBehaviour> DocumentTypeIconList
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<IconPickerBehaviour>(
|
||||
(InnerTextConfigurationElement<IconPickerBehaviour>)this["DocumentTypeIconList"],
|
||||
//set the default
|
||||
IconPickerBehaviour.HideFileDuplicates);
|
||||
}
|
||||
get { return GetOptionalTextElement("DocumentTypeIconList", IconPickerBehaviour.HideFileDuplicates); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("disallowedUploadFiles")]
|
||||
internal CommaDelimitedConfigurationElement DisallowedUploadFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalCommaDelimitedConfigurationElement(
|
||||
(CommaDelimitedConfigurationElement)this["disallowedUploadFiles"],
|
||||
//set the default
|
||||
new[] { "ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd" });
|
||||
|
||||
}
|
||||
get { return GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("cloneXmlContent")]
|
||||
internal InnerTextConfigurationElement<bool> CloneXmlContent
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["cloneXmlContent"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("cloneXmlContent", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("GlobalPreviewStorageEnabled")]
|
||||
internal InnerTextConfigurationElement<bool> GlobalPreviewStorageEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["GlobalPreviewStorageEnabled"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("GlobalPreviewStorageEnabled", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("defaultDocumentTypeProperty")]
|
||||
internal InnerTextConfigurationElement<string> DefaultDocumentTypeProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["defaultDocumentTypeProperty"],
|
||||
//set the default
|
||||
"Textstring");
|
||||
}
|
||||
get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("EnableInheritedDocumentTypes")]
|
||||
internal InnerTextConfigurationElement<bool> EnableInheritedDocumentTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>) this["EnableInheritedDocumentTypes"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("EnableInheritedDocumentTypes", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("EnableInheritedMediaTypes")]
|
||||
internal InnerTextConfigurationElement<bool> EnableInheritedMediaTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["EnableInheritedMediaTypes"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("EnableInheritedMediaTypes", true); }
|
||||
}
|
||||
|
||||
string IContentSection.NotificationEmailAddress
|
||||
|
||||
@@ -3,42 +3,24 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class ContentScriptEditorElement : ConfigurationElement
|
||||
internal class ContentScriptEditorElement : UmbracoConfigurationElement
|
||||
{
|
||||
[ConfigurationProperty("scriptFolderPath")]
|
||||
internal InnerTextConfigurationElement<string> ScriptFolderPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["scriptFolderPath"],
|
||||
//set the default
|
||||
"/scripts");
|
||||
}
|
||||
get { return GetOptionalTextElement("scriptFolderPath", "/scripts"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("scriptFileTypes")]
|
||||
internal OptionalCommaDelimitedConfigurationElement ScriptFileTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalCommaDelimitedConfigurationElement(
|
||||
(OptionalCommaDelimitedConfigurationElement)this["scriptFileTypes"],
|
||||
//set the default
|
||||
new[] { "js", "xml" });
|
||||
}
|
||||
get { return GetOptionalDelimitedElement("scriptFileTypes", new[] {"js", "xml"}); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("scriptDisableEditor")]
|
||||
internal InnerTextConfigurationElement<bool> ScriptEditorDisable
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>) this["scriptDisableEditor"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("scriptDisableEditor", false); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
Guid Id { get; }
|
||||
string RepositoryUrl { get; }
|
||||
string WebServiceUrl { get; }
|
||||
bool HasCustomWebServiceUrl { get; }
|
||||
bool HasCustomWebServiceUrl { get; }
|
||||
string RestApiUrl { get; }
|
||||
}
|
||||
}
|
||||
+5
-29
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class ImagingAutoFillUploadFieldElement : ConfigurationElement, IImagingAutoFillUploadField
|
||||
internal class ImagingAutoFillUploadFieldElement : UmbracoConfigurationElement, IImagingAutoFillUploadField
|
||||
{
|
||||
/// <summary>
|
||||
/// Allow setting internally so we can create a default
|
||||
@@ -17,49 +17,25 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
[ConfigurationProperty("widthFieldAlias")]
|
||||
internal InnerTextConfigurationElement<string> WidthFieldAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["widthFieldAlias"],
|
||||
//set the default
|
||||
"umbracoWidth");
|
||||
}
|
||||
get { return GetOptionalTextElement("widthFieldAlias", "umbracoWidth"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("heightFieldAlias")]
|
||||
internal InnerTextConfigurationElement<string> HeightFieldAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["heightFieldAlias"],
|
||||
//set the default
|
||||
"umbracoHeight");
|
||||
}
|
||||
get { return GetOptionalTextElement("heightFieldAlias", "umbracoHeight"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("lengthFieldAlias")]
|
||||
internal InnerTextConfigurationElement<string> LengthFieldAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["lengthFieldAlias"],
|
||||
//set the default
|
||||
"umbracoBytes");
|
||||
}
|
||||
get { return GetOptionalTextElement("lengthFieldAlias", "umbracoBytes"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("extensionFieldAlias")]
|
||||
internal InnerTextConfigurationElement<string> ExtensionFieldAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["extensionFieldAlias"],
|
||||
//set the default
|
||||
"umbracoExtension");
|
||||
}
|
||||
get { return GetOptionalTextElement("extensionFieldAlias", "umbracoExtension"); }
|
||||
}
|
||||
|
||||
string IImagingAutoFillUploadField.Alias
|
||||
|
||||
@@ -3,67 +3,37 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class LoggingElement : ConfigurationElement, ILoggingSection
|
||||
internal class LoggingElement : UmbracoConfigurationElement, ILoggingSection
|
||||
{
|
||||
|
||||
[ConfigurationProperty("autoCleanLogs")]
|
||||
internal InnerTextConfigurationElement<bool> AutoCleanLogs
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["autoCleanLogs"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("autoCleanLogs", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("enableLogging")]
|
||||
internal InnerTextConfigurationElement<bool> EnableLogging
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["enableLogging"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("enableLogging", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("enableAsyncLogging")]
|
||||
internal InnerTextConfigurationElement<bool> EnableAsyncLogging
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["enableAsyncLogging"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("enableAsyncLogging", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("cleaningMiliseconds")]
|
||||
internal InnerTextConfigurationElement<int> CleaningMiliseconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<int>(
|
||||
(InnerTextConfigurationElement<int>)this["cleaningMiliseconds"],
|
||||
//set the default
|
||||
-1);
|
||||
}
|
||||
get { return GetOptionalTextElement("cleaningMiliseconds", -1); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("maxLogAge")]
|
||||
internal InnerTextConfigurationElement<int> MaxLogAge
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<int>(
|
||||
(InnerTextConfigurationElement<int>)this["maxLogAge"],
|
||||
//set the default
|
||||
-1);
|
||||
}
|
||||
get { return GetOptionalTextElement("maxLogAge", -1); }
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(DisabledLogTypesCollection), AddItemName = "logTypeAlias")]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class NotificationsElement : ConfigurationElement
|
||||
internal class NotificationsElement : UmbracoConfigurationElement
|
||||
{
|
||||
[ConfigurationProperty("email")]
|
||||
internal InnerTextConfigurationElement<string> NotificationEmailAddress
|
||||
@@ -13,13 +13,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
[ConfigurationProperty("disableHtmlEmail")]
|
||||
internal InnerTextConfigurationElement<bool> DisableHtmlEmail
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>) this["disableHtmlEmail"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("disableHtmlEmail", false); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public static class RepositoryConfigExtensions
|
||||
{
|
||||
//Our package repo
|
||||
private static readonly Guid RepoGuid = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66");
|
||||
|
||||
public static IRepository GetDefault(this IRepositoriesSection repos)
|
||||
{
|
||||
var found = repos.Repositories.FirstOrDefault(x => x.Id == RepoGuid);
|
||||
if (found == null)
|
||||
throw new InvalidOperationException("No default package repository found with id " + RepoGuid);
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,16 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get
|
||||
{
|
||||
var prop = Properties["webserviceurl"];
|
||||
var repoUrl = this[prop] as ConfigurationElement;
|
||||
return (repoUrl != null && repoUrl.ElementInformation.IsPresent);
|
||||
return (string) prop.DefaultValue != (string) this[prop];
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty("restapiurl", DefaultValue = "https://our.umbraco.org/webapi/packages/v1")]
|
||||
public string RestApiUrl
|
||||
{
|
||||
get { return (string)base["restapiurl"]; }
|
||||
set { base["restapiurl"] = value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,30 +5,18 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class RequestHandlerElement : ConfigurationElement, IRequestHandlerSection
|
||||
internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSection
|
||||
{
|
||||
[ConfigurationProperty("useDomainPrefixes")]
|
||||
public InnerTextConfigurationElement<bool> UseDomainPrefixes
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["useDomainPrefixes"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("useDomainPrefixes", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("addTrailingSlash")]
|
||||
public InnerTextConfigurationElement<bool> AddTrailingSlash
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["addTrailingSlash"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("addTrailingSlash", true); }
|
||||
}
|
||||
|
||||
private UrlReplacingElement _defaultUrlReplacing;
|
||||
|
||||
@@ -2,66 +2,36 @@
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class SecurityElement : ConfigurationElement, ISecuritySection
|
||||
internal class SecurityElement : UmbracoConfigurationElement, ISecuritySection
|
||||
{
|
||||
[ConfigurationProperty("keepUserLoggedIn")]
|
||||
internal InnerTextConfigurationElement<bool> KeepUserLoggedIn
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["keepUserLoggedIn"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("keepUserLoggedIn", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("hideDisabledUsersInBackoffice")]
|
||||
internal InnerTextConfigurationElement<bool> HideDisabledUsersInBackoffice
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["hideDisabledUsersInBackoffice"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("hideDisabledUsersInBackoffice", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("allowPasswordReset")]
|
||||
internal InnerTextConfigurationElement<bool> AllowPasswordReset
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["allowPasswordReset"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("allowPasswordReset", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("authCookieName")]
|
||||
internal InnerTextConfigurationElement<string> AuthCookieName
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["authCookieName"],
|
||||
//set the default
|
||||
Constants.Web.AuthCookieName);
|
||||
}
|
||||
get { return GetOptionalTextElement("authCookieName", Constants.Web.AuthCookieName); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("authCookieDomain")]
|
||||
internal InnerTextConfigurationElement<string> AuthCookieDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<string>(
|
||||
(InnerTextConfigurationElement<string>)this["authCookieDomain"],
|
||||
//set the default
|
||||
null);
|
||||
}
|
||||
get { return GetOptionalTextElement<string>("authCookieDomain", null); }
|
||||
}
|
||||
|
||||
bool ISecuritySection.KeepUserLoggedIn
|
||||
|
||||
@@ -3,55 +3,31 @@ using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class TemplatesElement : ConfigurationElement, ITemplatesSection
|
||||
internal class TemplatesElement : UmbracoConfigurationElement, ITemplatesSection
|
||||
{
|
||||
[ConfigurationProperty("useAspNetMasterPages")]
|
||||
internal InnerTextConfigurationElement<bool> UseAspNetMasterPages
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["useAspNetMasterPages"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("useAspNetMasterPages", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("enableSkinSupport")]
|
||||
internal InnerTextConfigurationElement<bool> EnableSkinSupport
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["enableSkinSupport"],
|
||||
//set the default
|
||||
true);
|
||||
}
|
||||
get { return GetOptionalTextElement("enableSkinSupport", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("defaultRenderingEngine", IsRequired = true)]
|
||||
internal InnerTextConfigurationElement<RenderingEngine> DefaultRenderingEngine
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<RenderingEngine>(
|
||||
(InnerTextConfigurationElement<RenderingEngine>)this["defaultRenderingEngine"],
|
||||
//set the default
|
||||
RenderingEngine.Mvc);
|
||||
}
|
||||
get { return GetOptionalTextElement("defaultRenderingEngine", RenderingEngine.Mvc); }
|
||||
}
|
||||
|
||||
[Obsolete("This has no affect and will be removed in future versions")]
|
||||
[ConfigurationProperty("enableTemplateFolders")]
|
||||
internal InnerTextConfigurationElement<bool> EnableTemplateFolders
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OptionalInnerTextConfigurationElement<bool>(
|
||||
(InnerTextConfigurationElement<bool>)this["enableTemplateFolders"],
|
||||
//set the default
|
||||
false);
|
||||
}
|
||||
get { return GetOptionalTextElement("enableTemplateFolders", false); }
|
||||
}
|
||||
|
||||
bool ITemplatesSection.UseAspNetMasterPages
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class with shared helper methods
|
||||
/// </summary>
|
||||
internal class UmbracoConfigurationElement : ConfigurationElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Used so the RawElement types are not re-created every time they are accessed
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, RawXmlConfigurationElement> _rawElements = new ConcurrentDictionary<string, RawXmlConfigurationElement>();
|
||||
|
||||
protected OptionalInnerTextConfigurationElement<T> GetOptionalTextElement<T>(string name, T defaultVal)
|
||||
{
|
||||
return (OptionalInnerTextConfigurationElement<T>) _rawElements.GetOrAdd(
|
||||
name,
|
||||
s => new OptionalInnerTextConfigurationElement<T>(
|
||||
(InnerTextConfigurationElement<T>) this[s],
|
||||
//set the default
|
||||
defaultVal));
|
||||
}
|
||||
|
||||
protected OptionalCommaDelimitedConfigurationElement GetOptionalDelimitedElement(string name, string[] defaultVal)
|
||||
{
|
||||
return (OptionalCommaDelimitedConfigurationElement) _rawElements.GetOrAdd(
|
||||
name,
|
||||
s => new OptionalCommaDelimitedConfigurationElement(
|
||||
(CommaDelimitedConfigurationElement) this[name],
|
||||
//set the default
|
||||
defaultVal));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.5.2");
|
||||
private static readonly Version Version = new Version("7.5.13");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -122,6 +122,11 @@ namespace Umbraco.Core
|
||||
/// MediaType alias for an image.
|
||||
/// </summary>
|
||||
public const string Image = "Image";
|
||||
|
||||
/// <summary>
|
||||
/// MediaType alias indicating allowing auto-selection.
|
||||
/// </summary>
|
||||
public const string AutoSelect = "umbracoAutoSelect";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Document object.
|
||||
/// </summary>
|
||||
public static readonly Guid DocumentGuid = new Guid(Document);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Document Type object.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -30,12 +31,13 @@ using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Core.Strings;
|
||||
using IntegerValidator = Umbraco.Core.PropertyEditors.IntegerValidator;
|
||||
using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application
|
||||
/// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not provide any startup functionality relating to web objects
|
||||
@@ -191,14 +193,14 @@ namespace Umbraco.Core
|
||||
protected virtual CacheHelper CreateApplicationCache()
|
||||
{
|
||||
var cacheHelper = new CacheHelper(
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
|
||||
new StaticCacheProvider(),
|
||||
//we have no request based cache when not running in web-based context
|
||||
new NullCacheProvider(),
|
||||
new IsolatedRuntimeCache(type =>
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
|
||||
|
||||
@@ -251,18 +253,18 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special method to initialize the ApplicationEventsResolver and any modifications required for it such
|
||||
/// Special method to initialize the ApplicationEventsResolver and any modifications required for it such
|
||||
/// as adding custom types to the resolver.
|
||||
/// </summary>
|
||||
protected virtual void InitializeApplicationEventsResolver()
|
||||
{
|
||||
//find and initialize the application startup handlers, we need to initialize this resolver here because
|
||||
//it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to
|
||||
//it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to
|
||||
//events and to call their events during bootup.
|
||||
//ApplicationStartupHandler.RegisterHandlers();
|
||||
//... and set the special flag to let us resolve before frozen resolution
|
||||
ApplicationEventsResolver.Current = new ApplicationEventsResolver(
|
||||
ServiceProvider,
|
||||
ServiceProvider,
|
||||
ProfilingLogger.Logger,
|
||||
PluginManager.ResolveApplicationStartupHandlers())
|
||||
{
|
||||
@@ -282,7 +284,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur &
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur &
|
||||
/// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
|
||||
/// </summary>
|
||||
/// <param name="afterStartup"></param>
|
||||
@@ -334,7 +336,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (_isComplete)
|
||||
throw new InvalidOperationException("The boot manager has already been completed");
|
||||
|
||||
|
||||
FreezeResolution();
|
||||
|
||||
//Here we need to make sure the db can be connected to
|
||||
@@ -366,7 +368,7 @@ namespace Umbraco.Core
|
||||
ProfilingLogger.Logger.Error<CoreBootManager>("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Now, startup all of our legacy startup handler
|
||||
@@ -413,7 +415,7 @@ namespace Umbraco.Core
|
||||
|
||||
if (currentTry == 5)
|
||||
{
|
||||
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
|
||||
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but Umbraco cannot connect to the database.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +457,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
ServerRegistrarResolver.Current = new ServerRegistrarResolver(new ConfigServerRegistrar());
|
||||
}
|
||||
else if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"]))
|
||||
{
|
||||
ServerRegistrarResolver.Current = new ServerRegistrarResolver(new SingleServerRegistrar());
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerRegistrarResolver.Current = new ServerRegistrarResolver(
|
||||
@@ -462,7 +468,6 @@ namespace Umbraco.Core
|
||||
new Lazy<IServerRegistrationService>(() => ApplicationContext.Services.ServerRegistrationService),
|
||||
new DatabaseServerRegistrarOptions()));
|
||||
}
|
||||
|
||||
|
||||
//by default we'll use the database server messenger with default options (no callbacks),
|
||||
// this will be overridden in the web startup
|
||||
@@ -473,7 +478,7 @@ namespace Umbraco.Core
|
||||
ServiceProvider, ProfilingLogger.Logger,
|
||||
() => PluginManager.ResolveAssignedMapperTypes());
|
||||
|
||||
|
||||
|
||||
//RepositoryResolver.Current = new RepositoryResolver(
|
||||
// new RepositoryFactory(ApplicationCache));
|
||||
|
||||
|
||||
@@ -34,6 +34,12 @@ namespace Umbraco.Core
|
||||
private string _connectionString;
|
||||
private string _providerName;
|
||||
private DatabaseSchemaResult _result;
|
||||
private DateTime? _connectionLastChecked = null;
|
||||
|
||||
/// <summary>
|
||||
/// The number of minutes to throttle the checks to CanConnect
|
||||
/// </summary>
|
||||
private const int ConnectionCheckMinutes = 1;
|
||||
|
||||
[Obsolete("Use the constructor specifying all dependencies instead")]
|
||||
public DatabaseContext(IDatabaseFactory factory)
|
||||
@@ -110,13 +116,28 @@ namespace Umbraco.Core
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsDatabaseConfigured == false) return false;
|
||||
var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
|
||||
LogHelper.Info<DatabaseContext>("CanConnect = " + canConnect);
|
||||
return canConnect;
|
||||
if (IsDatabaseConfigured == false)
|
||||
return false;
|
||||
|
||||
//Don't check again if the timeout period hasn't elapsed
|
||||
//this ensures we don't keep checking the connection too many times in a row like during startup.
|
||||
//Do check if the _connectionLastChecked is null which means we're just initializing or it could
|
||||
//not connect last time it was checked.
|
||||
if ((_connectionLastChecked.HasValue && (DateTime.Now - _connectionLastChecked.Value).TotalMinutes > ConnectionCheckMinutes)
|
||||
|| _connectionLastChecked.HasValue == false)
|
||||
{
|
||||
var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
|
||||
LogHelper.Info<DatabaseContext>("CanConnect = " + canConnect);
|
||||
|
||||
_connectionLastChecked = canConnect == false ? null : (DateTime?) DateTime.Now;
|
||||
return canConnect;
|
||||
}
|
||||
|
||||
return _connectionLastChecked.HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured umbraco db connection string.
|
||||
/// </summary>
|
||||
@@ -174,7 +195,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public void ConfigureEmbeddedDatabaseConnection()
|
||||
{
|
||||
const string providerName = "System.Data.SqlServerCe.4.0";
|
||||
const string providerName = Constants.DatabaseProviders.SqlCe;
|
||||
|
||||
var connectionString = GetEmbeddedDatabaseConnectionString();
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
@@ -182,15 +203,10 @@ namespace Umbraco.Core
|
||||
var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_Data", "Umbraco.sdf");
|
||||
if (File.Exists(path) == false)
|
||||
{
|
||||
var engine = new SqlCeEngine(connectionString);
|
||||
engine.CreateDatabase();
|
||||
|
||||
// SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here
|
||||
// since it's been like this for quite some time
|
||||
//using (var engine = new SqlCeEngine(connectionString))
|
||||
//{
|
||||
// engine.CreateDatabase();
|
||||
//}
|
||||
using (var engine = new SqlCeEngine(connectionString))
|
||||
{
|
||||
engine.CreateDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
Initialize(providerName);
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Diagnostics
|
||||
{
|
||||
// taken from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/
|
||||
// and https://blogs.msdn.microsoft.com/dondu/2010/10/31/writing-minidumps-from-exceptions-in-c/
|
||||
// which itself got it from http://blog.kalmbach-software.de/2008/12/13/writing-minidumps-in-c/
|
||||
|
||||
internal static class MiniDump
|
||||
{
|
||||
private static readonly object LockO = new object();
|
||||
|
||||
[Flags]
|
||||
public enum Option : uint
|
||||
{
|
||||
// From dbghelp.h:
|
||||
Normal = 0x00000000,
|
||||
WithDataSegs = 0x00000001,
|
||||
WithFullMemory = 0x00000002,
|
||||
WithHandleData = 0x00000004,
|
||||
FilterMemory = 0x00000008,
|
||||
ScanMemory = 0x00000010,
|
||||
WithUnloadedModules = 0x00000020,
|
||||
WithIndirectlyReferencedMemory = 0x00000040,
|
||||
FilterModulePaths = 0x00000080,
|
||||
WithProcessThreadData = 0x00000100,
|
||||
WithPrivateReadWriteMemory = 0x00000200,
|
||||
WithoutOptionalData = 0x00000400,
|
||||
WithFullMemoryInfo = 0x00000800,
|
||||
WithThreadInfo = 0x00001000,
|
||||
WithCodeSegs = 0x00002000,
|
||||
WithoutAuxiliaryState = 0x00004000,
|
||||
WithFullAuxiliaryState = 0x00008000,
|
||||
WithPrivateWriteCopyMemory = 0x00010000,
|
||||
IgnoreInaccessibleMemory = 0x00020000,
|
||||
ValidTypeFlags = 0x0003ffff,
|
||||
}
|
||||
|
||||
//typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
|
||||
// DWORD ThreadId;
|
||||
// PEXCEPTION_POINTERS ExceptionPointers;
|
||||
// BOOL ClientPointers;
|
||||
//} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
|
||||
public struct MiniDumpExceptionInformation
|
||||
{
|
||||
public uint ThreadId;
|
||||
public IntPtr ExceptionPointers;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool ClientPointers;
|
||||
}
|
||||
|
||||
//BOOL
|
||||
//WINAPI
|
||||
//MiniDumpWriteDump(
|
||||
// __in HANDLE hProcess,
|
||||
// __in DWORD ProcessId,
|
||||
// __in HANDLE hFile,
|
||||
// __in MINIDUMP_TYPE DumpType,
|
||||
// __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
// __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
// __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||||
// );
|
||||
|
||||
// Overload requiring MiniDumpExceptionInformation
|
||||
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);
|
||||
|
||||
// Overload supporting MiniDumpExceptionInformation == NULL
|
||||
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
|
||||
private static extern uint GetCurrentThreadId();
|
||||
|
||||
private static bool Write(SafeHandle fileHandle, Option options, bool withException = false)
|
||||
{
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
var currentProcessHandle = currentProcess.Handle;
|
||||
var currentProcessId = (uint)currentProcess.Id;
|
||||
|
||||
MiniDumpExceptionInformation exp;
|
||||
|
||||
exp.ThreadId = GetCurrentThreadId();
|
||||
exp.ClientPointers = false;
|
||||
exp.ExceptionPointers = IntPtr.Zero;
|
||||
|
||||
if (withException)
|
||||
exp.ExceptionPointers = Marshal.GetExceptionPointers();
|
||||
|
||||
var bRet = exp.ExceptionPointers == IntPtr.Zero
|
||||
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
|
||||
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
public static bool Dump(Option options = Option.WithFullMemory, bool withException = false)
|
||||
{
|
||||
lock (LockO)
|
||||
{
|
||||
// work around "stack trace is not available while minidump debugging",
|
||||
// by making sure a local var (that we can inspect) contains the stack trace.
|
||||
// getting the call stack before it is unwound would require a special exception
|
||||
// filter everywhere in our code = not!
|
||||
var stacktrace = withException ? Environment.StackTrace : string.Empty;
|
||||
|
||||
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
|
||||
if (Directory.Exists(filepath) == false)
|
||||
Directory.CreateDirectory(filepath);
|
||||
|
||||
var filename = Path.Combine(filepath, string.Format("{0:yyyyMMddTHHmmss}.{1}.dmp", DateTime.UtcNow, Guid.NewGuid().ToString("N").Substring(0, 4)));
|
||||
using (var stream = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Write))
|
||||
{
|
||||
return Write(stream.SafeFileHandle, options, withException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool OkToDump()
|
||||
{
|
||||
lock (LockO)
|
||||
{
|
||||
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
|
||||
if (Directory.Exists(filepath) == false) return true;
|
||||
var count = Directory.GetFiles(filepath, "*.dmp").Length;
|
||||
return count < 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,8 +128,14 @@ namespace Umbraco.Core.Events
|
||||
get { return TargetSemVersion.GetVersion(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin version of the migration, i.e. the one that is currently installed.
|
||||
/// </summary>
|
||||
public SemVersion ConfiguredSemVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target version of the migration.
|
||||
/// </summary>
|
||||
public SemVersion TargetSemVersion { get; private set; }
|
||||
|
||||
public string ProductName { get; private set; }
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
internal class ConnectionException : Exception
|
||||
{
|
||||
public ConnectionException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
using (var file = fs.OpenFile(path))
|
||||
{
|
||||
using (var sr = new StreamReader(file))
|
||||
{
|
||||
var str = sr.ReadToEnd();
|
||||
return str.Length;
|
||||
}
|
||||
return file.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,39 +7,144 @@ 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
|
||||
//TODO: Should probably implement a rename?
|
||||
|
||||
public interface IFileSystem
|
||||
/// <summary>
|
||||
/// Provides methods allowing the manipulation of files within an Umbraco application.
|
||||
/// </summary>
|
||||
public interface IFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all directories matching the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the directories.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="IEnumerable{String}"/> representing the matched directories.
|
||||
/// </returns>
|
||||
IEnumerable<string> GetDirectories(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The name of the directory to remove.</param>
|
||||
void DeleteDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory and, if indicated, any subdirectories and files in the directory.
|
||||
/// </summary>
|
||||
/// <remarks>Azure blob storage has no real concept of directories so deletion is always recursive.</remarks>
|
||||
/// <param name="path">The name of the directory to remove.</param>
|
||||
/// <param name="recursive">Whether to remove directories, subdirectories, and files in path.</param>
|
||||
void DeleteDirectory(string path, bool recursive);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The directory to check.</param>
|
||||
/// <returns>
|
||||
/// <c>True</c> if the directory exists and the user has permission to view it; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
bool DirectoryExists(string path);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a file to the file system.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the given file.</param>
|
||||
/// <param name="stream">The <see cref="Stream"/> containing the file contents.</param>
|
||||
void AddFile(string path, Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a file to the file system.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the given file.</param>
|
||||
/// <param name="stream">The <see cref="Stream"/> containing the file contents.</param>
|
||||
/// <param name="overrideIfExists">Whether to override the file if it already exists.</param>
|
||||
void AddFile(string path, Stream stream, bool overrideIfExists);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files matching the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the files.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="IEnumerable{String}"/> representing the matched files.
|
||||
/// </returns>
|
||||
IEnumerable<string> GetFiles(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files matching the given path and filter.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the files.</param>
|
||||
/// <param name="filter">A filter that allows the querying of file extension. <example>*.jpg</example></param>
|
||||
/// <returns>
|
||||
/// The <see cref="IEnumerable{String}"/> representing the matched files.
|
||||
/// </returns>
|
||||
IEnumerable<string> GetFiles(string path, string filter);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Stream"/> representing the file at the gieven path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <returns>
|
||||
/// <see cref="Stream"/>.
|
||||
/// </returns>
|
||||
Stream OpenFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The name of the file to remove.</param>
|
||||
void DeleteFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified file exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to check.</param>
|
||||
/// <returns>
|
||||
/// <c>True</c> if the file exists and the user has permission to view it; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
bool FileExists(string path);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application relative path to the file.
|
||||
/// </summary>
|
||||
/// <param name="fullPathOrUrl">The full path or url.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="string"/> representing the relative path.
|
||||
/// </returns>
|
||||
string GetRelativePath(string fullPathOrUrl);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full qualified path to the file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to return the full path for.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="string"/> representing the full path.
|
||||
/// </returns>
|
||||
string GetFullPath(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application relative url to the file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to return the url for.</param>
|
||||
/// <returns>
|
||||
/// <see cref="string"/> representing the relative url.
|
||||
/// </returns>
|
||||
string GetUrl(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modified date/time of the file, expressed as a UTC value.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <returns>
|
||||
/// <see cref="DateTimeOffset"/>.
|
||||
/// </returns>
|
||||
DateTimeOffset GetLastModified(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the created date/time of the file, expressed as a UTC value.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <returns>
|
||||
/// <see cref="DateTimeOffset"/>.
|
||||
/// </returns>
|
||||
DateTimeOffset GetCreated(string path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Core.IO
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // ensure it exists
|
||||
|
||||
if (stream.CanSeek)
|
||||
stream.Seek(0, 0);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (var destination = (Stream)File.Create(fullPath))
|
||||
stream.CopyTo(destination);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -72,15 +73,28 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GlobalSettings.ContentCacheXmlStoredInCodeGen && SystemUtilities.GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
|
||||
{
|
||||
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
|
||||
switch (GlobalSettings.ContentCacheXmlStorageLocation)
|
||||
{
|
||||
case ContentXmlStorage.AspNetTemp:
|
||||
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
|
||||
case ContentXmlStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
return Path.Combine(cachePath, "umbraco.config");
|
||||
case ContentXmlStorage.Default:
|
||||
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use GlobalSettings.ContentCacheXmlStoredInCodeGen instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static bool ContentCacheXmlStoredInCodeGen
|
||||
{
|
||||
get { return GlobalSettings.ContentCacheXmlStoredInCodeGen; }
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.IO
|
||||
private void Initialize()
|
||||
{
|
||||
Filename = _fs.GetFileName(Path);
|
||||
Extension = _fs.GetExtension(Path) != null
|
||||
Extension = string.IsNullOrEmpty(_fs.GetExtension(Path)) == false
|
||||
? _fs.GetExtension(Path).Substring(1).ToLowerInvariant()
|
||||
: "";
|
||||
Url = _fs.GetUrl(Path);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace Umbraco.Core.Logging
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ImageProcessor.Common.Exceptions;
|
||||
|
||||
using ImageProcessor.Common.Exceptions;
|
||||
namespace Umbraco.Core.Logging
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A logger for explicitly logging ImageProcessor exceptions.
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Diagnostics;
|
||||
|
||||
namespace Umbraco.Core.Logging
|
||||
{
|
||||
@@ -56,18 +59,72 @@ namespace Umbraco.Core.Logging
|
||||
internal ILog LoggerFor(object getTypeFromInstance)
|
||||
{
|
||||
if (getTypeFromInstance == null) throw new ArgumentNullException("getTypeFromInstance");
|
||||
|
||||
|
||||
return LogManager.GetLogger(getTypeFromInstance.GetType());
|
||||
}
|
||||
|
||||
|
||||
public void Error(Type callingType, string message, Exception exception)
|
||||
{
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger != null)
|
||||
logger.Error((message), exception);
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null) return;
|
||||
|
||||
var dump = false;
|
||||
|
||||
if (IsTimeoutThreadAbortException(exception))
|
||||
{
|
||||
message += "\r\nThe thread has been aborted, because the request has timed out.";
|
||||
|
||||
// dump if configured, or if stacktrace contains Monitor.ReliableEnter
|
||||
dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
|
||||
|
||||
// dump if it is ok to dump (might have a cap on number of dump...)
|
||||
dump &= MiniDump.OkToDump();
|
||||
}
|
||||
|
||||
if (dump)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dumped = MiniDump.Dump(withException: true);
|
||||
message += dumped
|
||||
? "\r\nA minidump was created in App_Data/MiniDump"
|
||||
: "\r\nFailed to create a minidump";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message += string.Format("\r\nFailed to create a minidump ({0}: {1})", e.GetType().FullName, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
logger.Error(message, exception);
|
||||
}
|
||||
|
||||
public void Warn(Type callingType, string message, params Func<object>[] formatItems)
|
||||
private static bool IsMonitorEnterThreadAbortException(Exception exception)
|
||||
{
|
||||
var abort = exception as ThreadAbortException;
|
||||
if (abort == null) return false;
|
||||
|
||||
var stacktrace = abort.StackTrace;
|
||||
return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
|
||||
}
|
||||
|
||||
private static bool IsTimeoutThreadAbortException(Exception exception)
|
||||
{
|
||||
var abort = exception as ThreadAbortException;
|
||||
if (abort == null) return false;
|
||||
|
||||
if (abort.ExceptionState == null) return false;
|
||||
|
||||
var stateType = abort.ExceptionState.GetType();
|
||||
if (stateType.FullName != "System.Web.HttpApplication+CancelModuleException") return false;
|
||||
|
||||
var timeoutField = stateType.GetField("_timeout", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (timeoutField == null) return false;
|
||||
|
||||
return (bool) timeoutField.GetValue(abort.ExceptionState);
|
||||
}
|
||||
|
||||
public void Warn(Type callingType, string message, params Func<object>[] formatItems)
|
||||
{
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null || logger.IsWarnEnabled == false) return;
|
||||
@@ -82,7 +139,7 @@ namespace Umbraco.Core.Logging
|
||||
if (showHttpTrace && HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.Trace.Warn(callingType.Name, string.Format(message, formatItems.Select(x => x.Invoke()).ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null || logger.IsWarnEnabled == false) return;
|
||||
@@ -99,7 +156,7 @@ namespace Umbraco.Core.Logging
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null || logger.IsWarnEnabled == false) return;
|
||||
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
|
||||
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
|
||||
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+60
-22
@@ -1,18 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
// represents the main domain
|
||||
class MainDom : IRegisteredObject
|
||||
/// <summary>
|
||||
/// Represents the main AppDomain running for a given application.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>There can be only one "main" AppDomain running for a given application at a time.</para>
|
||||
/// <para>When an AppDomain starts, it tries to acquire the main domain status.</para>
|
||||
/// <para>When an AppDomain stops (eg the application is restarting) it should release the main domain status.</para>
|
||||
/// <para>It is possible to register against the MainDom and be notified when it is released.</para>
|
||||
/// </remarks>
|
||||
internal class MainDom : IRegisteredObject
|
||||
{
|
||||
#region Vars
|
||||
|
||||
@@ -25,7 +28,7 @@ namespace Umbraco.Core
|
||||
private readonly AsyncLock _asyncLock;
|
||||
private IDisposable _asyncLocker;
|
||||
|
||||
// event wait handle used to notify current main domain that it should
|
||||
// event wait handle used to notify current main domain that it should
|
||||
// release the lock because a new domain wants to be the main domain
|
||||
private readonly EventWaitHandle _signal;
|
||||
|
||||
@@ -34,16 +37,26 @@ namespace Umbraco.Core
|
||||
private volatile bool _signaled; // we have been signaled
|
||||
|
||||
// actions to run before releasing the main domain
|
||||
private readonly SortedList<int, Action> _callbacks = new SortedList<int, Action>();
|
||||
private readonly SortedList<int, Action> _callbacks = new SortedList<int, Action>(new WeightComparer());
|
||||
|
||||
private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds
|
||||
|
||||
private class WeightComparer : IComparer<int>
|
||||
{
|
||||
public int Compare(int x, int y)
|
||||
{
|
||||
var result = x.CompareTo(y);
|
||||
// return "equal" as "greater than"
|
||||
return result == 0 ? 1 : result;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
// initializes a new instance of MainDom
|
||||
public MainDom(ILogger logger)
|
||||
internal MainDom(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
@@ -52,22 +65,47 @@ namespace Umbraco.Core
|
||||
if (HostingEnvironment.ApplicationID != null)
|
||||
appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
|
||||
|
||||
var lockName = "UMBRACO-" + appId + "-MAINDOM-LCK";
|
||||
// combining with the physical path because if running on eg IIS Express,
|
||||
// two sites could have the same appId even though they are different.
|
||||
//
|
||||
// now what could still collide is... two sites, running in two different processes
|
||||
// and having the same appId, and running on the same app physical path
|
||||
//
|
||||
// we *cannot* use the process ID here because when an AppPool restarts it is
|
||||
// a new process for the same application path
|
||||
|
||||
var appPath = HostingEnvironment.ApplicationPhysicalPath;
|
||||
var hash = (appId + ":::" + appPath).ToSHA1();
|
||||
|
||||
var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK";
|
||||
_asyncLock = new AsyncLock(lockName);
|
||||
|
||||
var eventName = "UMBRACO-" + appId + "-MAINDOM-EVT";
|
||||
var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT";
|
||||
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// register a main domain consumer
|
||||
/// <summary>
|
||||
/// Registers a resource that requires the current AppDomain to be the main domain to function.
|
||||
/// </summary>
|
||||
/// <param name="release">An action to execute before the AppDomain releases the main domain status.</param>
|
||||
/// <param name="weight">An optional weight (lower goes first).</param>
|
||||
/// <returns>A value indicating whether it was possible to register.</returns>
|
||||
public bool Register(Action release, int weight = 100)
|
||||
{
|
||||
return Register(null, release, weight);
|
||||
}
|
||||
|
||||
// register a main domain consumer
|
||||
/// <summary>
|
||||
/// Registers a resource that requires the current AppDomain to be the main domain to function.
|
||||
/// </summary>
|
||||
/// <param name="install">An action to execute when registering.</param>
|
||||
/// <param name="release">An action to execute before the AppDomain releases the main domain status.</param>
|
||||
/// <param name="weight">An optional weight (lower goes first).</param>
|
||||
/// <returns>A value indicating whether it was possible to register.</returns>
|
||||
/// <remarks>If registering is successful, then the <paramref name="install"/> action
|
||||
/// is guaranteed to execute before the AppDomain releases the main domain status.</remarks>
|
||||
public bool Register(Action install, Action release, int weight = 100)
|
||||
{
|
||||
lock (_locko)
|
||||
@@ -97,7 +135,7 @@ namespace Umbraco.Core
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Debug<MainDom>("Stopping...");
|
||||
_logger.Info<MainDom>("Stopping...");
|
||||
foreach (var callback in _callbacks.Values)
|
||||
{
|
||||
try
|
||||
@@ -109,7 +147,7 @@ namespace Umbraco.Core
|
||||
_logger.Error<MainDom>("Error while running callback, remaining callbacks will not run.", e);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
_logger.Debug<MainDom>("Stopped.");
|
||||
}
|
||||
@@ -118,12 +156,12 @@ namespace Umbraco.Core
|
||||
// in any case...
|
||||
_isMainDom = false;
|
||||
_asyncLocker.Dispose();
|
||||
_logger.Debug<MainDom>("Released MainDom.");
|
||||
_logger.Info<MainDom>("Released MainDom.");
|
||||
}
|
||||
}
|
||||
|
||||
// acquires the main domain
|
||||
public bool Acquire()
|
||||
internal bool Acquire()
|
||||
{
|
||||
lock (_locko) // we don't want the hosting environment to interfere by signaling
|
||||
{
|
||||
@@ -131,11 +169,11 @@ namespace Umbraco.Core
|
||||
// the handler is not installed so that would be the hosting environment
|
||||
if (_signaled)
|
||||
{
|
||||
_logger.Debug<MainDom>("Cannot acquire MainDom (signaled).");
|
||||
_logger.Info<MainDom>("Cannot acquire MainDom (signaled).");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Debug<MainDom>("Acquiring MainDom...");
|
||||
_logger.Info<MainDom>("Acquiring MainDom...");
|
||||
|
||||
// signal other instances that we want the lock, then wait one the lock,
|
||||
// which may timeout, and this is accepted - see comments below
|
||||
@@ -162,7 +200,7 @@ namespace Umbraco.Core
|
||||
|
||||
HostingEnvironment.RegisterObject(this);
|
||||
|
||||
_logger.Debug<MainDom>("Acquired MainDom.");
|
||||
_logger.Info<MainDom>("Acquired MainDom.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -174,7 +212,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// IRegisteredObject
|
||||
public void Stop(bool immediate)
|
||||
void IRegisteredObject.Stop(bool immediate)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -227,35 +227,37 @@ namespace Umbraco.Core.Media
|
||||
}
|
||||
|
||||
// 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 predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
|
||||
using (var ms = new MemoryStream())
|
||||
string newFileName;
|
||||
using (var ep = new EncoderParameters())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
fs.AddFile(predictableThumbnailNameJpg, ms);
|
||||
}
|
||||
// Save the new image using the dimensions of the image
|
||||
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
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);
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
fs.AddFile(predictableThumbnailNameJpg, ms);
|
||||
}
|
||||
|
||||
fs.AddFile(newFileName, ms);
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
fs.AddFile(newFileName, ms);
|
||||
}
|
||||
}
|
||||
|
||||
return new ResizedImage(widthTh, heightTh, newFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,9 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } }
|
||||
|
||||
[IgnoreDataMember]
|
||||
internal DateTime PublishedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Trashed state of the content object
|
||||
/// </summary>
|
||||
|
||||
@@ -404,8 +404,7 @@ namespace Umbraco.Core.Models
|
||||
return content.Properties
|
||||
.Where(property => propertyGroup.PropertyTypes
|
||||
.Select(propertyType => propertyType.Id)
|
||||
.Contains(property.PropertyTypeId))
|
||||
.OrderBy(x => x.PropertyType.SortOrder);
|
||||
.Contains(property.PropertyTypeId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -546,7 +545,7 @@ namespace Umbraco.Core.Models
|
||||
if (supportsResizing)
|
||||
{
|
||||
//get the original image from the original stream
|
||||
if (fileStream.CanSeek) fileStream.Seek(0, 0);
|
||||
if (fileStream.CanSeek) fileStream.Seek(0, SeekOrigin.Begin);
|
||||
using (var originalImage = Image.FromStream(fileStream))
|
||||
{
|
||||
var additionalSizes = new List<int>();
|
||||
|
||||
@@ -326,7 +326,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// A boolean flag indicating if a property type has been removed from this instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a ContentType, which Media is based on
|
||||
/// </summary
|
||||
/// </summary>
|
||||
public interface IMediaType : IContentTypeComposition
|
||||
{
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IUser, BackOfficeIdentityUser>()
|
||||
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
|
||||
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
|
||||
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
|
||||
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
|
||||
|
||||
@@ -24,12 +24,17 @@ namespace Umbraco.Core.Models.Identity
|
||||
///
|
||||
/// </summary>
|
||||
public IdentityUser()
|
||||
{
|
||||
{
|
||||
this.Claims = new List<TClaim>();
|
||||
this.Roles = new List<TRole>();
|
||||
this.Logins = new List<TLogin>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last login date
|
||||
/// </summary>
|
||||
public virtual DateTime? LastLoginDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Email
|
||||
///
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace Umbraco.Core.Models
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
|
||||
//TODO: Instead of 'new' this should explicitly implement one of the collection interfaces members
|
||||
internal new void Add(PropertyType item)
|
||||
{
|
||||
using (new WriteLock(_addLocker))
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Models
|
||||
NoAccessNodeId = noAccessNode.Id;
|
||||
_protectedNodeId = protectedNode.Id;
|
||||
|
||||
_ruleCollection = new ObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection = new ObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Umbraco.Core.Models
|
||||
internal IEnumerable<Guid> RemovedRules
|
||||
{
|
||||
get { return _removedRules; }
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<PublicAccessRule> Rules
|
||||
{
|
||||
@@ -107,10 +107,13 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public void ClearRules()
|
||||
{
|
||||
foreach (var rule in _ruleCollection)
|
||||
{
|
||||
RemoveRule(rule);
|
||||
}
|
||||
_ruleCollection.Clear();
|
||||
}
|
||||
|
||||
|
||||
internal void ClearRemovedRules()
|
||||
{
|
||||
_removedRules.Clear();
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
@@ -126,7 +129,7 @@ namespace Umbraco.Core.Models
|
||||
get { return _noAccessNodeId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _noAccessNodeId, Ps.Value.NoAccessNodeIdSelector); }
|
||||
}
|
||||
|
||||
|
||||
[DataMember]
|
||||
public int ProtectedNodeId
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
|
||||
if (contentType == null)
|
||||
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
|
||||
throw new InvalidOperationException(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
|
||||
itemType.ToString().ToLower(), alias));
|
||||
|
||||
return new PublishedContentType(contentType);
|
||||
|
||||
@@ -106,12 +106,13 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <param name="propertyTypeAlias">The property type alias.</param>
|
||||
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
|
||||
/// <param name="propertyEditorAlias">The property editor alias.</param>
|
||||
/// <param name="initConverters">Generally used only for testing, in production this will always be true</param>
|
||||
/// <remarks>
|
||||
/// <para>The new published property type does not belong to a published content type.</para>
|
||||
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
|
||||
/// assumed to be valid and consistent.</para>
|
||||
/// </remarks>
|
||||
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
|
||||
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool initConverters = true)
|
||||
{
|
||||
// ContentType
|
||||
// - in unit tests, to be set by PublishedContentType when creating it
|
||||
@@ -122,7 +123,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
DataTypeId = dataTypeDefinitionId;
|
||||
PropertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
InitializeConverters();
|
||||
if (initConverters)
|
||||
InitializeConverters();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -19,5 +19,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("newest")]
|
||||
public bool Newest { get; set; }
|
||||
|
||||
[Column("updateDate")]
|
||||
public DateTime VersionDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyData")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("contentNodeId")]
|
||||
|
||||
@@ -1,15 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.UI.WebControls;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal static class UmbracoEntityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static void ValidatePathWithException(this NodeDto entity)
|
||||
{
|
||||
//don't validate if it's empty and it has no id
|
||||
if (entity.NodeId == default(int) && entity.Path.IsNullOrWhiteSpace())
|
||||
return;
|
||||
|
||||
if (entity.Path.IsNullOrWhiteSpace())
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an empty path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
|
||||
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (pathParts.Length < 2)
|
||||
{
|
||||
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
}
|
||||
|
||||
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
|
||||
{
|
||||
//the 2nd last id in the path must be it's parent id
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ValidatePath(this IUmbracoEntity entity)
|
||||
{
|
||||
//don't validate if it's empty and it has no id
|
||||
if (entity.HasIdentity == false && entity.Path.IsNullOrWhiteSpace())
|
||||
return true;
|
||||
|
||||
if (entity.Path.IsNullOrWhiteSpace())
|
||||
return false;
|
||||
|
||||
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (pathParts.Length < 2)
|
||||
{
|
||||
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
|
||||
{
|
||||
//the 2nd last id in the path must be it's parent id
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will validate the entity's path and if it's invalid it will fix it, if fixing is required it will recursively
|
||||
/// check and fix all ancestors if required.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="getParent">A callback specified to retrieve the parent entity of the entity</param>
|
||||
/// <param name="update">A callback specified to update a fixed entity</param>
|
||||
public static void EnsureValidPath<T>(this T entity,
|
||||
ILogger logger,
|
||||
Func<T, T> getParent,
|
||||
Action<T> update)
|
||||
where T: IUmbracoEntity
|
||||
{
|
||||
if (entity.HasIdentity == false)
|
||||
throw new InvalidOperationException("Could not ensure the entity path, the entity has not been assigned an identity");
|
||||
|
||||
if (entity.ValidatePath() == false)
|
||||
{
|
||||
logger.Warn(typeof(UmbracoEntityExtensions), string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.Id, entity.Path, entity.ParentId));
|
||||
if (entity.ParentId == -1)
|
||||
{
|
||||
entity.Path = string.Concat("-1,", entity.Id);
|
||||
//path changed, update it
|
||||
update(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parent = getParent(entity);
|
||||
if (parent == null)
|
||||
throw new NullReferenceException("Could not ensure path for entity " + entity.Id + " could not resolve it's parent " + entity.ParentId);
|
||||
|
||||
//the parent must also be valid!
|
||||
parent.EnsureValidPath(logger, getParent, update);
|
||||
|
||||
entity.Path = string.Concat(parent.Path, ",", entity.Id);
|
||||
//path changed, update it
|
||||
update(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasChildren(this IUmbracoEntity entity)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
@@ -83,5 +80,19 @@ namespace Umbraco.Core.Models
|
||||
if (media == null) throw new ArgumentNullException("media");
|
||||
return HasPathAccess(media.Path, user.StartMediaId, Constants.System.RecycleBinMedia);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this user is an admin.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this user is admin; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool IsAdmin(this IUser user)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return user.UserType.Alias == "admin";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,20 @@ using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// A resolver to return all IApplicationEvents objects
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
|
||||
/// <para>This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.</para>
|
||||
/// <para>Ordering handlers: handlers are ordered by (ascending) weight. By default, handlers from the Umbraco.* or Concorde.*
|
||||
/// assemblies have a -100 weight whereas any other handler has a weight of +100. A custom weight can be assigned to a handler
|
||||
/// by marking the class with the WeightAttribute. For example, the CacheRefresherEventHandler is marked with [Weight(int.MinValue)]
|
||||
/// because its events need to run before anything else. Positive weights are considered "user-space" while negative weights are
|
||||
/// "core". Finally, users can register a filter to process the list (after it has been ordered) and re-order it, or remove handlers.</para>
|
||||
/// <para>BEWARE! handlers order is an important thing, and removing handlers or reordering handlers can have unexpected consequences.</para>
|
||||
/// </remarks>
|
||||
internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase<ApplicationEventsResolver, IApplicationEventHandler>, IDisposable
|
||||
public sealed class ApplicationEventsResolver : ManyObjectsResolverBase<ApplicationEventsResolver, IApplicationEventHandler>, IDisposable
|
||||
{
|
||||
|
||||
private readonly LegacyStartupHandlerResolver _legacyResolver;
|
||||
|
||||
/// <summary>
|
||||
@@ -23,18 +28,18 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="applicationEventHandlers"></param>
|
||||
/// <param name="serviceProvider"></param>
|
||||
/// <param name="serviceProvider"></param>
|
||||
internal ApplicationEventsResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable<Type> applicationEventHandlers)
|
||||
: base(serviceProvider, logger, applicationEventHandlers)
|
||||
{
|
||||
{
|
||||
//create the legacy resolver and only include the legacy types
|
||||
_legacyResolver = new LegacyStartupHandlerResolver(
|
||||
_legacyResolver = new LegacyStartupHandlerResolver(
|
||||
serviceProvider, logger,
|
||||
applicationEventHandlers.Where(x => !TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>(x)));
|
||||
applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>(x) == false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override in order to only return types of IApplicationEventHandler and above,
|
||||
/// Override in order to only return types of IApplicationEventHandler and above,
|
||||
/// do not include the legacy types of IApplicationStartupHandler
|
||||
/// </summary>
|
||||
protected override IEnumerable<Type> InstanceTypes
|
||||
@@ -42,14 +47,60 @@ namespace Umbraco.Core.ObjectResolution
|
||||
get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom<IApplicationEventHandler>); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IApplicationEventHandler"/> implementations.
|
||||
/// </summary>
|
||||
public IEnumerable<IApplicationEventHandler> ApplicationEventHandlers
|
||||
private List<IApplicationEventHandler> _orderedAndFiltered;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IApplicationEventHandler"/> implementations.
|
||||
/// </summary>
|
||||
public IEnumerable<IApplicationEventHandler> ApplicationEventHandlers
|
||||
{
|
||||
get { return Values; }
|
||||
get
|
||||
{
|
||||
if (_orderedAndFiltered == null)
|
||||
{
|
||||
_orderedAndFiltered = GetSortedValues().ToList();
|
||||
if (FilterCollection != null)
|
||||
FilterCollection(_orderedAndFiltered);
|
||||
}
|
||||
return _orderedAndFiltered;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a delegate to filter the event handler list (EXPERT!).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This can be set on startup in the pre-boot process in either a custom boot manager or global.asax (UmbracoApplication).</para>
|
||||
/// <para>Allows custom logic to execute in order to filter and/or re-order the event handlers prior to executing.</para>
|
||||
/// <para>To be used by custom boot sequences where the boot loader needs to remove some handlers, or raise their priority.</para>
|
||||
/// <para>Filtering the event handler collection can have ugly consequences. Use with care.</para>
|
||||
/// </remarks>
|
||||
public Action<IList<IApplicationEventHandler>> FilterCollection
|
||||
{
|
||||
get { return _filterCollection; }
|
||||
set
|
||||
{
|
||||
if (_orderedAndFiltered != null)
|
||||
throw new InvalidOperationException("Cannot set the FilterCollection delegate once the ApplicationEventHandlers are resolved");
|
||||
if (_filterCollection != null)
|
||||
throw new InvalidOperationException("Cannot set the FilterCollection delegate once it's already been specified");
|
||||
|
||||
_filterCollection = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override int GetObjectWeight(object o)
|
||||
{
|
||||
var type = o.GetType();
|
||||
var attr = type.GetCustomAttribute<WeightAttribute>(true);
|
||||
if (attr != null) return attr.Weight;
|
||||
var name = type.Assembly.FullName;
|
||||
|
||||
// we should really attribute all our Core handlers, so this is temp
|
||||
var core = name.InvariantStartsWith("Umbraco.") || name.InvariantStartsWith("Concorde.");
|
||||
return core ? -DefaultPluginWeight : DefaultPluginWeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create instances of all of the legacy startup handlers
|
||||
/// </summary>
|
||||
@@ -62,11 +113,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
protected override bool SupportsClear
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsInsert
|
||||
{
|
||||
get { return false; }
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private class LegacyStartupHandlerResolver : ManyObjectsResolverBase<ApplicationEventsResolver, IApplicationStartupHandler>, IDisposable
|
||||
@@ -90,8 +141,9 @@ namespace Umbraco.Core.ObjectResolution
|
||||
|
||||
private bool _disposed;
|
||||
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
|
||||
private Action<IList<IApplicationEventHandler>> _filterCollection;
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
@@ -146,7 +198,8 @@ namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
_legacyResolver.Dispose();
|
||||
ResetCollections();
|
||||
_orderedAndFiltered.Clear();
|
||||
_orderedAndFiltered = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// for when there is some processing overhead (i.e. Type finding in assemblies) to return the Types used to instantiate the instances.
|
||||
/// In some these cases we don't want to have to type-find during application startup, only when we need to resolve the instances.</para>
|
||||
/// <para>Important notes about this resolver: it does not support Insert or Remove and therefore does not support any ordering unless
|
||||
/// the types are marked with the WeightedPluginAttribute.</para>
|
||||
/// the types are marked with the WeightAttribute.</para>
|
||||
/// </remarks>
|
||||
public abstract class LazyManyObjectsResolverBase<TResolver, TResolved> : ManyObjectsResolverBase<TResolver, TResolved>
|
||||
where TResolved : class
|
||||
|
||||
@@ -14,18 +14,19 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <typeparam name="TResolver">The type of the concrete resolver class.</typeparam>
|
||||
/// <typeparam name="TResolved">The type of the resolved objects.</typeparam>
|
||||
public abstract class ManyObjectsResolverBase<TResolver, TResolved> : ResolverBase<TResolver>
|
||||
where TResolved : class
|
||||
where TResolved : class
|
||||
where TResolver : ResolverBase
|
||||
{
|
||||
private Lazy<IEnumerable<TResolved>> _applicationInstances;
|
||||
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
private readonly string _httpContextKey;
|
||||
private readonly List<Type> _instanceTypes = new List<Type>();
|
||||
private IEnumerable<TResolved> _sortedValues;
|
||||
{
|
||||
private Lazy<IEnumerable<TResolved>> _applicationInstances;
|
||||
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
private readonly string _httpContextKey;
|
||||
private readonly List<Type> _instanceTypes = new List<Type>();
|
||||
private IEnumerable<TResolved> _sortedValues;
|
||||
private readonly Func<HttpContextBase> _httpContextGetter;
|
||||
|
||||
private int _defaultPluginWeight = 10;
|
||||
private int _defaultPluginWeight = 100;
|
||||
|
||||
#region Constructors
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
|
||||
@@ -42,12 +43,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
CanResolveBeforeFrozen = false;
|
||||
if (scope == ObjectLifetimeScope.HttpRequest)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null");
|
||||
|
||||
CurrentHttpContext = new HttpContextWrapper(HttpContext.Current);
|
||||
}
|
||||
_httpContextGetter = () => new HttpContextWrapper(HttpContext.Current);
|
||||
|
||||
ServiceProvider = serviceProvider;
|
||||
Logger = logger;
|
||||
@@ -61,11 +57,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use ctor specifying IServiceProvider instead")]
|
||||
protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, scope)
|
||||
{
|
||||
|
||||
}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
|
||||
@@ -76,19 +72,19 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
|
||||
protected ManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext)
|
||||
{
|
||||
{
|
||||
if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
CanResolveBeforeFrozen = false;
|
||||
Logger = logger;
|
||||
LifetimeScope = ObjectLifetimeScope.HttpRequest;
|
||||
_httpContextKey = GetType().FullName;
|
||||
LifetimeScope = ObjectLifetimeScope.HttpRequest;
|
||||
_httpContextKey = GetType().FullName;
|
||||
ServiceProvider = serviceProvider;
|
||||
CurrentHttpContext = httpContext;
|
||||
_instanceTypes = new List<Type>();
|
||||
_httpContextGetter = () => httpContext;
|
||||
_instanceTypes = new List<Type>();
|
||||
|
||||
InitializeAppInstances();
|
||||
}
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use ctor specifying IServiceProvider instead")]
|
||||
@@ -110,57 +106,66 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
|
||||
protected ManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable<Type> value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(serviceProvider, logger, scope)
|
||||
{
|
||||
_instanceTypes = value.ToList();
|
||||
}
|
||||
{
|
||||
_instanceTypes = value.ToList();
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use ctor specifying IServiceProvider instead")]
|
||||
protected ManyObjectsResolverBase(IEnumerable<Type> value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, value, scope)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects,
|
||||
/// with creation of objects based on an HttpRequest lifetime scope.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
|
||||
/// <param name="value">The list of object types.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects,
|
||||
/// with creation of objects based on an HttpRequest lifetime scope.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
|
||||
/// <param name="value">The list of object types.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
|
||||
[Obsolete("Use ctor specifying IServiceProvider instead")]
|
||||
protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Type> value)
|
||||
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext)
|
||||
{
|
||||
_instanceTypes = value.ToList();
|
||||
}
|
||||
#endregion
|
||||
{
|
||||
_instanceTypes = value.ToList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void InitializeAppInstances()
|
||||
{
|
||||
_applicationInstances = new Lazy<IEnumerable<TResolved>>(() => CreateInstances().ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
|
||||
/// </summary>
|
||||
/// <remarks>This is false by default and is used for some special internal resolvers.</remarks>
|
||||
internal bool CanResolveBeforeFrozen { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
|
||||
/// </summary>
|
||||
/// <remarks>This is false by default and is used for some special internal resolvers.</remarks>
|
||||
internal bool CanResolveBeforeFrozen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of types to create instances from.
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<Type> InstanceTypes
|
||||
{
|
||||
get { return _instanceTypes; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the list of types to create instances from.
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<Type> InstanceTypes
|
||||
{
|
||||
get { return _instanceTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="HttpContextBase"/> used to initialize this object, if any.
|
||||
/// </summary>
|
||||
/// <remarks>If not null, then <c>LifetimeScope</c> will be <c>ObjectLifetimeScope.HttpRequest</c>.</remarks>
|
||||
protected HttpContextBase CurrentHttpContext { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="HttpContextBase"/> used to initialize this object, if any.
|
||||
/// </summary>
|
||||
/// <remarks>If not null, then <c>LifetimeScope</c> will be <c>ObjectLifetimeScope.HttpRequest</c>.</remarks>
|
||||
protected HttpContextBase CurrentHttpContext
|
||||
{
|
||||
get
|
||||
{
|
||||
var context = _httpContextGetter == null ? null : _httpContextGetter();
|
||||
if (context == null)
|
||||
throw new InvalidOperationException("Cannot use this resolver with lifetime 'HttpRequest' when there is no current HttpContext. Either use the ctor accepting an HttpContextBase, or use the resolver from within a request exclusively.");
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the service provider used to instantiate objects
|
||||
@@ -174,16 +179,16 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
protected ObjectLifetimeScope LifetimeScope { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resolved object instances, sorted by weight.
|
||||
/// </summary>
|
||||
/// <returns>The sorted resolved object instances.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The order is based upon the <c>WeightedPluginAttribute</c> and <c>DefaultPluginWeight</c>.</para>
|
||||
/// <para>Weights are sorted ascendingly (lowest weights come first).</para>
|
||||
/// </remarks>
|
||||
protected IEnumerable<TResolved> GetSortedValues()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the resolved object instances, sorted by weight.
|
||||
/// </summary>
|
||||
/// <returns>The sorted resolved object instances.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The order is based upon the <c>WeightAttribute</c> and <c>DefaultPluginWeight</c>.</para>
|
||||
/// <para>Weights are sorted ascendingly (lowest weights come first).</para>
|
||||
/// </remarks>
|
||||
protected IEnumerable<TResolved> GetSortedValues()
|
||||
{
|
||||
if (_sortedValues == null)
|
||||
{
|
||||
var values = Values.ToList();
|
||||
@@ -191,18 +196,18 @@ namespace Umbraco.Core.ObjectResolution
|
||||
_sortedValues = values;
|
||||
}
|
||||
return _sortedValues;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default type weight.
|
||||
/// </summary>
|
||||
/// <remarks>Determines the weight of types that do not have a <c>WeightedPluginAttribute</c> set on
|
||||
/// them, when calling <c>GetSortedValues</c>.</remarks>
|
||||
protected virtual int DefaultPluginWeight
|
||||
{
|
||||
get { return _defaultPluginWeight; }
|
||||
set { _defaultPluginWeight = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the default type weight.
|
||||
/// </summary>
|
||||
/// <remarks>Determines the weight of types that do not have a <c>WeightAttribute</c> set on
|
||||
/// them, when calling <c>GetSortedValues</c>.</remarks>
|
||||
protected virtual int DefaultPluginWeight
|
||||
{
|
||||
get { return _defaultPluginWeight; }
|
||||
set { _defaultPluginWeight = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the weight of an object for user with GetSortedValues
|
||||
@@ -210,22 +215,22 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <param name="o"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual int GetObjectWeight(object o)
|
||||
{
|
||||
var type = o.GetType();
|
||||
var attr = type.GetCustomAttribute<WeightedPluginAttribute>(true);
|
||||
return attr == null ? DefaultPluginWeight : attr.Weight;
|
||||
}
|
||||
{
|
||||
var type = o.GetType();
|
||||
var attr = type.GetCustomAttribute<WeightAttribute>(true);
|
||||
return attr == null ? DefaultPluginWeight : attr.Weight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resolved object instances.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"><c>CanResolveBeforeFrozen</c> is false, and resolution is not frozen.</exception>
|
||||
protected IEnumerable<TResolved> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
using (Resolution.Reader(CanResolveBeforeFrozen))
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the resolved object instances.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"><c>CanResolveBeforeFrozen</c> is false, and resolution is not frozen.</exception>
|
||||
protected IEnumerable<TResolved> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
using (Resolution.Reader(CanResolveBeforeFrozen))
|
||||
{
|
||||
// note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that
|
||||
// comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the
|
||||
// instances have actually been instanciated when we return.
|
||||
@@ -247,7 +252,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
CurrentHttpContext.Items[_httpContextKey] = instances;
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
|
||||
|
||||
case ObjectLifetimeScope.Application:
|
||||
|
||||
return _applicationInstances.Value;
|
||||
@@ -258,132 +263,132 @@ namespace Umbraco.Core.ObjectResolution
|
||||
return CreateInstances().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the object instances for the types contained in the types collection.
|
||||
/// </summary>
|
||||
/// <returns>A list of objects of type <typeparamref name="TResolved"/>.</returns>
|
||||
protected virtual IEnumerable<TResolved> CreateInstances()
|
||||
{
|
||||
return ServiceProvider.CreateInstances<TResolved>(InstanceTypes, Logger);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the object instances for the types contained in the types collection.
|
||||
/// </summary>
|
||||
/// <returns>A list of objects of type <typeparamref name="TResolved"/>.</returns>
|
||||
protected virtual IEnumerable<TResolved> CreateInstances()
|
||||
{
|
||||
return ServiceProvider.CreateInstances<TResolved>(InstanceTypes, Logger);
|
||||
}
|
||||
|
||||
#region Types collection manipulation
|
||||
#region Types collection manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to remove.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public virtual void RemoveType(Type value)
|
||||
{
|
||||
EnsureSupportsRemove();
|
||||
/// <summary>
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to remove.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public virtual void RemoveType(Type value)
|
||||
{
|
||||
EnsureSupportsRemove();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Remove(value);
|
||||
}
|
||||
}
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to remove.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public void RemoveType<T>()
|
||||
/// <summary>
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to remove.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public void RemoveType<T>()
|
||||
where T : TResolved
|
||||
{
|
||||
RemoveType(typeof(T));
|
||||
}
|
||||
{
|
||||
RemoveType(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds types.
|
||||
/// </summary>
|
||||
/// <param name="types">The types to add.</param>
|
||||
/// <remarks>The types are appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// a type is not a valid type for the resolver, or a type is already in the collection of types.</exception>
|
||||
protected void AddTypes(IEnumerable<Type> types)
|
||||
{
|
||||
EnsureSupportsAdd();
|
||||
/// <summary>
|
||||
/// Adds types.
|
||||
/// </summary>
|
||||
/// <param name="types">The types to add.</param>
|
||||
/// <remarks>The types are appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// a type is not a valid type for the resolver, or a type is already in the collection of types.</exception>
|
||||
protected void AddTypes(IEnumerable<Type> types)
|
||||
{
|
||||
EnsureSupportsAdd();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
foreach(var t in types)
|
||||
{
|
||||
EnsureCorrectType(t);
|
||||
using (Resolution.Configuration)
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
foreach (var t in types)
|
||||
{
|
||||
EnsureCorrectType(t);
|
||||
if (_instanceTypes.Contains(t))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", t.FullName));
|
||||
}
|
||||
_instanceTypes.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", t.FullName));
|
||||
}
|
||||
_instanceTypes.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to add.</param>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public virtual void AddType(Type value)
|
||||
{
|
||||
EnsureSupportsAdd();
|
||||
/// <summary>
|
||||
/// Adds a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to add.</param>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public virtual void AddType(Type value)
|
||||
{
|
||||
EnsureSupportsAdd();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
if (_instanceTypes.Contains(value))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Add(value);
|
||||
}
|
||||
}
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to add.</typeparam>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public void AddType<T>()
|
||||
/// <summary>
|
||||
/// Adds a type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to add.</typeparam>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public void AddType<T>()
|
||||
where T : TResolved
|
||||
{
|
||||
AddType(typeof(T));
|
||||
}
|
||||
{
|
||||
AddType(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the list of types
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support clearing types.</exception>
|
||||
public virtual void Clear()
|
||||
{
|
||||
EnsureSupportsClear();
|
||||
/// <summary>
|
||||
/// Clears the list of types
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support clearing types.</exception>
|
||||
public virtual void Clear()
|
||||
{
|
||||
EnsureSupportsClear();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
_instanceTypes.Clear();
|
||||
}
|
||||
}
|
||||
using (Resolution.Configuration)
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
_instanceTypes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances
|
||||
@@ -399,38 +404,38 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the type should be inserted.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
|
||||
public virtual void InsertType(int index, Type value)
|
||||
{
|
||||
EnsureSupportsInsert();
|
||||
/// <summary>
|
||||
/// Inserts a type at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the type should be inserted.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
|
||||
public virtual void InsertType(int index, Type value)
|
||||
{
|
||||
EnsureSupportsInsert();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
if (_instanceTypes.Contains(value))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Insert(index, value);
|
||||
}
|
||||
}
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Insert(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type at the beginning of the list.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public virtual void InsertType(Type value)
|
||||
{
|
||||
@@ -445,9 +450,9 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
|
||||
public void InsertType<T>(int index)
|
||||
where T : TResolved
|
||||
{
|
||||
InsertType(index, typeof(T));
|
||||
}
|
||||
{
|
||||
InsertType(index, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type at the beginning of the list.
|
||||
@@ -458,68 +463,68 @@ namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
InsertType(0, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type before a specified, already existing type.
|
||||
/// </summary>
|
||||
/// <param name="existingType">The existing type before which to insert.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public virtual void InsertTypeBefore(Type existingType, Type value)
|
||||
{
|
||||
EnsureSupportsInsert();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(existingType);
|
||||
EnsureCorrectType(value);
|
||||
/// <summary>
|
||||
/// Inserts a type before a specified, already existing type.
|
||||
/// </summary>
|
||||
/// <param name="existingType">The existing type before which to insert.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public virtual void InsertTypeBefore(Type existingType, Type value)
|
||||
{
|
||||
EnsureSupportsInsert();
|
||||
|
||||
using (Resolution.Configuration)
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
EnsureCorrectType(existingType);
|
||||
EnsureCorrectType(value);
|
||||
if (_instanceTypes.Contains(existingType) == false)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is not in the collection of types.", existingType.FullName));
|
||||
}
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is not in the collection of types.", existingType.FullName));
|
||||
}
|
||||
if (_instanceTypes.Contains(value))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(
|
||||
"Type {0} is already in the collection of types.", value.FullName));
|
||||
}
|
||||
int index = _instanceTypes.IndexOf(existingType);
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Insert(index, value);
|
||||
}
|
||||
}
|
||||
l.UpgradeToWriteLock();
|
||||
_instanceTypes.Insert(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type before a specified, already existing type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TExisting">The existing type before which to insert.</typeparam>
|
||||
/// <typeparam name="T">The type to insert.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public void InsertTypeBefore<TExisting, T>()
|
||||
/// <summary>
|
||||
/// Inserts a type before a specified, already existing type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TExisting">The existing type before which to insert.</typeparam>
|
||||
/// <typeparam name="T">The type to insert.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public void InsertTypeBefore<TExisting, T>()
|
||||
where TExisting : TResolved
|
||||
where T : TResolved
|
||||
{
|
||||
InsertTypeBefore(typeof(TExisting), typeof(T));
|
||||
}
|
||||
{
|
||||
InsertTypeBefore(typeof(TExisting), typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified type is already in the collection of types.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to look for.</param>
|
||||
/// <returns>A value indicating whether the type is already in the collection of types.</returns>
|
||||
public virtual bool ContainsType(Type value)
|
||||
{
|
||||
using (new ReadLock(_lock))
|
||||
{
|
||||
return _instanceTypes.Contains(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified type is already in the collection of types.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to look for.</param>
|
||||
/// <returns>A value indicating whether the type is already in the collection of types.</returns>
|
||||
public virtual bool ContainsType(Type value)
|
||||
{
|
||||
using (new ReadLock(_lock))
|
||||
{
|
||||
return _instanceTypes.Contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types in the collection of types.
|
||||
@@ -536,27 +541,27 @@ namespace Umbraco.Core.ObjectResolution
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified type is already in the collection of types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to look for.</typeparam>
|
||||
/// <returns>A value indicating whether the type is already in the collection of types.</returns>
|
||||
public bool ContainsType<T>()
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified type is already in the collection of types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to look for.</typeparam>
|
||||
/// <returns>A value indicating whether the type is already in the collection of types.</returns>
|
||||
public bool ContainsType<T>()
|
||||
where T : TResolved
|
||||
{
|
||||
return ContainsType(typeof(T));
|
||||
}
|
||||
{
|
||||
return ContainsType(typeof(T));
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a WriteLock to use when modifying collections
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected WriteLock GetWriteLock()
|
||||
{
|
||||
return new WriteLock(_lock);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a WriteLock to use when modifying collections
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected WriteLock GetWriteLock()
|
||||
{
|
||||
return new WriteLock(_lock);
|
||||
}
|
||||
|
||||
#region Type utilities
|
||||
|
||||
@@ -581,70 +586,71 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">The resolver does not support removing types.</exception>
|
||||
protected void EnsureSupportsRemove()
|
||||
{
|
||||
if (SupportsRemove == false)
|
||||
{
|
||||
if (SupportsRemove == false)
|
||||
throw new InvalidOperationException("This resolver does not support removing types");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the resolver supports clearing types.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">The resolver does not support clearing types.</exception>
|
||||
protected void EnsureSupportsClear() {
|
||||
if (SupportsClear == false)
|
||||
protected void EnsureSupportsClear()
|
||||
{
|
||||
if (SupportsClear == false)
|
||||
throw new InvalidOperationException("This resolver does not support clearing types");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the resolver supports adding types.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">The resolver does not support adding types.</exception>
|
||||
protected void EnsureSupportsAdd()
|
||||
{
|
||||
if (SupportsAdd == false)
|
||||
{
|
||||
if (SupportsAdd == false)
|
||||
throw new InvalidOperationException("This resolver does not support adding new types");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the resolver supports inserting types.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">The resolver does not support inserting types.</exception>
|
||||
protected void EnsureSupportsInsert()
|
||||
{
|
||||
if (SupportsInsert == false)
|
||||
{
|
||||
if (SupportsInsert == false)
|
||||
throw new InvalidOperationException("This resolver does not support inserting new types");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolver supports adding types.
|
||||
/// </summary>
|
||||
protected virtual bool SupportsAdd
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolver supports inserting types.
|
||||
/// </summary>
|
||||
protected virtual bool SupportsInsert
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolver supports clearing types.
|
||||
/// </summary>
|
||||
protected virtual bool SupportsClear
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolver supports removing types.
|
||||
/// </summary>
|
||||
protected virtual bool SupportsRemove
|
||||
{
|
||||
get { return true; }
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
+24
-24
@@ -1,25 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the relative weight of a resolved object type.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
internal class WeightedPluginAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeightedPluginAttribute"/> class with a weight.
|
||||
/// </summary>
|
||||
/// <param name="weight">The object type weight.</param>
|
||||
public WeightedPluginAttribute(int weight)
|
||||
{
|
||||
Weight = weight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight of the object type.
|
||||
/// </summary>
|
||||
public int Weight { get; private set; }
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the relative weight of a resolved object type.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public class WeightAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeightAttribute"/> class with a weight.
|
||||
/// </summary>
|
||||
/// <param name="weight">The object type weight.</param>
|
||||
public WeightAttribute(int weight)
|
||||
{
|
||||
Weight = weight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight of the object type.
|
||||
/// </summary>
|
||||
public int Weight { get; private set; }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
{
|
||||
internal static class DefinitionFactory
|
||||
{
|
||||
public static TableDefinition GetTableDefinition(Type modelType)
|
||||
public static TableDefinition GetTableDefinition(ISqlSyntaxProvider syntaxProvider, Type modelType)
|
||||
{
|
||||
//Looks for PetaPoco's TableNameAtribute for the name of the table
|
||||
//If no attribute is set we use the name of the Type as the default convention
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
//Otherwise use the name of the property itself as the default convention
|
||||
var columnAttribute = propertyInfo.FirstAttribute<ColumnAttribute>();
|
||||
string columnName = columnAttribute != null ? columnAttribute.Name : propertyInfo.Name;
|
||||
var columnDefinition = GetColumnDefinition(modelType, propertyInfo, columnName, tableName);
|
||||
var columnDefinition = GetColumnDefinition(syntaxProvider, modelType, propertyInfo, columnName, tableName);
|
||||
tableDefinition.Columns.Add(columnDefinition);
|
||||
|
||||
//Creates a foreignkey definition and adds it to the collection on the table definition
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
return tableDefinition;
|
||||
}
|
||||
|
||||
public static ColumnDefinition GetColumnDefinition(Type modelType, PropertyInfo propertyInfo, string columnName, string tableName)
|
||||
public static ColumnDefinition GetColumnDefinition(ISqlSyntaxProvider syntaxProvider, Type modelType, PropertyInfo propertyInfo, string columnName, string tableName)
|
||||
{
|
||||
var definition = new ColumnDefinition{ Name = columnName, TableName = tableName, ModificationType = ModificationType.Create };
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
|
||||
{
|
||||
//Special case for MySQL as it can't have multiple default DateTime values, which
|
||||
//is what the umbracoServer table definition is trying to create
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider && definition.TableName == "umbracoServer" &&
|
||||
if (syntaxProvider is MySqlSyntaxProvider && definition.TableName == "umbracoServer" &&
|
||||
definition.TableName.ToLowerInvariant() == "lastNotifiedDate".ToLowerInvariant())
|
||||
return definition;
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public void CreateTable(bool overwrite, Type modelType)
|
||||
{
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(modelType);
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(_syntaxProvider, modelType);
|
||||
var tableName = tableDefinition.Name;
|
||||
|
||||
string createSql = _syntaxProvider.Format(tableDefinition);
|
||||
|
||||
@@ -4,9 +4,11 @@ using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
@@ -69,6 +71,38 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetConnStringExSecurityInfo(this IDbConnection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connection is SqlConnection)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connection.ConnectionString);
|
||||
return string.Format("DataSource: {0}, InitialCatalog: {1}", builder.DataSource, builder.InitialCatalog);
|
||||
}
|
||||
|
||||
if (connection is SqlCeConnection)
|
||||
{
|
||||
var builder = new SqlCeConnectionStringBuilder(connection.ConnectionString);
|
||||
return string.Format("DataSource: {0}", builder.DataSource);
|
||||
}
|
||||
|
||||
if (connection is MySqlConnection)
|
||||
{
|
||||
var builder = new MySqlConnectionStringBuilder(connection.ConnectionString);
|
||||
return string.Format("Server: {0}, Database: {1}", builder.Server, builder.Database);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WarnWithException(typeof(DbConnectionExtensions),
|
||||
"Could not resolve connection string parameters", ex);
|
||||
return "(Could not resolve)";
|
||||
}
|
||||
|
||||
throw new ArgumentException(string.Format("The connection type {0} is not supported", connection.GetType()));
|
||||
}
|
||||
|
||||
public static bool IsAvailable(this IDbConnection connection)
|
||||
{
|
||||
try
|
||||
@@ -79,7 +113,8 @@ namespace Umbraco.Core.Persistence
|
||||
catch (DbException exc)
|
||||
{
|
||||
// Don't swallow this error, the exception is super handy for knowing "why" its not available
|
||||
LogHelper.WarnWithException<IDbConnection>("Configured database is reporting as not being available!", exc);
|
||||
LogHelper.WarnWithException(typeof(DbConnectionExtensions),
|
||||
"Configured database is reporting as not being available! {0}", exc, connection.GetConnStringExSecurityInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,28 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IContent,DocumentDto>
|
||||
|
||||
public IContent BuildEntity(DocumentDto dto)
|
||||
/// <summary>
|
||||
/// Builds a IContent item from the dto(s) and content type
|
||||
/// </summary>
|
||||
/// <param name="dto">
|
||||
/// This DTO can contain all of the information to build an IContent item, however in cases where multiple entities are being built,
|
||||
/// a separate <see cref="DocumentPublishedReadOnlyDto"/> publishedDto entity will be supplied in place of the <see cref="DocumentDto"/>'s own
|
||||
/// ResultColumn DocumentPublishedReadOnlyDto
|
||||
/// </param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="publishedDto">
|
||||
/// When querying for multiple content items the main DTO will not contain the ResultColumn DocumentPublishedReadOnlyDto and a separate publishedDto instance will be supplied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static IContent BuildEntity(DocumentDto dto, IContentType contentType, DocumentPublishedReadOnlyDto publishedDto = null)
|
||||
{
|
||||
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType);
|
||||
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
|
||||
|
||||
try
|
||||
{
|
||||
content.DisableChangeTracking();
|
||||
|
||||
content.Id = _id;
|
||||
content.Id = dto.NodeId;
|
||||
content.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
|
||||
content.Name = dto.Text;
|
||||
content.NodeName = dto.ContentVersionDto.ContentDto.NodeDto.Text;
|
||||
@@ -49,11 +62,19 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
content.Published = dto.Published;
|
||||
content.CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate;
|
||||
content.UpdateDate = dto.ContentVersionDto.VersionDate;
|
||||
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?) null;
|
||||
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?) null;
|
||||
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
|
||||
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
|
||||
content.Version = dto.ContentVersionDto.VersionId;
|
||||
|
||||
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
|
||||
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
|
||||
|
||||
//Check if the publishedDto has been supplied, if not the use the dto's own DocumentPublishedReadOnlyDto value
|
||||
content.PublishedVersionGuid = publishedDto == null
|
||||
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
|
||||
: publishedDto.VersionId;
|
||||
content.PublishedDate = publishedDto == null
|
||||
? (dto.DocumentPublishedReadOnlyDto == null ? default(DateTime) : dto.DocumentPublishedReadOnlyDto.VersionDate)
|
||||
: publishedDto.VersionDate;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
@@ -64,6 +85,13 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
content.EnableChangeTracking();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
|
||||
public IContent BuildEntity(DocumentDto dto)
|
||||
{
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public DocumentDto BuildDto(IContent entity)
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
else if (entity is IMemberType)
|
||||
nodeObjectType = Constants.ObjectTypes.MemberTypeGuid;
|
||||
else
|
||||
throw new Exception("oops: invalid entity.");
|
||||
throw new Exception("Invalid entity.");
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
{
|
||||
|
||||
@@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
|
||||
|
||||
public IMedia BuildEntity(ContentVersionDto dto)
|
||||
public static IMedia BuildEntity(ContentVersionDto dto, IMediaType contentType)
|
||||
{
|
||||
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType);
|
||||
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, contentType);
|
||||
|
||||
try
|
||||
{
|
||||
media.DisableChangeTracking();
|
||||
|
||||
media.Id = _id;
|
||||
media.Id = dto.NodeId;
|
||||
media.Key = dto.ContentDto.NodeDto.UniqueId;
|
||||
media.Path = dto.ContentDto.NodeDto.Path;
|
||||
media.CreatorId = dto.ContentDto.NodeDto.UserId.Value;
|
||||
@@ -55,6 +55,13 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
media.EnableChangeTracking();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
|
||||
public IMedia BuildEntity(ContentVersionDto dto)
|
||||
{
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public ContentVersionDto BuildDto(IMedia entity)
|
||||
|
||||
@@ -28,17 +28,17 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
|
||||
|
||||
public IMember BuildEntity(MemberDto dto)
|
||||
public static IMember BuildEntity(MemberDto dto, IMemberType contentType)
|
||||
{
|
||||
var member = new Member(
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.Text,
|
||||
dto.Email, dto.LoginName, dto.Password, _contentType);
|
||||
dto.Email, dto.LoginName, dto.Password, contentType);
|
||||
|
||||
try
|
||||
{
|
||||
member.DisableChangeTracking();
|
||||
|
||||
member.Id = _id;
|
||||
member.Id = dto.NodeId;
|
||||
member.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
|
||||
member.Path = dto.ContentVersionDto.ContentDto.NodeDto.Path;
|
||||
member.CreatorId = dto.ContentVersionDto.ContentDto.NodeDto.UserId.Value;
|
||||
@@ -62,6 +62,12 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
|
||||
public IMember BuildEntity(MemberDto dto)
|
||||
{
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public MemberDto BuildDto(IMember entity)
|
||||
{
|
||||
var dto = new MemberDto
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
// note: no idea why Id is nullable here, but better check
|
||||
if (groupDto.Id.HasValue == false)
|
||||
throw new Exception("oops: groupDto.Id has no value.");
|
||||
throw new Exception("GroupDto.Id has no value.");
|
||||
group.Id = groupDto.Id.Value;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
_updateDate = updateDate;
|
||||
}
|
||||
|
||||
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
|
||||
public static IEnumerable<Property> BuildEntity(IReadOnlyCollection<PropertyDataDto> dtos, PropertyType[] compositionTypeProperties, DateTime createDate, DateTime updateDate)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
|
||||
foreach (var propertyType in _compositionTypeProperties)
|
||||
foreach (var propertyType in compositionTypeProperties)
|
||||
{
|
||||
var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id);
|
||||
var property = propertyDataDto == null
|
||||
@@ -47,8 +47,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
property.DisableChangeTracking();
|
||||
|
||||
property.CreateDate = _createDate;
|
||||
property.UpdateDate = _updateDate;
|
||||
property.CreateDate = createDate;
|
||||
property.UpdateDate = updateDate;
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
property.ResetDirtyProperties(false);
|
||||
properties.Add(property);
|
||||
@@ -57,12 +57,18 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
property.EnableChangeTracking();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
[Obsolete("Use the static method instead, there's no reason to allocate one of these classes everytime we want to map values")]
|
||||
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
|
||||
{
|
||||
return BuildEntity(dtos, _compositionTypeProperties, _createDate, _updateDate);
|
||||
}
|
||||
|
||||
public IEnumerable<PropertyDataDto> BuildDto(IEnumerable<Property> properties)
|
||||
{
|
||||
var propertyDataDtos = new List<PropertyDataDto>();
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Reflection;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
//figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data
|
||||
foreach (var k in originalEntityProperties.Keys
|
||||
.Select(x => new { orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase) })
|
||||
.Select(x => new { orig = x, title = x.ToCleanString(CleanStringType.PascalCase | CleanStringType.Ascii | CleanStringType.ConvertCase) })
|
||||
.Where(x => entityProps.InvariantContains(x.title) == false))
|
||||
{
|
||||
entity.AdditionalData[k.title] = originalEntityProperties[k.orig];
|
||||
@@ -75,65 +76,6 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
entity.EnableChangeTracking();
|
||||
}
|
||||
}
|
||||
|
||||
public UmbracoEntity BuildEntity(EntityRepository.UmbracoEntityDto dto)
|
||||
{
|
||||
var entity = new UmbracoEntity(dto.Trashed)
|
||||
{
|
||||
CreateDate = dto.CreateDate,
|
||||
CreatorId = dto.UserId.Value,
|
||||
Id = dto.NodeId,
|
||||
Key = dto.UniqueId,
|
||||
Level = dto.Level,
|
||||
Name = dto.Text,
|
||||
NodeObjectTypeId = dto.NodeObjectType.Value,
|
||||
ParentId = dto.ParentId,
|
||||
Path = dto.Path,
|
||||
SortOrder = dto.SortOrder,
|
||||
HasChildren = dto.Children > 0,
|
||||
ContentTypeAlias = dto.Alias ?? string.Empty,
|
||||
ContentTypeIcon = dto.Icon ?? string.Empty,
|
||||
ContentTypeThumbnail = dto.Thumbnail ?? string.Empty,
|
||||
};
|
||||
|
||||
entity.IsPublished = dto.PublishedVersion != default(Guid) || (dto.NewestVersion != default(Guid) && dto.PublishedVersion == dto.NewestVersion);
|
||||
entity.IsDraft = dto.NewestVersion != default(Guid) && (dto.PublishedVersion == default(Guid) || dto.PublishedVersion != dto.NewestVersion);
|
||||
entity.HasPendingChanges = (dto.PublishedVersion != default(Guid) && dto.NewestVersion != default(Guid)) && dto.PublishedVersion != dto.NewestVersion;
|
||||
|
||||
if (dto.UmbracoPropertyDtos != null)
|
||||
{
|
||||
foreach (var propertyDto in dto.UmbracoPropertyDtos)
|
||||
{
|
||||
entity.AdditionalData[propertyDto.PropertyAlias] = new UmbracoEntity.EntityProperty
|
||||
{
|
||||
PropertyEditorAlias = propertyDto.PropertyEditorAlias,
|
||||
Value = propertyDto.NTextValue.IsNullOrWhiteSpace()
|
||||
? propertyDto.NVarcharValue
|
||||
: propertyDto.NTextValue.ConvertToJsonIfPossible()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public EntityRepository.UmbracoEntityDto BuildDto(UmbracoEntity entity)
|
||||
{
|
||||
var node = new EntityRepository.UmbracoEntityDto
|
||||
{
|
||||
CreateDate = entity.CreateDate,
|
||||
Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
|
||||
NodeId = entity.Id,
|
||||
NodeObjectType = entity.NodeObjectTypeId,
|
||||
ParentId = entity.ParentId,
|
||||
Path = entity.Path,
|
||||
SortOrder = entity.SortOrder,
|
||||
Text = entity.Name,
|
||||
Trashed = entity.Trashed,
|
||||
UniqueId = entity.Key,
|
||||
UserId = entity.CreatorId
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,17 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
public abstract class BaseMapper
|
||||
{
|
||||
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
|
||||
protected BaseMapper() : this(SqlSyntaxContext.SqlSyntaxProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected BaseMapper(ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
_sqlSyntax = sqlSyntax;
|
||||
}
|
||||
|
||||
internal abstract ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache { get; }
|
||||
|
||||
internal abstract void BuildMap();
|
||||
@@ -58,8 +68,8 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
string columnName = columnAttribute.Name;
|
||||
|
||||
string columnMap = string.Format("{0}.{1}",
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName),
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(columnName));
|
||||
_sqlSyntax.GetQuotedTableName(tableName),
|
||||
_sqlSyntax.GetQuotedColumnName(columnName));
|
||||
return columnMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -16,6 +17,11 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
public ContentMapper(ISqlSyntaxProvider sqlSyntax) : base(sqlSyntax)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
public ContentMapper()
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
internal BaseMapper ResolveMapperByType(Type type)
|
||||
public virtual BaseMapper ResolveMapperByType(Type type)
|
||||
{
|
||||
return _mapperCache.GetOrAdd(type, type1 =>
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
return byAttribute.Result;
|
||||
}
|
||||
throw new Exception("Invalid Type: A Mapper could not be resolved based on the passed in Type");
|
||||
throw new Exception("Invalid Type: A Mapper could not be resolved based on the passed in Type " + type);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
return Attempt<BaseMapper>.Succeed(mapper);
|
||||
}
|
||||
|
||||
internal string GetMapping(Type type, string propertyName)
|
||||
public virtual string GetMapping(Type type, string propertyName)
|
||||
{
|
||||
var mapper = ResolveMapperByType(type);
|
||||
var result = mapper.Map(propertyName);
|
||||
|
||||
@@ -149,8 +149,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
private void CreateCmsContentTypeData()
|
||||
{
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "icon-folder", IsContainer = false, AllowAtRoot = true });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture", AllowAtRoot = true });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document", AllowAtRoot = true });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "icon-user" });
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
foreach (var item in OrderedTables.OrderBy(x => x.Key))
|
||||
{
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(item.Value);
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(_sqlSyntaxProvider, item.Value);
|
||||
result.TableDefinitions.Add(tableDefinition);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
internal IMigrationContext Context;
|
||||
public IMigrationContext Context;
|
||||
|
||||
public abstract void Up();
|
||||
public abstract void Down();
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive
|
||||
{
|
||||
/// <summary>
|
||||
/// See: http://issues.umbraco.org/issue/U4-4196
|
||||
/// </summary>
|
||||
[Migration("7.5.5", 1, GlobalSettings.UmbracoMigrationName)]
|
||||
public class UpdateAllowedMediaTypesAtRoot : MigrationBase
|
||||
{
|
||||
public UpdateAllowedMediaTypesAtRoot(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
Execute.Sql("UPDATE cmsContentType SET allowAtRoot = 1 WHERE nodeId = 1032 OR nodeId = 1033");
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
{
|
||||
/// <summary>
|
||||
/// See: http://issues.umbraco.org/issue/U4-9188
|
||||
/// </summary>
|
||||
[Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase
|
||||
{
|
||||
public UpdateUniqueIndexOnCmsPropertyData(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
//tuple = tablename, indexname, columnname, unique
|
||||
var indexes = SqlSyntax.GetDefinedIndexes(Context.Database).ToArray();
|
||||
var found = indexes.FirstOrDefault(
|
||||
x => x.Item1.InvariantEquals("cmsPropertyData")
|
||||
&& x.Item2.InvariantEquals("IX_cmsPropertyData"));
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
//drop the index
|
||||
Delete.Index("IX_cmsPropertyData").OnTable("cmsPropertyData");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -739,6 +739,11 @@ namespace Umbraco.Core.Persistence
|
||||
/// <param name="take"></param>
|
||||
internal virtual void BuildSqlDbSpecificPagingQuery(DBType databaseType, long skip, long take, string sql, string sqlSelectRemoved, string sqlOrderBy, ref object[] args, out string sqlPage)
|
||||
{
|
||||
// this is overriden in UmbracoDatabase, and if running SqlServer >=2012, the database type
|
||||
// is switched from SqlServer to SqlServerCE in order to use the better paging syntax that
|
||||
// SqlCE supports, and SqlServer >=2012 too.
|
||||
// so the first case is actually for SqlServer <2012, and second case is CE *and* SqlServer >=2012
|
||||
|
||||
if (databaseType == DBType.SqlServer || databaseType == DBType.Oracle)
|
||||
{
|
||||
sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
|
||||
@@ -746,8 +751,16 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
|
||||
}
|
||||
sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
|
||||
sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
|
||||
|
||||
// split to ensure that peta_rn is the last field to be selected, else Page<int> would fail
|
||||
// the resulting sql is not perfect, NPoco has a much nicer way to do it, but it would require
|
||||
// importing large parts of NPoco
|
||||
var pos = sqlSelectRemoved.IndexOf("FROM");
|
||||
var sqlColumns = sqlSelectRemoved.Substring(0, pos);
|
||||
var sqlFrom = sqlSelectRemoved.Substring(pos);
|
||||
|
||||
sqlPage = string.Format("SELECT * FROM (SELECT {0}, ROW_NUMBER() OVER ({1}) peta_rn {2}) peta_paged WHERE peta_rn>@{3} AND peta_rn<=@{4}",
|
||||
sqlColumns, sqlOrderBy ?? "ORDER BY (SELECT NULL)", sqlFrom, args.Length, args.Length + 1);
|
||||
args = args.Concat(new object[] { skip, skip + take }).ToArray();
|
||||
}
|
||||
else if (databaseType == DBType.SqlServerCE)
|
||||
@@ -774,7 +787,7 @@ namespace Umbraco.Core.Persistence
|
||||
throw new Exception("Unable to parse SQL statement for paged query");
|
||||
if (_dbType == DBType.Oracle && sqlSelectRemoved.StartsWith("*"))
|
||||
throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");
|
||||
|
||||
|
||||
BuildSqlDbSpecificPagingQuery(_dbType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MySql.Data.MySqlClient;
|
||||
using StackExchange.Profiling.Data;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
@@ -56,7 +60,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// Safely inserts a record, or updates if it exists, based on a unique constraint.
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="poco"></param>
|
||||
/// <param name="poco"></param>
|
||||
/// <returns>The action that executed, either an insert or an update. If an insert occurred and a PK value got generated, the poco object
|
||||
/// passed in will contain the updated value.</returns>
|
||||
/// <remarks>
|
||||
@@ -78,7 +82,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="poco"></param>
|
||||
/// <param name="updateArgs"></param>
|
||||
/// <param name="updateArgs"></param>
|
||||
/// <param name="updateCommand">If the entity has a composite key they you need to specify the update command explicitly</param>
|
||||
/// <returns>The action that executed, either an insert or an update. If an insert occurred and a PK value got generated, the poco object
|
||||
/// passed in will contain the updated value.</returns>
|
||||
@@ -130,7 +134,7 @@ namespace Umbraco.Core.Persistence
|
||||
if (rowCount > 0)
|
||||
return RecordPersistenceType.Update;
|
||||
|
||||
// failed: does not exist (due to race cond RC2), need to insert
|
||||
// failed: does not exist (due to race cond RC2), need to insert
|
||||
// loop
|
||||
}
|
||||
}
|
||||
@@ -150,7 +154,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
//this fancy regex will only match a single @ not a double, etc...
|
||||
var regex = new Regex("(?<!@)@(?!@)");
|
||||
return regex.Replace(value, "@@");
|
||||
return regex.Replace(value, "@@");
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -172,6 +176,13 @@ namespace Umbraco.Core.Persistence
|
||||
creator.CreateTable<T>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
|
||||
/// when finished
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection)
|
||||
{
|
||||
//don't do anything if there are no records.
|
||||
@@ -180,7 +191,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
using (var tr = db.GetTransaction())
|
||||
{
|
||||
db.BulkInsertRecords(collection, tr, true);
|
||||
db.BulkInsertRecords(collection, tr, SqlSyntaxContext.SqlSyntaxProvider, true, true); // use native, commit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,55 +203,98 @@ namespace Umbraco.Core.Persistence
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="tr"></param>
|
||||
/// <param name="syntaxProvider"></param>
|
||||
/// <param name="useNativeSqlPlatformBulkInsert">
|
||||
/// If this is false this will try to just generate bulk insert statements instead of using the current SQL platform's bulk
|
||||
/// insert logic. For SQLCE, bulk insert statements do not work so if this is false it will insert one at a time.
|
||||
/// </param>
|
||||
/// <param name="commitTrans"></param>
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection, Transaction tr, bool commitTrans = false)
|
||||
/// <returns>The number of items inserted</returns>
|
||||
public static int BulkInsertRecords<T>(this Database db,
|
||||
IEnumerable<T> collection,
|
||||
Transaction tr,
|
||||
ISqlSyntaxProvider syntaxProvider,
|
||||
bool useNativeSqlPlatformBulkInsert = true,
|
||||
bool commitTrans = false)
|
||||
{
|
||||
|
||||
//don't do anything if there are no records.
|
||||
if (collection.Any() == false)
|
||||
return;
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var pd = Database.PocoData.ForType(typeof(T));
|
||||
if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
|
||||
|
||||
try
|
||||
{
|
||||
//if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
|
||||
var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
|
||||
int processed = 0;
|
||||
|
||||
if ((sqlServerSyntax != null && (int)sqlServerSyntax.GetVersionName(db) < (int)SqlServerVersionName.V2008)
|
||||
|| SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
var usedNativeSqlPlatformInserts = useNativeSqlPlatformBulkInsert
|
||||
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
|
||||
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
db.Insert(poco);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (usedNativeSqlPlatformInserts == false)
|
||||
{
|
||||
string[] sqlStatements;
|
||||
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
|
||||
for (var i = 0; i < sqlStatements.Length; i++)
|
||||
//if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
|
||||
var sqlServerSyntax = syntaxProvider as SqlServerSyntaxProvider;
|
||||
if ((sqlServerSyntax != null && (int) sqlServerSyntax.GetVersionName(db) < (int) SqlServerVersionName.V2008)
|
||||
|| syntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
using (var cmd = cmds[i])
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
cmd.CommandText = sqlStatements[i];
|
||||
cmd.ExecuteNonQuery();
|
||||
db.Insert(poco);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//we'll need to generate insert statements instead
|
||||
|
||||
string[] sqlStatements;
|
||||
var cmds = db.GenerateBulkInsertCommand(pd, collection, out sqlStatements);
|
||||
for (var i = 0; i < sqlStatements.Length; i++)
|
||||
{
|
||||
using (var cmd = cmds[i])
|
||||
{
|
||||
cmd.CommandText = sqlStatements[i];
|
||||
cmd.ExecuteNonQuery();
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (commitTrans)
|
||||
{
|
||||
tr.Complete();
|
||||
tr.Complete();
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (commitTrans)
|
||||
{
|
||||
tr.Dispose();
|
||||
tr.Dispose();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
|
||||
/// when finished
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="tr"></param>
|
||||
/// <param name="commitTrans"></param>
|
||||
[Obsolete("Use the method that specifies an SqlSyntaxContext instance instead")]
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection, Transaction tr, bool commitTrans = false)
|
||||
{
|
||||
db.BulkInsertRecords<T>(collection, tr, SqlSyntaxContext.SqlSyntaxProvider, commitTrans);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -249,45 +303,36 @@ namespace Umbraco.Core.Persistence
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="connection"></param>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="pd"></param>
|
||||
/// <returns>Sql commands with populated command parameters required to execute the sql statement</returns>
|
||||
/// <remarks>
|
||||
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
|
||||
/// we need to detect that many params and split somehow.
|
||||
/// For some reason the 2100 limit is not actually allowed even though the exception from sql server mentions 2100 as a max, perhaps it is 2099
|
||||
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
|
||||
/// we need to detect that many params and split somehow.
|
||||
/// For some reason the 2100 limit is not actually allowed even though the exception from sql server mentions 2100 as a max, perhaps it is 2099
|
||||
/// that is max. I've reduced it to 2000 anyways.
|
||||
/// </remarks>
|
||||
internal static IDbCommand[] GenerateBulkInsertCommand<T>(
|
||||
this Database db,
|
||||
IEnumerable<T> collection,
|
||||
IDbConnection connection,
|
||||
this Database db,
|
||||
Database.PocoData pd,
|
||||
IEnumerable<T> collection,
|
||||
out string[] sql)
|
||||
{
|
||||
//A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
|
||||
Func<Database.PocoData, KeyValuePair<string, Database.PocoColumn>, bool> includeColumn = (data, column) =>
|
||||
{
|
||||
if (column.Value.ResultColumn) return false;
|
||||
if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
var pd = Database.PocoData.ForType(typeof(T));
|
||||
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
|
||||
|
||||
//get all columns to include and format for sql
|
||||
var cols = string.Join(", ",
|
||||
var cols = string.Join(", ",
|
||||
pd.Columns
|
||||
.Where(c => includeColumn(pd, c))
|
||||
.Where(c => IncludeColumn(pd, c))
|
||||
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
||||
|
||||
var itemArray = collection.ToArray();
|
||||
|
||||
//calculate number of parameters per item
|
||||
var paramsPerItem = pd.Columns.Count(i => includeColumn(pd, i));
|
||||
|
||||
var paramsPerItem = pd.Columns.Count(i => IncludeColumn(pd, i));
|
||||
|
||||
//Example calc:
|
||||
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
|
||||
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
|
||||
// 2100 / 8 = 262.5
|
||||
// Math.Floor(2100 / 8) = 262 items per trans
|
||||
// 4168 / 262 = 15.908... = there will be 16 trans in total
|
||||
@@ -306,14 +351,14 @@ namespace Umbraco.Core.Persistence
|
||||
.Skip(tIndex * (int)itemsPerTrans)
|
||||
.Take((int)itemsPerTrans);
|
||||
|
||||
var cmd = db.CreateCommand(connection, "");
|
||||
var cmd = db.CreateCommand(db.Connection, string.Empty);
|
||||
var pocoValues = new List<string>();
|
||||
var index = 0;
|
||||
foreach (var poco in itemsForTrans)
|
||||
{
|
||||
var values = new List<string>();
|
||||
//get all columns except result cols and not the primary key if it is auto-incremental
|
||||
foreach (var i in pd.Columns.Where(x => includeColumn(pd, x)))
|
||||
foreach (var i in pd.Columns.Where(x => IncludeColumn(pd, x)))
|
||||
{
|
||||
db.AddParam(cmd, i.Value.GetValue(poco), "@");
|
||||
values.Add(string.Format("{0}{1}", "@", index++));
|
||||
@@ -321,14 +366,211 @@ namespace Umbraco.Core.Persistence
|
||||
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
|
||||
}
|
||||
|
||||
var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
||||
var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
||||
sqlQueries.Add(sqlResult);
|
||||
commands.Add(cmd);
|
||||
}
|
||||
|
||||
sql = sqlQueries.ToArray();
|
||||
|
||||
return commands.ToArray();
|
||||
return commands.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IncludeColumn(Database.PocoData data, KeyValuePair<string, Database.PocoColumn> column)
|
||||
{
|
||||
if (column.Value.ResultColumn) return false;
|
||||
if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulk insert records with Sql BulkCopy or TableDirect or whatever sql platform specific bulk insert records should be used
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="syntaxProvider"></param>
|
||||
/// <param name="pd"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="processed">The number of records inserted</param>
|
||||
private static bool NativeSqlPlatformBulkInsertRecords<T>(Database db, ISqlSyntaxProvider syntaxProvider, Database.PocoData pd, IEnumerable<T> collection, out int processed)
|
||||
{
|
||||
|
||||
var dbConnection = db.Connection;
|
||||
|
||||
//unwrap the profiled connection if there is one
|
||||
var profiledConnection = dbConnection as ProfiledDbConnection;
|
||||
if (profiledConnection != null)
|
||||
{
|
||||
dbConnection = profiledConnection.InnerConnection;
|
||||
}
|
||||
|
||||
//check if it's SQL or SqlCe
|
||||
|
||||
var sqlConnection = dbConnection as SqlConnection;
|
||||
if (sqlConnection != null)
|
||||
{
|
||||
processed = BulkInsertRecordsSqlServer(db, (SqlServerSyntaxProvider)syntaxProvider, pd, collection);
|
||||
return true;
|
||||
}
|
||||
|
||||
var sqlCeConnection = dbConnection as SqlCeConnection;
|
||||
if (sqlCeConnection != null)
|
||||
{
|
||||
processed = BulkInsertRecordsSqlCe(db, pd, collection);
|
||||
return true;
|
||||
}
|
||||
|
||||
//could not use the SQL server's specific bulk insert operations
|
||||
processed = 0;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logic used to perform bulk inserts with SqlCe's TableDirect
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="pd"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <returns></returns>
|
||||
internal static int BulkInsertRecordsSqlCe<T>(Database db,
|
||||
Database.PocoData pd,
|
||||
IEnumerable<T> collection)
|
||||
{
|
||||
var cols = pd.Columns.ToArray();
|
||||
|
||||
using (var cmd = db.CreateCommand(db.Connection, string.Empty))
|
||||
{
|
||||
cmd.CommandText = pd.TableInfo.TableName;
|
||||
cmd.CommandType = CommandType.TableDirect;
|
||||
//cmd.Transaction = GetTypedTransaction<SqlCeTransaction>(db.Connection.);
|
||||
|
||||
//get the real command
|
||||
using (var sqlCeCommand = GetTypedCommand<SqlCeCommand>(cmd))
|
||||
{
|
||||
// This seems to cause problems, I think this is primarily used for retrieval, not
|
||||
// inserting. see: https://msdn.microsoft.com/en-us/library/system.data.sqlserverce.sqlcecommand.indexname%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396
|
||||
//sqlCeCommand.IndexName = pd.TableInfo.PrimaryKey;
|
||||
|
||||
var count = 0;
|
||||
using (var rs = sqlCeCommand.ExecuteResultSet(ResultSetOptions.Updatable))
|
||||
{
|
||||
var rec = rs.CreateRecord();
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
for (var i = 0; i < cols.Length; i++)
|
||||
{
|
||||
//skip the index if this shouldn't be included (i.e. PK)
|
||||
if (IncludeColumn(pd, cols[i]))
|
||||
{
|
||||
var val = cols[i].Value.GetValue(item);
|
||||
rec.SetValue(i, val);
|
||||
}
|
||||
}
|
||||
rs.Insert(rec);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logic used to perform bulk inserts with SqlServer's BulkCopy
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="sqlSyntaxProvider"></param>
|
||||
/// <param name="pd"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <returns></returns>
|
||||
internal static int BulkInsertRecordsSqlServer<T>(Database db, SqlServerSyntaxProvider sqlSyntaxProvider,
|
||||
Database.PocoData pd, IEnumerable<T> collection)
|
||||
{
|
||||
//NOTE: We need to use the original db.Connection here to create the command, but we need to pass in the typed
|
||||
// connection below to the SqlBulkCopy
|
||||
using (var cmd = db.CreateCommand(db.Connection, string.Empty))
|
||||
{
|
||||
using (var copy = new SqlBulkCopy(
|
||||
GetTypedConnection<SqlConnection>(db.Connection),
|
||||
SqlBulkCopyOptions.Default,
|
||||
GetTypedTransaction<SqlTransaction>(cmd.Transaction))
|
||||
{
|
||||
BulkCopyTimeout = 10000,
|
||||
DestinationTableName = pd.TableInfo.TableName
|
||||
})
|
||||
{
|
||||
//var cols = pd.Columns.Where(x => IncludeColumn(pd, x)).Select(x => x.Value).ToArray();
|
||||
|
||||
using (var bulkReader = new PocoDataDataReader<T, SqlServerSyntaxProvider>(collection, pd, sqlSyntaxProvider))
|
||||
{
|
||||
copy.WriteToServer(bulkReader);
|
||||
|
||||
return bulkReader.RecordsAffected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
|
||||
/// </summary>
|
||||
/// <typeparam name="TConnection"></typeparam>
|
||||
/// <param name="connection"></param>
|
||||
/// <returns></returns>
|
||||
private static TConnection GetTypedConnection<TConnection>(IDbConnection connection)
|
||||
where TConnection : class, IDbConnection
|
||||
{
|
||||
var profiled = connection as ProfiledDbConnection;
|
||||
if (profiled != null)
|
||||
{
|
||||
return profiled.InnerConnection as TConnection;
|
||||
}
|
||||
return connection as TConnection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
|
||||
/// </summary>
|
||||
/// <typeparam name="TTransaction"></typeparam>
|
||||
/// <param name="connection"></param>
|
||||
/// <returns></returns>
|
||||
private static TTransaction GetTypedTransaction<TTransaction>(IDbTransaction connection)
|
||||
where TTransaction : class, IDbTransaction
|
||||
{
|
||||
var profiled = connection as ProfiledDbTransaction;
|
||||
if (profiled != null)
|
||||
{
|
||||
return profiled.WrappedTransaction as TTransaction;
|
||||
}
|
||||
return connection as TTransaction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommand"></typeparam>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
private static TCommand GetTypedCommand<TCommand>(IDbCommand command)
|
||||
where TCommand : class, IDbCommand
|
||||
{
|
||||
var profiled = command as ProfiledDbCommand;
|
||||
if (profiled != null)
|
||||
{
|
||||
return profiled.InternalCommand as TCommand;
|
||||
}
|
||||
return command as TCommand;
|
||||
}
|
||||
|
||||
[Obsolete("Use the DatabaseSchemaHelper instead")]
|
||||
@@ -405,8 +647,8 @@ namespace Umbraco.Core.Persistence
|
||||
return ApplicationContext.Current.DatabaseContext.DatabaseProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -27,18 +27,42 @@ namespace Umbraco.Core.Persistence
|
||||
return sql.From(sqlSyntax.GetQuotedTableName(tableName));
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionHelper<T>();
|
||||
var expresionist = new PocoToSqlExpressionVisitor<T>();
|
||||
var whereExpression = expresionist.Visit(predicate);
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionVisitor<T>(sqlSyntax);
|
||||
var whereExpression = expresionist.Visit(predicate);
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
private static string GetFieldName<T>(Expression<Func<T, object>> fieldSelector, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var field = ExpressionHelper.FindProperty(fieldSelector) as PropertyInfo;
|
||||
var fieldName = field.GetColumnName();
|
||||
|
||||
var type = typeof(T);
|
||||
var tableName = type.GetTableName();
|
||||
|
||||
return sqlSyntax.GetQuotedTableName(tableName) + "." + sqlSyntax.GetQuotedColumnName(fieldName);
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
public static Sql WhereIn<T>(this Sql sql, Expression<Func<T, object>> fieldSelector, IEnumerable values)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionHelper<T>();
|
||||
var fieldExpression = expresionist.Visit(fieldSelector);
|
||||
return sql.Where(fieldExpression + " IN (@values)", new {@values = values});
|
||||
return sql.WhereIn(fieldSelector, values, SqlSyntaxContext.SqlSyntaxProvider);
|
||||
}
|
||||
|
||||
public static Sql WhereIn<T>(this Sql sql, Expression<Func<T, object>> fieldSelector, IEnumerable values, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var fieldName = GetFieldName(fieldSelector, sqlSyntax);
|
||||
return sql.Where(fieldName + " IN (@values)", new { values });
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
@@ -49,17 +73,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
|
||||
var columnName = column.GetColumnName();
|
||||
|
||||
var type = typeof(TColumn);
|
||||
var tableName = type.GetTableName();
|
||||
|
||||
//need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
|
||||
var syntax = string.Format("({0}.{1})",
|
||||
sqlSyntax.GetQuotedTableName(tableName),
|
||||
sqlSyntax.GetQuotedColumnName(columnName));
|
||||
|
||||
var syntax = "(" + GetFieldName(columnMember, sqlSyntax) + ")";
|
||||
return sql.OrderBy(syntax);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
/// <summary>
|
||||
/// A data reader used for reading collections of PocoData entity types
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We are using a custom data reader so that tons of memory is not consumed when rebuilding this table, previously
|
||||
/// we'd generate SQL insert statements, but we'd have to put all of the XML structures into memory first. Alternatively
|
||||
/// we can use .net's DataTable, but this also requires putting everything into memory. By using a DataReader we don't have to
|
||||
/// store every content item and it's XML structure in memory to get it into the DB, we can stream it into the db with this
|
||||
/// reader.
|
||||
/// </remarks>
|
||||
internal class PocoDataDataReader<T, TSyntax> : BulkDataReader
|
||||
where TSyntax : ISqlSyntaxProvider
|
||||
{
|
||||
private readonly MicrosoftSqlSyntaxProviderBase<TSyntax> _sqlSyntaxProvider;
|
||||
private readonly TableDefinition _tableDefinition;
|
||||
private readonly Database.PocoColumn[] _readerColumns;
|
||||
private readonly IEnumerator<T> _enumerator;
|
||||
private readonly ColumnDefinition[] _columnDefinitions;
|
||||
private int _recordsAffected = -1;
|
||||
|
||||
public PocoDataDataReader(
|
||||
IEnumerable<T> dataSource,
|
||||
Database.PocoData pd,
|
||||
MicrosoftSqlSyntaxProviderBase<TSyntax> sqlSyntaxProvider)
|
||||
{
|
||||
if (dataSource == null) throw new ArgumentNullException("dataSource");
|
||||
if (sqlSyntaxProvider == null) throw new ArgumentNullException("sqlSyntaxProvider");
|
||||
|
||||
_tableDefinition = DefinitionFactory.GetTableDefinition(sqlSyntaxProvider, pd.type);
|
||||
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.type);
|
||||
|
||||
_readerColumns = pd.Columns.Select(x => x.Value).ToArray();
|
||||
_sqlSyntaxProvider = sqlSyntaxProvider;
|
||||
_enumerator = dataSource.GetEnumerator();
|
||||
_columnDefinitions = _tableDefinition.Columns.ToArray();
|
||||
|
||||
}
|
||||
|
||||
protected override string SchemaName
|
||||
{
|
||||
get { return _tableDefinition.SchemaName; }
|
||||
}
|
||||
|
||||
protected override string TableName
|
||||
{
|
||||
get { return _tableDefinition.Name; }
|
||||
}
|
||||
|
||||
public override int RecordsAffected
|
||||
{
|
||||
get { return _recordsAffected <= 0 ? -1 : _recordsAffected; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will automatically add the schema rows based on the Poco table definition and the columns passed in
|
||||
/// </summary>
|
||||
protected override void AddSchemaTableRows()
|
||||
{
|
||||
//var colNames = _readerColumns.Select(x => x.ColumnName).ToArray();
|
||||
//foreach (var col in _columnDefinitions.Where(x => colNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
|
||||
foreach (var col in _columnDefinitions)
|
||||
{
|
||||
var sqlDbType = SqlDbType.NVarChar;
|
||||
if (col.HasSpecialDbType)
|
||||
{
|
||||
//get the SqlDbType from the 'special type'
|
||||
switch (col.DbType)
|
||||
{
|
||||
case SpecialDbTypes.NTEXT:
|
||||
sqlDbType = SqlDbType.NText;
|
||||
break;
|
||||
case SpecialDbTypes.NCHAR:
|
||||
sqlDbType = SqlDbType.NChar;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
else if (col.Type.HasValue)
|
||||
{
|
||||
//get the SqlDbType from the DbType
|
||||
sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.Type.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
//get the SqlDbType from the clr type
|
||||
sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.PropertyType);
|
||||
}
|
||||
|
||||
AddSchemaTableRow(
|
||||
col.Name,
|
||||
col.Size > 0 ? (int?)col.Size : null,
|
||||
col.Precision > 0 ? (short?)col.Precision : null,
|
||||
null, col.IsUnique, col.IsIdentity, col.IsNullable, sqlDbType,
|
||||
null, null, null, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value from the column index for the current object
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetValue(int i)
|
||||
{
|
||||
if (_enumerator.Current != null)
|
||||
{
|
||||
return _readerColumns[i].GetValue(_enumerator.Current);
|
||||
//return _columnDefinitions[i]. .GetValue(_enumerator.Current);
|
||||
}
|
||||
|
||||
return null;
|
||||
//TODO: Or throw ?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advance the cursor
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool Read()
|
||||
{
|
||||
var result = _enumerator.MoveNext();
|
||||
if (result)
|
||||
{
|
||||
if (_recordsAffected == -1)
|
||||
{
|
||||
_recordsAffected = 0;
|
||||
}
|
||||
_recordsAffected++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the enumerator is disposed
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_enumerator.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+924
-772
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// SD: This is a horrible hack but unless we break compatibility with anyone who's actually implemented IQuery{T} there's not much we can do.
|
||||
/// The IQuery{T} interface is useless without having a GetWhereClauses method and cannot be used for tests.
|
||||
/// We have to wait till v8 to make this change I suppose.
|
||||
/// </summary>
|
||||
internal static class QueryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns all translated where clauses and their sql parameters
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Tuple<string, object[]>> GetWhereClauses<T>(this IQuery<T> query)
|
||||
{
|
||||
var q = query as Query<T>;
|
||||
if (q == null)
|
||||
{
|
||||
throw new NotSupportedException(typeof(IQuery<T>) + " cannot be cast to " + typeof(Query<T>));
|
||||
}
|
||||
return q.GetWhereClauses();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a query for building Linq translatable SQL queries
|
||||
/// </summary>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper<T>
|
||||
{
|
||||
|
||||
private readonly BaseMapper _mapper;
|
||||
|
||||
public ModelToSqlExpressionHelper()
|
||||
{
|
||||
_mapper = MappingResolver.Current.ResolveMapperByType(typeof(T));
|
||||
}
|
||||
|
||||
protected override string VisitMemberAccess(MemberExpression m)
|
||||
{
|
||||
if (m.Expression != null &&
|
||||
m.Expression.NodeType == ExpressionType.Parameter
|
||||
&& m.Expression.Type == typeof(T))
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
var member = Expression.Convert(m, typeof(object));
|
||||
var lambda = Expression.Lambda<Func<object>>(member);
|
||||
var getter = lambda.Compile();
|
||||
object o = getter();
|
||||
|
||||
SqlParameters.Add(o);
|
||||
return string.Format("@{0}", SqlParameters.Count - 1);
|
||||
|
||||
//return GetQuotedValue(o, o != null ? o.GetType() : null);
|
||||
|
||||
}
|
||||
|
||||
//protected bool IsFieldName(string quotedExp)
|
||||
//{
|
||||
// //Not entirely sure this is reliable, but its better then simply returning true
|
||||
// return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length;
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// An expression tree parser to create SQL statements and SQL parameters based on a strongly typed expression,
|
||||
/// based on Umbraco's business logic models.
|
||||
/// </summary>
|
||||
/// <remarks>This object is stateful and cannot be re-used to parse an expression.</remarks>
|
||||
internal class ModelToSqlExpressionVisitor<T> : ExpressionVisitorBase
|
||||
{
|
||||
private readonly MappingResolver _mappingResolver;
|
||||
private readonly BaseMapper _mapper;
|
||||
|
||||
public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, MappingResolver mappingResolver)
|
||||
: base(sqlSyntax)
|
||||
{
|
||||
_mapper = mappingResolver.ResolveMapperByType(typeof(T));
|
||||
_mappingResolver = mappingResolver;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
|
||||
public ModelToSqlExpressionVisitor()
|
||||
: this(SqlSyntaxContext.SqlSyntaxProvider, MappingResolver.Current)
|
||||
{ }
|
||||
|
||||
protected override string VisitMemberAccess(MemberExpression m)
|
||||
{
|
||||
if (m.Expression != null &&
|
||||
m.Expression.NodeType == ExpressionType.Parameter
|
||||
&& m.Expression.Type == typeof(T))
|
||||
{
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
|
||||
return field;
|
||||
}
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
|
||||
{
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
|
||||
return field;
|
||||
}
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (m.Expression != null
|
||||
&& m.Expression.Type != typeof(T)
|
||||
&& TypeHelper.IsTypeAssignableFrom<IUmbracoEntity>(m.Expression.Type)
|
||||
&& EndsWithConstant(m) == false)
|
||||
{
|
||||
//if this is the case, it means we have a sub expression / nested property access, such as: x.ContentType.Alias == "Test";
|
||||
//and since the sub type (x.ContentType) is not the same as x, we need to resolve a mapper for x.ContentType to get it's mapped SQL column
|
||||
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
var subMapper = _mappingResolver.ResolveMapperByType(m.Expression.Type);
|
||||
if (subMapper == null)
|
||||
throw new NullReferenceException("No mapper found for type " + m.Expression.Type);
|
||||
var field = subMapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException(string.Format("The mapper returned an empty field for the member name: {0} for type: {1}", m.Member.Name, m.Expression.Type));
|
||||
return field;
|
||||
}
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
//TODO: When m.Expression.NodeType == ExpressionType.Constant and it's an expression like: content => aliases.Contains(content.ContentType.Alias);
|
||||
// then an SQL parameter will be added for aliases as an array, however in SqlIn on the subclass it will manually add these SqlParameters anyways,
|
||||
// however the query will still execute because the SQL that is written will only contain the correct indexes of SQL parameters, this would be ignored,
|
||||
// I'm just unsure right now due to time constraints how to make it correct. It won't matter right now and has been working already with this bug but I've
|
||||
// only just discovered what it is actually doing.
|
||||
|
||||
var member = Expression.Convert(m, typeof(object));
|
||||
var lambda = Expression.Lambda<Func<object>>(member);
|
||||
var getter = lambda.Compile();
|
||||
var o = getter();
|
||||
|
||||
SqlParameters.Add(o);
|
||||
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
return string.Format("@{0}", SqlParameters.Count - 1);
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the MemberExpression ends in a Constant value
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
private bool EndsWithConstant(MemberExpression m)
|
||||
{
|
||||
Expression expr = m;
|
||||
|
||||
while (expr is MemberExpression)
|
||||
{
|
||||
var memberExpr = expr as MemberExpression;
|
||||
expr = memberExpr.Expression;
|
||||
}
|
||||
|
||||
var constExpr = expr as ConstantExpression;
|
||||
return constExpr != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper<T>
|
||||
{
|
||||
private readonly Database.PocoData _pd;
|
||||
|
||||
public PocoToSqlExpressionHelper()
|
||||
{
|
||||
_pd = new Database.PocoData(typeof(T));
|
||||
}
|
||||
|
||||
protected override string VisitMemberAccess(MemberExpression m)
|
||||
{
|
||||
if (m.Expression != null &&
|
||||
m.Expression.NodeType == ExpressionType.Parameter
|
||||
&& m.Expression.Type == typeof(T))
|
||||
{
|
||||
string field = GetFieldName(_pd, m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
|
||||
{
|
||||
string field = GetFieldName(_pd, m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
var member = Expression.Convert(m, typeof(object));
|
||||
var lambda = Expression.Lambda<Func<object>>(member);
|
||||
var getter = lambda.Compile();
|
||||
object o = getter();
|
||||
|
||||
SqlParameters.Add(o);
|
||||
return string.Format("@{0}", SqlParameters.Count - 1);
|
||||
|
||||
//return GetQuotedValue(o, o != null ? o.GetType() : null);
|
||||
|
||||
}
|
||||
|
||||
protected virtual string GetFieldName(Database.PocoData pocoData, string name)
|
||||
{
|
||||
var column = pocoData.Columns.FirstOrDefault(x => x.Value.PropertyInfo.Name == name);
|
||||
return string.Format("{0}.{1}",
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(pocoData.TableInfo.TableName),
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(column.Value.ColumnName));
|
||||
}
|
||||
|
||||
//protected bool IsFieldName(string quotedExp)
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// An expression tree parser to create SQL statements and SQL parameters based on a strongly typed expression,
|
||||
/// based on Umbraco's DTOs.
|
||||
/// </summary>
|
||||
/// <remarks>This object is stateful and cannot be re-used to parse an expression.</remarks>
|
||||
internal class PocoToSqlExpressionVisitor<T> : ExpressionVisitorBase
|
||||
{
|
||||
private readonly Database.PocoData _pd;
|
||||
|
||||
|
||||
public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider)
|
||||
: base(syntaxProvider)
|
||||
{
|
||||
_pd = new Database.PocoData(typeof(T));
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
|
||||
public PocoToSqlExpressionVisitor()
|
||||
: this(SqlSyntaxContext.SqlSyntaxProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string VisitMemberAccess(MemberExpression m)
|
||||
{
|
||||
if (m.Expression != null &&
|
||||
m.Expression.NodeType == ExpressionType.Parameter
|
||||
&& m.Expression.Type == typeof(T))
|
||||
{
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
string field = GetFieldName(_pd, m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
|
||||
{
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
string field = GetFieldName(_pd, m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var member = Expression.Convert(m, typeof(object));
|
||||
var lambda = Expression.Lambda<Func<object>>(member);
|
||||
var getter = lambda.Compile();
|
||||
var o = getter();
|
||||
|
||||
SqlParameters.Add(o);
|
||||
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
return string.Format("@{0}", SqlParameters.Count - 1);
|
||||
//already compiled, return
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
protected virtual string GetFieldName(Database.PocoData pocoData, string name)
|
||||
{
|
||||
var column = pocoData.Columns.FirstOrDefault(x => x.Value.PropertyInfo.Name == name);
|
||||
return string.Format("{0}.{1}",
|
||||
SqlSyntax.GetQuotedTableName(pocoData.TableInfo.TableName),
|
||||
SqlSyntax.GetQuotedColumnName(column.Value.ColumnName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
if (predicate != null)
|
||||
{
|
||||
var expressionHelper = new ModelToSqlExpressionHelper<T>();
|
||||
//TODO: This should have an SqlSyntax object passed in, this ctor is relying on a singleton
|
||||
var expressionHelper = new ModelToSqlExpressionVisitor<T>();
|
||||
string whereExpression = expressionHelper.Visit(predicate);
|
||||
|
||||
_wheres.Add(new Tuple<string, object[]>(whereExpression, expressionHelper.GetSqlParameters()));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// SD: This is a horrible hack but unless we break compatibility with anyone who's actually implemented IQuery{T} there's not much we can do.
|
||||
/// The IQuery{T} interface is useless without having a GetWhereClauses method and cannot be used for tests.
|
||||
/// We have to wait till v8 to make this change I suppose.
|
||||
/// </summary>
|
||||
internal static class QueryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns all translated where clauses and their sql parameters
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Tuple<string, object[]>> GetWhereClauses<T>(this IQuery<T> query)
|
||||
{
|
||||
var q = query as Query<T>;
|
||||
if (q == null)
|
||||
{
|
||||
throw new NotSupportedException(typeof(IQuery<T>) + " cannot be cast to " + typeof(Query<T>));
|
||||
}
|
||||
return q.GetWhereClauses();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
@@ -6,8 +7,13 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
/// <summary>
|
||||
/// String extension methods used specifically to translate into SQL
|
||||
/// </summary>
|
||||
internal static class SqlStringExtensions
|
||||
internal static class SqlExpressionExtensions
|
||||
{
|
||||
public static bool SqlIn<T>(this IEnumerable<T> collection, T item)
|
||||
{
|
||||
return collection.Contains(item);
|
||||
}
|
||||
|
||||
public static bool SqlWildcard(this string str, string txt, TextColumnType columnType)
|
||||
{
|
||||
var wildcardmatch = new Regex("^" + Regex.Escape(txt).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user