Compare commits
826 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d230a7b01 | |||
| 18c5348bdf | |||
| c50fea9794 | |||
| 0a53f7636c | |||
| 6e6d552441 | |||
| 358dd7f32b | |||
| 6531d027fc | |||
| d11e787624 | |||
| 055ae0d126 | |||
| 52b1a08912 | |||
| 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 | |||
| 0415e910dc | |||
| 960040e51e | |||
| f48ba93a01 | |||
| 4823235bf5 | |||
| 90f30c3f3c | |||
| 22397ded9b | |||
| de1da4b060 | |||
| b7240f2048 | |||
| 13f7f96101 | |||
| 006ee00527 | |||
| 3e8f85a4b4 | |||
| 8305a1acd3 | |||
| 309081cccf | |||
| 1221c0e416 | |||
| c505cc689b | |||
| 86cc3f69aa | |||
| 3a216c055e | |||
| ee3a4614c3 | |||
| bb9bc73daa | |||
| 5b5e1847eb | |||
| eeb94e1668 | |||
| 14bf4bbd32 | |||
| 0e61d18b9e | |||
| 531fa9f25a | |||
| a44a670f50 | |||
| 81750c41bd | |||
| 672568d1f5 | |||
| 0179540023 | |||
| ed37b84b3b | |||
| 47c8e6854e | |||
| 1def61432e | |||
| e9b2175506 | |||
| a2a4ad3947 | |||
| d95f818e43 | |||
| 64978dbe89 | |||
| 77e9643a09 | |||
| 17316363f1 | |||
| 0e57d558f6 | |||
| f23247cd85 | |||
| 93ee850bcc | |||
| 8ba6cb3abf | |||
| a266a22daf | |||
| c4d36f06f8 | |||
| 78a2a4a55b | |||
| a70f37a53e | |||
| 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 | |||
| 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 | |||
| 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 | |||
| af5455a500 | |||
| be57692a62 | |||
| 460d884109 | |||
| 1a1a3c43ca | |||
| 6df6b87c4c | |||
| 172895f17f | |||
| 8d3add8834 | |||
| ae257550da | |||
| 4653746117 | |||
| 84bd22190b | |||
| 5e58e0d0a5 | |||
| 8b2fa22b90 | |||
| f76fd360f1 | |||
| bc9a6abccf | |||
| 39ebb9545d | |||
| 6572eea915 | |||
| 99ad5d6826 | |||
| 2a1697b0a3 | |||
| 583a09260e | |||
| 344dab65d4 | |||
| 9f5a9f179f | |||
| f658613fad | |||
| 1216f4a49e | |||
| 460e72fc1f | |||
| 908c8837e9 | |||
| 11c720cc2a | |||
| c93b4fa1fe | |||
| d409d17189 | |||
| 38c876bb53 | |||
| 95fb7f97d0 | |||
| 9dfa9e2fb6 | |||
| 6ee8382ff3 | |||
| 69fb5e9932 | |||
| ca9f3bbd5a | |||
| fe7efa4fba | |||
| 1ec2cc430b | |||
| c1a8ea9df3 | |||
| cd2c79e7d5 | |||
| d5d0a958d3 | |||
| 9e9e51cf29 | |||
| 808793edd8 | |||
| 3895a93b61 | |||
| eadb9ba276 | |||
| fc582b0516 | |||
| 432fac7dc9 | |||
| fad69046df | |||
| 6bcadfea44 | |||
| a3c457e50b | |||
| b2a56fa58f | |||
| f97c20253a | |||
| 51f57eb5ce | |||
| 54e82f9ed1 | |||
| 2ff3ce933b | |||
| 1c15daca52 | |||
| de2d4bef13 | |||
| 8c6f56d3f5 | |||
| 2f0b26db47 | |||
| 1fd020a0f2 | |||
| e12c294150 | |||
| 8d1d394ded | |||
| 3274965c34 | |||
| 54fb1c6231 | |||
| d3ff1cbba9 | |||
| f4072bc975 | |||
| 31e0913488 | |||
| f55e673632 | |||
| f1087fce3c | |||
| 5620074790 | |||
| 600d7be2ac | |||
| 596888a8f6 | |||
| 0f0a0a186c | |||
| 200f0bb67e | |||
| af1be7f32f | |||
| d355d2609a | |||
| 06da9f4f7d | |||
| 5bd5bf87ad | |||
| db78e26647 | |||
| a2adfe4798 | |||
| a7b19e1acf | |||
| a92aa6ad9a | |||
| 154e4a19a5 | |||
| 0142bc61bc | |||
| d99a691bdc | |||
| 10422079b6 | |||
| c1da28dd47 | |||
| 2018dab869 | |||
| 5b88f35471 | |||
| 2ee71ecb96 | |||
| 213a25e3fe | |||
| 14b527a9fa | |||
| 2d25c4dad5 | |||
| 8bdf0924de | |||
| 1b85163a7f | |||
| 634dfaaf75 | |||
| 2279e6cc53 | |||
| 82c5b91deb | |||
| 9d2efdfb37 | |||
| 952cfc6d5f | |||
| ff2311d19c | |||
| ef64098f4e | |||
| 611acf8cea | |||
| 910e6b7a64 | |||
| bffd9c340d | |||
| df116555ad | |||
| ca7e9c90b9 | |||
| 65fc23dadc | |||
| 3ac7512a98 | |||
| a553fabce4 | |||
| 2a2453a969 | |||
| f4711a4473 | |||
| 2069c585fb | |||
| 045374993f | |||
| 57923866ed | |||
| bf61956b7f | |||
| 9ad84a7eab | |||
| 4301af108f | |||
| c331ba134a | |||
| 7d4e547b8d | |||
| cbd20cb9b3 | |||
| 3170d6829a | |||
| 55752abdcf | |||
| 69c952ddda | |||
| 4ff728b62a | |||
| 1b9a3e8ebc | |||
| fee4a84c66 | |||
| ad2a644bdf | |||
| f4b0700528 | |||
| 126c358546 | |||
| f316393955 | |||
| 80d579891b | |||
| bfc2e6f666 | |||
| c3035f26a7 | |||
| b2e03dcc1b | |||
| 97a765d6af | |||
| 67430392e3 | |||
| 6560f752d6 | |||
| e02575830f | |||
| a838d20c3e | |||
| 081284bc62 | |||
| 9e9c1ad030 | |||
| 32d48b12d1 | |||
| b4b91f0fef | |||
| cbf67a4399 | |||
| f58e0dc08e | |||
| e449c237be | |||
| 1dae6724cc | |||
| a28c52aa16 | |||
| 26708ae82b | |||
| 809970bc2e | |||
| 27d041c055 | |||
| 7bf3d04a25 | |||
| 87934e52dc | |||
| 4022c3cec9 | |||
| 816107a348 | |||
| aedba40b54 | |||
| 31b17e3048 | |||
| 42666403a6 | |||
| aa66fd6e33 | |||
| 19325ac522 | |||
| b8b30bdc6e | |||
| 69b6044bc6 | |||
| 18a1aa2f6f | |||
| 92f7782ab9 | |||
| 3e932ecf3f | |||
| dd62a917fd | |||
| 958e20356c | |||
| 1f8dc9a424 | |||
| 539637720f | |||
| 6a3f146da1 | |||
| 7fe7f88c98 | |||
| 774f00d4f5 | |||
| aab2c65aaa | |||
| 3dcba7a6b0 | |||
| 3ece30bd8b | |||
| 36a191464d | |||
| 9131b73cbb | |||
| 68b3ea349e | |||
| 2f0ba3ac07 | |||
| 6c271d92f9 | |||
| e1daa79bab | |||
| a136235c72 | |||
| e8e440516f | |||
| b2e7c7a8d4 | |||
| 2c15db5950 | |||
| 7ed240d510 | |||
| a91b8e0acf | |||
| 0be81aa4e5 | |||
| 221edc76cf | |||
| 0280d832b5 | |||
| 875070fed8 | |||
| 9d62b2f5f9 | |||
| 548c7845cb | |||
| 9c7052f0fd | |||
| 177b61bbf5 | |||
| 8038016b55 | |||
| ae0ad2becf | |||
| ba11de51e3 | |||
| ccaa198e25 | |||
| 66c0d9dc12 | |||
| 59c3d6a206 | |||
| 0ab145e4ac | |||
| 423588d018 | |||
| 16afab86f3 | |||
| 43737e78fb | |||
| f6943ceab4 | |||
| 6495077568 | |||
| c61a9ee433 | |||
| b7264b58f9 | |||
| ba5be0e22a | |||
| d8f68f5f9b | |||
| b365a9944f | |||
| 5b2bcdbfac | |||
| abb994cf30 | |||
| 6e480058b9 | |||
| 912a460bd8 | |||
| 52d366bb6f | |||
| 2ddbca8861 | |||
| e66eefbbd7 | |||
| f1d9775a0f | |||
| 1aa2845b4a | |||
| 1c88117498 | |||
| 6237d4a3e5 | |||
| 77cbdcf38f | |||
| 85a3abf643 | |||
| c53a04d658 | |||
| b4d5140d07 | |||
| 11cafa2c4c | |||
| a5f3b50699 | |||
| 00bc454080 | |||
| 67cd4aeb59 | |||
| b4ea03cdf3 | |||
| c8d7a62a47 | |||
| b9fcda8f39 | |||
| e020779a14 | |||
| d611ae85e1 | |||
| 2df8f55032 | |||
| 6788e8954e | |||
| 080da771ff | |||
| 08b3b635b4 | |||
| 8e411069f4 | |||
| 7d0c92937a | |||
| 1ca6674095 | |||
| 3ebb3dc544 | |||
| 1f6caa0164 | |||
| 6adccb66dd | |||
| 6837dd2ce8 | |||
| b686fa74c9 | |||
| 22d12f3abc | |||
| bb4671faf1 | |||
| 57d2901c2e | |||
| 9914ab6c59 | |||
| ccd03cb1f2 | |||
| 89fd31085c | |||
| e53b81b627 | |||
| 385c53936e | |||
| be8a52e7b6 | |||
| 3cdf80a82b | |||
| b631db14c3 | |||
| 5f7859a8c1 | |||
| 1a2f7b8788 | |||
| 39aca137a4 | |||
| 88c6b0450e | |||
| 9ae339f17b | |||
| b1f13cdbb2 | |||
| 1636c16954 | |||
| f7f736fe63 | |||
| ba6736dbb3 | |||
| 29be571afd | |||
| c5b9e6a194 | |||
| 2cad3838f4 | |||
| c955cce28d | |||
| bb376367d6 | |||
| a83b524130 | |||
| dcdd47dff4 | |||
| c6a2d41888 | |||
| d37b65adcf | |||
| 379a0d68f7 | |||
| c206965f88 | |||
| 8fd1743bce | |||
| 4acfd1dcca | |||
| 67234ef115 | |||
| de89f555d4 | |||
| 9f36d2b7c6 | |||
| 2aa27dcf8c | |||
| 43da97332c | |||
| 379661e302 | |||
| 5f3f698ffd | |||
| 4ab200d538 | |||
| 55fad36bad | |||
| 99a7aa53a1 | |||
| d783554476 | |||
| 5444b65eaf | |||
| c4400a20d6 | |||
| 32d59bb2bc | |||
| 6c6c56645d | |||
| be16e84dbb | |||
| a4fec9dd0f | |||
| 3f2ebbda59 | |||
| ed392cd252 | |||
| 0fe24a45ff | |||
| b290e6af8a | |||
| 6e440f0f69 | |||
| 2eaa2b3166 | |||
| a0caab410e | |||
| 05ebc9dfa7 | |||
| f4a2990819 | |||
| dc476dcb02 | |||
| 0603cb3ab1 | |||
| d81a95dbdc | |||
| 576539c774 | |||
| 4786a8037c | |||
| e7f0eda504 | |||
| b9b3de0b01 | |||
| 826b9e9cc3 | |||
| 4b08442652 | |||
| fdd1a89fb3 | |||
| 29e45534c5 | |||
| 889ef47fd2 | |||
| 8d55a55a80 | |||
| 944898d6e5 | |||
| f247e1b66f | |||
| 2e9e8f23e9 | |||
| a099c40d93 | |||
| 952ff74899 | |||
| 3f5729767d | |||
| def690b8c3 | |||
| b168923d5f | |||
| 1fc17e6f46 | |||
| 695c69af7f | |||
| 0607782028 | |||
| 45ff11b884 | |||
| e39f818389 | |||
| e2aee08959 | |||
| 5fab1ca207 | |||
| 02ffaa9ee8 | |||
| 89cd2f50d7 | |||
| c10534a064 | |||
| 3e46105ba8 | |||
| d8eaab2aa4 | |||
| ce897509ba | |||
| 330cd7a409 | |||
| 56492cd1b2 | |||
| 9427979b1c | |||
| 9d47529d20 | |||
| eec651c9d3 | |||
| f5ceab82f1 | |||
| 3921f88e30 |
@@ -141,3 +141,4 @@ build/docs.zip
|
||||
build/ui-docs.zip
|
||||
build/csharp-docs.zip
|
||||
build/msbuild.log
|
||||
.vs/
|
||||
|
||||
@@ -1,40 +1,52 @@
|
||||
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 dev` in `src\Umbraco.Web.UI.Client`.
|
||||
|
||||
If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself.
|
||||
|
||||
## Watch a introduction video ##
|
||||
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 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).
|
||||
|
||||
@@ -35,7 +35,18 @@ test:
|
||||
assemblies: src\Umbraco.Tests\bin\Debug\Umbraco.Tests.dll
|
||||
artifacts:
|
||||
- path: build\UmbracoCms.*
|
||||
name: UmbracoFiles
|
||||
- path: build\msbuild.log
|
||||
name: BuildLog
|
||||
deploy:
|
||||
- provider: AzureBlob
|
||||
storage_account_name: umbraconightlies
|
||||
storage_access_key:
|
||||
secure: bmEMml2SF7QLHULiePa/a01XOeIa2SxJeXuaZ+1R27b+Vb2nNUQVYiPlUyF2cZAFSHI/zO/LekRsVU1rTescGhJjF7SSjKybymI3p+F/OWpwqiu2WfFee1ofXBFx8QHw
|
||||
container: umbraco-750
|
||||
artifact: UmbracoFiles
|
||||
on:
|
||||
branch: dev-v7
|
||||
notifications:
|
||||
- provider: Slack
|
||||
auth_token:
|
||||
|
||||
+42
-11
@@ -1,23 +1,33 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
:: SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
|
||||
git.exe --version
|
||||
if %ERRORLEVEL%==9009 GOTO :trydefaultpath
|
||||
IF %ERRORLEVEL%==9009 GOTO :trydefaultpath
|
||||
GOTO :EOF
|
||||
:: Git is installed, no need to to anything else
|
||||
|
||||
:trydefaultpath
|
||||
path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
|
||||
PATH=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
|
||||
git.exe --version
|
||||
if %ERRORLEVEL%==9009 GOTO :showerror
|
||||
IF %ERRORLEVEL%==9009 GOTO :showerror
|
||||
GOTO :EOF
|
||||
:: Git is installed, no need to to anything else
|
||||
|
||||
:showerror
|
||||
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
|
||||
set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
|
||||
if %install%==y (
|
||||
SET /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
|
||||
IF %install%==y (
|
||||
:: Create a temporary batch file to execute either after elevating to admin or as-is when the user is already admin
|
||||
ECHO @ECHO OFF > "%temp%\ChocoInstallGit.cmd"
|
||||
ECHO SETLOCAL >> "%temp%\ChocoInstallGit.cmd"
|
||||
ECHO ECHO Installing Chocolatey first >> "%temp%\ChocoInstallGit.cmd"
|
||||
ECHO @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" >> "%temp%\ChocoInstallGit.cmd"
|
||||
ECHO SET PATH=%%PATH%%;%%ALLUSERSPROFILE%%\chocolatey\bin >> "%temp%\ChocoInstallGit.cmd"
|
||||
ECHO choco install git -y >> "%temp%\ChocoInstallGit.cmd"
|
||||
|
||||
GOTO :installgit
|
||||
) else (
|
||||
) ELSE (
|
||||
GOTO :cantcontinue
|
||||
)
|
||||
|
||||
@@ -26,7 +36,28 @@ ECHO Can't complete the build without Git being in the path. Please add it to be
|
||||
GOTO :EOF
|
||||
|
||||
:installgit
|
||||
ECHO Installing Chocolatey first
|
||||
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
|
||||
ECHO Installing Git through Chocolatey
|
||||
choco install git
|
||||
pushd %~dp0
|
||||
:: Running prompt elevated
|
||||
|
||||
:: --> Check for permissions
|
||||
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
|
||||
|
||||
:: --> If error flag set, we do not have admin.
|
||||
IF '%errorlevel%' NEQ '0' (
|
||||
GOTO UACPrompt
|
||||
) ELSE ( GOTO gotAdmin )
|
||||
|
||||
:UACPrompt
|
||||
ECHO You're not currently running this with admin privileges, we'll now try to execute the install of Git through Chocolatey after elevating to admin privileges
|
||||
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
|
||||
ECHO UAC.ShellExecute "%temp%\ChocoInstallGit.cmd", "", "", "runas", 1 >> "%temp%\getadmin.vbs"
|
||||
|
||||
"%temp%\getadmin.vbs"
|
||||
EXIT /B
|
||||
|
||||
:gotAdmin
|
||||
IF EXIST "%temp%\getadmin.vbs" ( DEL "%temp%\getadmin.vbs" )
|
||||
pushd "%CD%"
|
||||
CD /D "%~dp0"
|
||||
|
||||
CALL "%temp%\ChocoInstallGit.cmd"
|
||||
@@ -27,14 +27,14 @@
|
||||
<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.0-beta4, 2.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
|
||||
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
|
||||
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 9.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.69-beta, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.4.3, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.6.3, 5.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.7.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 -->
|
||||
@@ -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>
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 9.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.3, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -43,6 +44,7 @@
|
||||
<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="build\**" target="build" />
|
||||
</files>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas xdt:Transform="InsertIfMissing">
|
||||
<area xdt:Transform="InsertIfMissing">developer</area>
|
||||
</areas>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
|
||||
<control showOnce="true" addPanel="true" panelCaption="">
|
||||
views/dashboard/developer/developerdashboardvideos.html
|
||||
@@ -30,16 +36,21 @@
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control>
|
||||
views/dashboard/developer/examinemanagement.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Xml Data Integrity Report" xdt:Transform="InsertIfMissing">
|
||||
</tab>
|
||||
<tab caption="Health Check" xdt:Transform="InsertIfMissing" xdt:Locator="Match(caption)">
|
||||
<control>
|
||||
views/dashboard/developer/xmldataintegrityreport.html
|
||||
views/dashboard/developer/healthcheck.html
|
||||
</control>
|
||||
</tab>
|
||||
</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">
|
||||
@@ -52,19 +63,6 @@
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Get Started" xdt:Transform="Insert">
|
||||
<access>
|
||||
<grant>admin</grant>
|
||||
</access>
|
||||
<control showOnce="true" addPanel="true" panelCaption="">
|
||||
views/dashboard/default/startupdashboardintro.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Get Started" xdt:Transform="Insert">
|
||||
@@ -80,5 +78,6 @@
|
||||
|
||||
<section alias="StartupDashboardSection">
|
||||
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
</dashBoard>
|
||||
@@ -292,6 +292,8 @@
|
||||
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<remove fileExtension=".woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<remove fileExtension=".woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<mimeMap fileExtension=".woff2" mimeType="application/x-font-woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<remove fileExtension=".less" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
<mimeMap fileExtension=".less" mimeType="text/css" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
|
||||
</staticContent>
|
||||
@@ -317,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" />
|
||||
@@ -332,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"/>
|
||||
|
||||
@@ -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" />
|
||||
@@ -55,21 +55,29 @@
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<!--Developer-->
|
||||
<add alias="datatype" application="developer"
|
||||
<add alias="packager" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add initialize="true" sortOrder="0" alias="datatypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco"
|
||||
<add alias="packagerPackages" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
xdt:Transform="Remove" />
|
||||
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, umbraco"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<!-- Before 7.4 this tree had the alias 'dataType', without the 's' on the end, this is here to rename it -->
|
||||
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco"
|
||||
xdt:Locator="Match(application,type)"
|
||||
xdt:Transform="SetAttributes(alias,sortOrder)" />
|
||||
|
||||
<!-- Yes, set the sortOrder again, like above because.. sometimes apparently we already have a dataTypes node and we can't remove more than one.. if they're equal though (same alias,application and sortOrder) it doesn't throw an error -->
|
||||
<add sortOrder="1" alias="dataTypes" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes(sortOrder)" />
|
||||
|
||||
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="packager" title="Packages" type="umbraco.loadPackager, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="packagerPackages" title="Packager Packages" type="umbraco.loadPackages, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" initialize="false" sortOrder="3"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.5.0
|
||||
beta2
|
||||
7.5.7
|
||||
@@ -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.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.0-beta2")]
|
||||
[assembly: AssemblyFileVersion("7.5.7")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.7")]
|
||||
@@ -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,113 +112,65 @@ 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 GetOptionalTextElement("EnableInheritedMediaTypes", true); }
|
||||
}
|
||||
|
||||
string IContentSection.NotificationEmailAddress
|
||||
{
|
||||
get { return Notifications.NotificationEmailAddress; }
|
||||
@@ -431,5 +310,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
get { return EnableInheritedDocumentTypes; }
|
||||
}
|
||||
|
||||
bool IContentSection.EnableInheritedMediaTypes
|
||||
{
|
||||
get { return EnableInheritedMediaTypes; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,5 +61,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
string DefaultDocumentTypeProperty { get; }
|
||||
|
||||
bool EnableInheritedDocumentTypes { get; }
|
||||
|
||||
bool EnableInheritedMediaTypes { get; }
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
bool DisableAlternativeTemplates { get; }
|
||||
|
||||
bool DisableFindContentByIdPath { get; }
|
||||
|
||||
bool DisableRedirectUrlTracking { get; }
|
||||
|
||||
string UrlProviderMode { 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); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return (bool) base["disableFindContentByIdPath"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("disableRedirectUrlTracking", DefaultValue = "false")]
|
||||
public bool DisableRedirectUrlTracking
|
||||
{
|
||||
get { return (bool) base["disableRedirectUrlTracking"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
|
||||
public string UrlProviderMode
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.5.0");
|
||||
private static readonly Version Version = new Version("7.5.7");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "beta2"; } }
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
public static class Examine
|
||||
{
|
||||
/// <summary>
|
||||
/// The alias of the internal member indexer
|
||||
/// </summary>
|
||||
public const string InternalMemberIndexer = "InternalMemberIndexer";
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the internal content indexer
|
||||
/// </summary>
|
||||
public const string InternalIndexer = "InternalIndexer";
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the external content indexer
|
||||
/// </summary>
|
||||
public const string ExternalIndexer = "ExternalIndexer";
|
||||
/// <summary>
|
||||
/// The alias of the internal member searcher
|
||||
/// </summary>
|
||||
@@ -19,6 +27,11 @@ namespace Umbraco.Core
|
||||
/// The alias of the internal content searcher
|
||||
/// </summary>
|
||||
public const string InternalSearcher = "InternalSearcher";
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the external content searcher
|
||||
/// </summary>
|
||||
public const string ExternalSearcher = "ExternalSearcher";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,33 +1,40 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the identifiers for Umbraco system nodes.
|
||||
/// </summary>
|
||||
public static class System
|
||||
{
|
||||
/// <summary>
|
||||
/// The integer identifier for global system root node.
|
||||
/// </summary>
|
||||
public const int Root = -1;
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the identifiers for Umbraco system nodes.
|
||||
/// </summary>
|
||||
public static class System
|
||||
{
|
||||
/// <summary>
|
||||
/// The integer identifier for global system root node.
|
||||
/// </summary>
|
||||
public const int Root = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinContent = -20;
|
||||
/// <summary>
|
||||
/// The integer identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinContent = -20;
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinMedia = -21;
|
||||
/// <summary>
|
||||
/// The integer identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinMedia = -21;
|
||||
|
||||
public const int DefaultContentListViewDataTypeId = -95;
|
||||
public const int DefaultContentListViewDataTypeId = -95;
|
||||
public const int DefaultMediaListViewDataTypeId = -96;
|
||||
public const int DefaultMembersListViewDataTypeId = -97;
|
||||
|
||||
// identifiers for lock objects
|
||||
public const int ServersLock = -331;
|
||||
}
|
||||
}
|
||||
public const int ServersLock = -331;
|
||||
}
|
||||
|
||||
public static class DatabaseProviders
|
||||
{
|
||||
public const string SqlCe = "System.Data.SqlServerCe.4.0";
|
||||
public const string SqlServer = "System.Data.SqlClient";
|
||||
public const string MySql = "MySql.Data.MySqlClient";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -135,7 +156,7 @@ namespace Umbraco.Core
|
||||
if (string.IsNullOrEmpty(_providerName) == false)
|
||||
return _providerName;
|
||||
|
||||
_providerName = "System.Data.SqlClient";
|
||||
_providerName = Constants.DatabaseProviders.SqlServer;
|
||||
if (ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName] != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName) == false)
|
||||
@@ -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);
|
||||
@@ -212,10 +233,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
var provider = DbConnectionExtensions.DetectProviderFromConnectionString(connectionString);
|
||||
var databaseProvider = provider.ToString();
|
||||
var providerName = "System.Data.SqlClient";
|
||||
var providerName = Constants.DatabaseProviders.SqlServer;
|
||||
if (databaseProvider.ToLower().Contains("mysql"))
|
||||
{
|
||||
providerName = "MySql.Data.MySqlClient";
|
||||
providerName = Constants.DatabaseProviders.MySql;
|
||||
}
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
Initialize(string.Empty);
|
||||
@@ -240,10 +261,10 @@ namespace Umbraco.Core
|
||||
|
||||
public string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
|
||||
{
|
||||
providerName = "System.Data.SqlClient";
|
||||
providerName = Constants.DatabaseProviders.SqlServer;
|
||||
if (databaseProvider.ToLower().Contains("mysql"))
|
||||
{
|
||||
providerName = "MySql.Data.MySqlClient";
|
||||
providerName = Constants.DatabaseProviders.MySql;
|
||||
return string.Format("Server={0}; Database={1};Uid={2};Pwd={3}", server, databaseName, user, password);
|
||||
}
|
||||
if (databaseProvider.ToLower().Contains("azure"))
|
||||
@@ -260,7 +281,7 @@ namespace Umbraco.Core
|
||||
/// <param name="databaseName">Name of the database</param>
|
||||
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
|
||||
{
|
||||
const string providerName = "System.Data.SqlClient";
|
||||
const string providerName = Constants.DatabaseProviders.SqlServer;
|
||||
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
|
||||
SaveConnectionString(connectionString, providerName);
|
||||
Initialize(providerName);
|
||||
@@ -373,7 +394,7 @@ namespace Umbraco.Core
|
||||
var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName];
|
||||
if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false && string.IsNullOrWhiteSpace(databaseSettings.ProviderName) == false)
|
||||
{
|
||||
var providerName = "System.Data.SqlClient";
|
||||
var providerName = Constants.DatabaseProviders.SqlServer;
|
||||
string connString = null;
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName))
|
||||
{
|
||||
@@ -381,8 +402,7 @@ namespace Umbraco.Core
|
||||
connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
|
||||
}
|
||||
Initialize(providerName, connString);
|
||||
|
||||
DetermineSqlServerVersion();
|
||||
|
||||
}
|
||||
else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false)
|
||||
{
|
||||
@@ -395,8 +415,8 @@ namespace Umbraco.Core
|
||||
else if (legacyConnString.ToLowerInvariant().Contains("tcp:"))
|
||||
{
|
||||
//Must be sql azure
|
||||
SaveConnectionString(legacyConnString, "System.Data.SqlClient");
|
||||
Initialize("System.Data.SqlClient");
|
||||
SaveConnectionString(legacyConnString, Constants.DatabaseProviders.SqlServer);
|
||||
Initialize(Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
else if (legacyConnString.ToLowerInvariant().Contains("datalayer=mysql"))
|
||||
{
|
||||
@@ -407,20 +427,19 @@ namespace Umbraco.Core
|
||||
foreach (var variable in legacyConnString.Split(';').Where(x => x.ToLowerInvariant().StartsWith("datalayer") == false))
|
||||
connectionStringWithoutDatalayer = string.Format("{0}{1};", connectionStringWithoutDatalayer, variable);
|
||||
|
||||
SaveConnectionString(connectionStringWithoutDatalayer, "MySql.Data.MySqlClient");
|
||||
Initialize("MySql.Data.MySqlClient");
|
||||
SaveConnectionString(connectionStringWithoutDatalayer, Constants.DatabaseProviders.MySql);
|
||||
Initialize(Constants.DatabaseProviders.MySql);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Must be sql
|
||||
SaveConnectionString(legacyConnString, "System.Data.SqlClient");
|
||||
Initialize("System.Data.SqlClient");
|
||||
SaveConnectionString(legacyConnString, Constants.DatabaseProviders.SqlServer);
|
||||
Initialize(Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
|
||||
//Remove the legacy connection string, so we don't end up in a loop if something goes wrong.
|
||||
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
|
||||
|
||||
DetermineSqlServerVersion();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -465,49 +484,6 @@ namespace Umbraco.Core
|
||||
Initialize(providerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the lazy resolution of determining the SQL server version if that is the db type we're using
|
||||
/// </summary>
|
||||
private void DetermineSqlServerVersion()
|
||||
{
|
||||
|
||||
var sqlServerSyntax = SqlSyntax as SqlServerSyntaxProvider;
|
||||
if (sqlServerSyntax != null)
|
||||
{
|
||||
//this will not execute now, it is lazy so will only execute when we need to actually know
|
||||
// the sql server version.
|
||||
sqlServerSyntax.VersionName = new Lazy<SqlServerVersionName>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var database = this._factory.CreateDatabase();
|
||||
|
||||
var version = database.ExecuteScalar<string>("SELECT SERVERPROPERTY('productversion')");
|
||||
var firstPart = version.Split('.')[0];
|
||||
switch (firstPart)
|
||||
{
|
||||
case "11":
|
||||
return SqlServerVersionName.V2012;
|
||||
case "10":
|
||||
return SqlServerVersionName.V2008;
|
||||
case "9":
|
||||
return SqlServerVersionName.V2005;
|
||||
case "8":
|
||||
return SqlServerVersionName.V2000;
|
||||
case "7":
|
||||
return SqlServerVersionName.V7;
|
||||
default:
|
||||
return SqlServerVersionName.Other;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return SqlServerVersionName.Invalid;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal DatabaseSchemaResult ValidateDatabaseSchema()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
@@ -517,7 +493,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
|
||||
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
|
||||
&& ProviderName == "MySql.Data.MySqlClient")
|
||||
&& ProviderName == Constants.DatabaseProviders.MySql)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
|
||||
}
|
||||
@@ -625,13 +601,8 @@ namespace Umbraco.Core
|
||||
|
||||
var installedSchemaVersion = new SemVersion(schemaResult.DetermineInstalledVersion());
|
||||
|
||||
var installedMigrationVersion = new SemVersion(0);
|
||||
//we cannot check the migrations table if it doesn't exist, this will occur when upgrading to 7.3
|
||||
if (schemaResult.ValidTables.Any(x => x.InvariantEquals("umbracoMigration")))
|
||||
{
|
||||
installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
|
||||
}
|
||||
|
||||
var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
|
||||
|
||||
var targetVersion = UmbracoVersion.Current;
|
||||
|
||||
//In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
|
||||
@@ -731,7 +702,7 @@ namespace Umbraco.Core
|
||||
private Attempt<Result> CheckReadyForInstall()
|
||||
{
|
||||
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
|
||||
&& ProviderName == "MySql.Data.MySqlClient")
|
||||
&& ProviderName == Constants.DatabaseProviders.MySql)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
|
||||
}
|
||||
@@ -780,7 +751,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
var dbIsSqlCe = false;
|
||||
if (databaseSettings != null && databaseSettings.ProviderName != null)
|
||||
dbIsSqlCe = databaseSettings.ProviderName == "System.Data.SqlServerCe.4.0";
|
||||
dbIsSqlCe = databaseSettings.ProviderName == Constants.DatabaseProviders.SqlCe;
|
||||
var sqlCeDatabaseExists = false;
|
||||
if (dbIsSqlCe)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
Culture = Configuration.GlobalSettings.DefaultUILanguage;
|
||||
}
|
||||
|
||||
public virtual async Task<ClaimsIdentity> GenerateUserIdentityAsync(BackOfficeUserManager manager)
|
||||
public virtual async Task<ClaimsIdentity> GenerateUserIdentityAsync(BackOfficeUserManager<BackOfficeIdentityUser> manager)
|
||||
{
|
||||
// NOTE the authenticationType must match the umbraco one
|
||||
// defined in CookieAuthenticationOptions.AuthenticationType
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
private static void ThrowTypeException(object value, Type expected, string alias)
|
||||
{
|
||||
throw new Exception(string.Format("Value \"{0}\" of type \"{1}\" could not be converted"
|
||||
throw new InvalidOperationException(string.Format("Value \"{0}\" of type \"{1}\" could not be converted"
|
||||
+ " to type \"{2}\" which is expected by property type \"{3}\".",
|
||||
value, value.GetType(), expected, alias));
|
||||
}
|
||||
|
||||
@@ -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,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public void ClearRules()
|
||||
{
|
||||
foreach (var rule in _ruleCollection)
|
||||
{
|
||||
RemoveRule(rule);
|
||||
}
|
||||
_ruleCollection.Clear();
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
@@ -126,7 +123,7 @@ namespace Umbraco.Core.Models
|
||||
get { return _noAccessNodeId; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _noAccessNodeId, Ps.Value.NoAccessNodeIdSelector); }
|
||||
}
|
||||
|
||||
|
||||
[DataMember]
|
||||
public int ProtectedNodeId
|
||||
{
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[TableName("umbracoRedirectUrl")]
|
||||
[PrimaryKey("id")]
|
||||
[PrimaryKey("id", autoIncrement = false)]
|
||||
[ExplicitColumns]
|
||||
class RedirectUrlDto
|
||||
{
|
||||
@@ -22,8 +22,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
// inserts, and much faster on reads, so... we have an index on a hash.
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 1, Name = "PK_umbracoRedirectUrl")]
|
||||
public int Id { get; set; }
|
||||
[PrimaryKeyColumn(Name = "PK_umbracoRedirectUrl", AutoIncrement = false)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
public int ContentId { get; set; }
|
||||
@@ -39,12 +39,12 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("url")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
//[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "url, createDateUtc")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[Column("urlHash")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "urlHash, contentKey, createDateUtc")]
|
||||
[Length(40)]
|
||||
public string UrlHash { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,18 @@ 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 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,
|
||||
@@ -61,11 +61,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 +76,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>();
|
||||
_instanceTypes = new List<Type>();
|
||||
|
||||
InitializeAppInstances();
|
||||
}
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use ctor specifying IServiceProvider instead")]
|
||||
@@ -110,57 +110,57 @@ 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; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the service provider used to instantiate objects
|
||||
@@ -174,16 +174,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 +191,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 +210,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 +247,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
CurrentHttpContext.Items[_httpContextKey] = instances;
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
|
||||
|
||||
case ObjectLifetimeScope.Application:
|
||||
|
||||
return _applicationInstances.Value;
|
||||
@@ -258,132 +258,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,32 +399,32 @@ 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.
|
||||
@@ -445,9 +445,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 +458,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 +536,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 +581,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);
|
||||
@@ -107,10 +107,10 @@ namespace Umbraco.Core.Persistence
|
||||
var foreignSql = _syntaxProvider.Format(tableDefinition.ForeignKeys);
|
||||
var indexSql = _syntaxProvider.Format(tableDefinition.Indexes);
|
||||
|
||||
var tableExist = _db.TableExist(tableName);
|
||||
var tableExist = TableExist(tableName);
|
||||
if (overwrite && tableExist)
|
||||
{
|
||||
_db.DropTable(tableName);
|
||||
DropTable(tableName);
|
||||
tableExist = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -42,13 +44,13 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
case DatabaseProviders.SqlServer:
|
||||
case DatabaseProviders.SqlAzure:
|
||||
factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
|
||||
factory = DbProviderFactories.GetFactory(Constants.DatabaseProviders.SqlServer);
|
||||
break;
|
||||
case DatabaseProviders.SqlServerCE:
|
||||
factory = DbProviderFactories.GetFactory("System.Data.SqlServerCe.4.0");
|
||||
break;
|
||||
case DatabaseProviders.MySql:
|
||||
factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
|
||||
factory = DbProviderFactories.GetFactory(Constants.DatabaseProviders.MySql);
|
||||
break;
|
||||
case DatabaseProviders.PostgreSQL:
|
||||
case DatabaseProviders.Oracle:
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
|
||||
@@ -266,9 +266,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
//defaults for the member list
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -1, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "10" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "Name" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "username" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -3, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "asc" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]" });
|
||||
|
||||
//layouts for the list view
|
||||
var cardLayout = "{\"name\": \"Grid\",\"path\": \"views/propertyeditors/listview/layouts/grid/grid.html\", \"icon\": \"icon-thumbnails-small\", \"isSystem\": 1, \"selected\": true}";
|
||||
@@ -276,10 +276,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
//defaults for the media list
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -5, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "100" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "VersionDate" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "updateDate" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"sortOrder\",\"isSystem\":1, \"header\": \"Sort order\"},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoRelationTypeData()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
/// <returns></returns>
|
||||
public SemVersion DetermineInstalledVersionByMigrations(IMigrationEntryService migrationEntryService)
|
||||
{
|
||||
var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
|
||||
var mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
|
||||
SemVersion mostrecent = null;
|
||||
|
||||
if (ValidTables.Any(x => x.InvariantEquals("umbracoMigration")))
|
||||
{
|
||||
var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
|
||||
mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
|
||||
}
|
||||
|
||||
return mostrecent ?? new SemVersion(new Version(0, 0, 0));
|
||||
}
|
||||
@@ -116,13 +121,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
//if the error is for umbracoAccess it must be the previous version to 7.3 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoAccess"))))
|
||||
{
|
||||
return new Version(7, 2, 5);
|
||||
return new Version(7, 2, 0);
|
||||
}
|
||||
|
||||
//if the error is for umbracoDeployChecksum it must be the previous version to 7.4 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoDeployChecksum"))))
|
||||
{
|
||||
return new Version(7, 3, 4);
|
||||
return new Version(7, 3, 0);
|
||||
}
|
||||
|
||||
//if the error is for umbracoRedirectUrl it must be the previous version to 7.5 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoRedirectUrl"))))
|
||||
{
|
||||
return new Version(7, 4, 0);
|
||||
}
|
||||
|
||||
return UmbracoVersion.Current;
|
||||
|
||||
+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()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
+28
-13
@@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Create;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
|
||||
@@ -21,24 +20,40 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer
|
||||
|
||||
private string MigrationCode(Database database)
|
||||
{
|
||||
// don't execute if the table is already there
|
||||
var tables = SqlSyntax.GetTablesInSchema(database).ToArray();
|
||||
if (tables.InvariantContains("umbracoRedirectUrl")) return null;
|
||||
var umbracoRedirectUrlTableName = "umbracoRedirectUrl";
|
||||
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
localContext.Create.Table("umbracoRedirectUrl")
|
||||
.WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoRedirectUrl")
|
||||
.WithColumn("contentId").AsInt32().NotNullable()
|
||||
.WithColumn("createDateUtc").AsDateTime().NotNullable()
|
||||
.WithColumn("url").AsString(2048).NotNullable();
|
||||
var tables = SqlSyntax.GetTablesInSchema(database).ToArray();
|
||||
|
||||
if (tables.InvariantContains(umbracoRedirectUrlTableName))
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(database).ToArray();
|
||||
if (columns.Any(x => x.TableName.InvariantEquals(umbracoRedirectUrlTableName) && x.ColumnName.InvariantEquals("id") && x.DataType == "uniqueidentifier"))
|
||||
return null;
|
||||
localContext.Delete.Table(umbracoRedirectUrlTableName);
|
||||
}
|
||||
|
||||
localContext.Create.Index("IX_umbracoRedirectUrl")
|
||||
.OnTable("umbracoRedirectUrl")
|
||||
.OnColumn("url").Ascending()
|
||||
.OnColumn("createDateUtc").Ascending()
|
||||
localContext.Create.Table(umbracoRedirectUrlTableName)
|
||||
.WithColumn("id").AsGuid().NotNullable().PrimaryKey("PK_" + umbracoRedirectUrlTableName)
|
||||
.WithColumn("createDateUtc").AsDateTime().NotNullable()
|
||||
.WithColumn("url").AsString(2048).NotNullable()
|
||||
.WithColumn("contentKey").AsGuid().NotNullable()
|
||||
.WithColumn("urlHash").AsString(40).NotNullable();
|
||||
|
||||
localContext.Create.Index("IX_" + umbracoRedirectUrlTableName).OnTable(umbracoRedirectUrlTableName)
|
||||
.OnColumn("urlHash")
|
||||
.Ascending()
|
||||
.OnColumn("contentKey")
|
||||
.Ascending()
|
||||
.OnColumn("createDateUtc")
|
||||
.Descending()
|
||||
.WithOptions().NonClustered();
|
||||
|
||||
localContext.Create.ForeignKey("FK_" + umbracoRedirectUrlTableName)
|
||||
.FromTable(umbracoRedirectUrlTableName).ForeignColumn("contentKey")
|
||||
.ToTable("umbracoNode").PrimaryColumn("uniqueID");
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Alter;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Create;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Delete;
|
||||
using Umbraco.Core.Persistence.Migrations.Syntax.Execute;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
|
||||
{
|
||||
[Migration("7.5.0", 101, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AddRedirectUrlTable2 : MigrationBase
|
||||
{
|
||||
public AddRedirectUrlTable2(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
// defer, because we are making decisions based upon what's in the database
|
||||
Execute.Code(MigrationCode);
|
||||
}
|
||||
|
||||
private string MigrationCode(Database database)
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoRedirectUrl") && x.ColumnName.InvariantEquals("contentKey")))
|
||||
return null;
|
||||
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
localContext.Execute.Sql("DELETE FROM umbracoRedirectUrl"); // else cannot add non-nullable field
|
||||
|
||||
localContext.Delete.Column("contentId").FromTable("umbracoRedirectUrl");
|
||||
|
||||
// SQL CE does not want to alter-add non-nullable columns ;-(
|
||||
// but it's OK to create as nullable then alter, go figure
|
||||
//localContext.Alter.Table("umbracoRedirectUrl")
|
||||
// .AddColumn("contentKey").AsGuid().NotNullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AddColumn("contentKey").AsGuid().Nullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AlterColumn("contentKey").AsGuid().NotNullable();
|
||||
|
||||
localContext.Create.ForeignKey("FK_umbracoRedirectUrl")
|
||||
.FromTable("umbracoRedirectUrl").ForeignColumn("contentKey")
|
||||
.ToTable("umbracoNode").PrimaryColumn("uniqueID");
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
|
||||
{
|
||||
[Migration("7.5.0", 102, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AddRedirectUrlTable3 : MigrationBase
|
||||
{
|
||||
public AddRedirectUrlTable3(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
// defer, because we are making decisions based upon what's in the database
|
||||
Execute.Code(MigrationCode);
|
||||
}
|
||||
private string MigrationCode(Database database)
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoRedirectUrl") && x.ColumnName.InvariantEquals("hurl")))
|
||||
return null;
|
||||
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
localContext.Execute.Sql("DELETE FROM umbracoRedirectUrl"); // else cannot add non-nullable field
|
||||
|
||||
localContext.Delete.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl");
|
||||
|
||||
// SQL CE does not want to alter-add non-nullable columns ;-(
|
||||
// but it's OK to create as nullable then alter, go figure
|
||||
//localContext.Alter.Table("umbracoRedirectUrl")
|
||||
// .AddColumn("urlHash").AsString(16).NotNullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AddColumn("hurl").AsString(16).Nullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AlterColumn("hurl").AsString(16).NotNullable();
|
||||
|
||||
localContext.Create.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl")
|
||||
.OnColumn("hurl")
|
||||
.Ascending()
|
||||
.OnColumn("contentKey")
|
||||
.Ascending()
|
||||
.OnColumn("createDateUtc")
|
||||
.Descending()
|
||||
.WithOptions().NonClustered();
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
|
||||
{
|
||||
[Migration("7.5.0", 103, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AddRedirectUrlTable4 : MigrationBase
|
||||
{
|
||||
public AddRedirectUrlTable4(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
// defer, because we are making decisions based upon what's in the database
|
||||
Execute.Code(MigrationCode);
|
||||
}
|
||||
private string MigrationCode(Database database)
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoRedirectUrl") && x.ColumnName.InvariantEquals("urlHash")))
|
||||
return null;
|
||||
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
localContext.Execute.Sql("DELETE FROM umbracoRedirectUrl"); // else cannot add non-nullable field
|
||||
|
||||
localContext.Delete.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl");
|
||||
|
||||
localContext.Delete.Column("hurl").FromTable("umbracoRedirectUrl");
|
||||
|
||||
// SQL CE does not want to alter-add non-nullable columns ;-(
|
||||
// but it's OK to create as nullable then alter, go figure
|
||||
//localContext.Alter.Table("umbracoRedirectUrl")
|
||||
// .AddColumn("urlHash").AsString(16).NotNullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AddColumn("urlHash").AsString(16).Nullable();
|
||||
localContext.Alter.Table("umbracoRedirectUrl")
|
||||
.AlterColumn("urlHash").AsString(16).NotNullable();
|
||||
|
||||
localContext.Create.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl")
|
||||
.OnColumn("urlHash")
|
||||
.Ascending()
|
||||
.OnColumn("contentKey")
|
||||
.Ascending()
|
||||
.OnColumn("createDateUtc")
|
||||
.Descending()
|
||||
.WithOptions().NonClustered();
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
+16
-6
@@ -19,22 +19,32 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
// defer, because we are making decisions based upon what's in the database
|
||||
Execute.Code(MigrationCode);
|
||||
}
|
||||
|
||||
private string MigrationCode(Database database)
|
||||
{
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
//Clear all stylesheet data if the tables exist
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
if (tables.InvariantContains("cmsStylesheetProperty"))
|
||||
{
|
||||
Delete.FromTable("cmsStylesheetProperty").AllRows();
|
||||
Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.StylesheetProperty) });
|
||||
localContext.Delete.FromTable("cmsStylesheetProperty").AllRows();
|
||||
localContext.Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.StylesheetProperty) });
|
||||
|
||||
Delete.Table("cmsStylesheetProperty");
|
||||
localContext.Delete.Table("cmsStylesheetProperty");
|
||||
}
|
||||
if (tables.InvariantContains("cmsStylesheet"))
|
||||
{
|
||||
Delete.FromTable("cmsStylesheet").AllRows();
|
||||
Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.Stylesheet) });
|
||||
localContext.Delete.FromTable("cmsStylesheet").AllRows();
|
||||
localContext.Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.Stylesheet) });
|
||||
|
||||
Delete.Table("cmsStylesheet");
|
||||
localContext.Delete.Table("cmsStylesheet");
|
||||
}
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence
|
||||
connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
|
||||
|
||||
// Work out connection string and provider name
|
||||
var providerName = "System.Data.SqlClient";
|
||||
var providerName = Constants.DatabaseProviders.SqlServer;
|
||||
if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
|
||||
@@ -181,7 +181,7 @@ namespace Umbraco.Core.Persistence
|
||||
CommonConstruct();
|
||||
}
|
||||
|
||||
enum DBType
|
||||
internal enum DBType
|
||||
{
|
||||
SqlServer,
|
||||
SqlServerCE,
|
||||
@@ -726,6 +726,55 @@ namespace Umbraco.Core.Persistence
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NOTE: This is a custom mod of PetaPoco!! This builds the paging sql for different db providers
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="sqlSelectRemoved"></param>
|
||||
/// <param name="sqlOrderBy"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="sqlPage"></param>
|
||||
/// <param name="databaseType"></param>
|
||||
/// <param name="skip"></param>
|
||||
/// <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, "");
|
||||
if (rxDistinct.IsMatch(sqlSelectRemoved))
|
||||
{
|
||||
sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1);
|
||||
args = args.Concat(new object[] { skip, take }).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1);
|
||||
args = args.Concat(new object[] { take, skip }).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
|
||||
{
|
||||
// Add auto select clause
|
||||
@@ -734,34 +783,12 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
// Split the SQL into the bits we need
|
||||
string sqlSelectRemoved, sqlOrderBy;
|
||||
if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
|
||||
if (SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy) == false)
|
||||
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");
|
||||
|
||||
// Build the SQL for the actual final result
|
||||
if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle)
|
||||
{
|
||||
sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
|
||||
if (rxDistinct.IsMatch(sqlSelectRemoved))
|
||||
{
|
||||
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);
|
||||
args = args.Concat(new object[] { skip, skip+take }).ToArray();
|
||||
}
|
||||
else if (_dbType == DBType.SqlServerCE)
|
||||
{
|
||||
sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1);
|
||||
args = args.Concat(new object[] { skip, take }).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1);
|
||||
args = args.Concat(new object[] { take, skip }).ToArray();
|
||||
}
|
||||
|
||||
BuildSqlDbSpecificPagingQuery(_dbType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage);
|
||||
}
|
||||
|
||||
// Fetch a page
|
||||
@@ -2315,8 +2342,8 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
|
||||
// Member variables
|
||||
string _connectionString;
|
||||
string _providerName;
|
||||
readonly string _connectionString;
|
||||
readonly string _providerName;
|
||||
DbProviderFactory _factory;
|
||||
IDbConnection _sharedConnection;
|
||||
IDbTransaction _transaction;
|
||||
|
||||
@@ -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,54 +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;
|
||||
if ((sqlServerSyntax != null && (int)sqlServerSyntax.VersionName.Value < (int)SqlServerVersionName.V2008)
|
||||
|| SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
int processed = 0;
|
||||
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
db.Insert(poco);
|
||||
}
|
||||
}
|
||||
else
|
||||
var usedNativeSqlPlatformInserts = useNativeSqlPlatformBulkInsert
|
||||
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
|
||||
|
||||
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>
|
||||
@@ -248,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
|
||||
@@ -305,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++));
|
||||
@@ -320,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")]
|
||||
@@ -404,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);
|
||||
}
|
||||
|
||||
@@ -77,7 +91,8 @@ namespace Umbraco.Core.Persistence
|
||||
var type = typeof(TColumn);
|
||||
var tableName = type.GetTableName();
|
||||
|
||||
var syntax = string.Format("{0}.{1} DESC",
|
||||
//need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
|
||||
var syntax = string.Format("({0}.{1}) DESC",
|
||||
sqlSyntax.GetQuotedTableName(tableName),
|
||||
sqlSyntax.GetQuotedColumnName(columnName));
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+915
-723
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,74 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
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 BaseMapper _mapper;
|
||||
|
||||
public ModelToSqlExpressionVisitor(ISqlSyntaxProvider sqlSyntax, BaseMapper mapper)
|
||||
: base(sqlSyntax)
|
||||
{
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public ModelToSqlExpressionVisitor()
|
||||
: this(SqlSyntaxContext.SqlSyntaxProvider, 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))
|
||||
{
|
||||
//don't execute if compiled
|
||||
if (Visited == false)
|
||||
{
|
||||
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;
|
||||
}
|
||||
//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("The mapper returned an empty field for the member name: " + 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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,81 @@
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
|
||||
public PocoToSqlExpressionVisitor()
|
||||
: base(SqlSyntaxContext.SqlSyntaxProvider)
|
||||
{
|
||||
_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))
|
||||
{
|
||||
//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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -63,9 +56,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.Where<DocumentDto>(x => x.Newest)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql).FirstOrDefault();
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
@@ -80,7 +73,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
if (ids.Any())
|
||||
{
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids });
|
||||
}
|
||||
|
||||
//we only want the newest ones with this method
|
||||
@@ -101,7 +94,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return ProcessQuery(sql);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<IContent>
|
||||
|
||||
@@ -172,90 +165,57 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of VersionableRepositoryBase<IContent>
|
||||
|
||||
public void RebuildXmlStructures(Func<IContent, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
|
||||
public void RebuildXmlStructures(Func<IContent, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
|
||||
{
|
||||
// the previous way of doing this was to run it all in one big transaction,
|
||||
// and to bulk-insert groups of xml rows - which works, until the transaction
|
||||
// times out - and besides, because v7 transactions are ReadCommited, it does
|
||||
// not bring much safety - so this reverts to updating each record individually,
|
||||
// and it may be slower in the end, but should be more resilient.
|
||||
|
||||
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
|
||||
using (var tr = Database.GetTransaction())
|
||||
var baseId = 0;
|
||||
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
|
||||
while (true)
|
||||
{
|
||||
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var subQuery = new Sql()
|
||||
.Select("DISTINCT cmsContentXml.nodeId")
|
||||
.From<ContentXmlDto>()
|
||||
.InnerJoin<DocumentDto>()
|
||||
.On<ContentXmlDto, DocumentDto>(left => left.NodeId, right => right.NodeId);
|
||||
// get the next group of nodes
|
||||
var query = GetBaseQuery(false);
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
query = query
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId && x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
else
|
||||
// no more nodes, break
|
||||
if (xmlItems.Count == 0) break;
|
||||
|
||||
foreach (var xmlItem in xmlItems)
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
try
|
||||
{
|
||||
var id1 = id;
|
||||
var subQuery = new Sql()
|
||||
.Select("cmsDocument.nodeId")
|
||||
.From<DocumentDto>()
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<DocumentDto>(dto => dto.Published)
|
||||
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
// should happen in most cases, then it tries to insert, and it should work
|
||||
// unless the node has been deleted, and we just report the exception
|
||||
Database.InsertOrUpdate(xmlItem);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
|
||||
}
|
||||
}
|
||||
|
||||
//now insert the data, again if something fails here, the whole transaction is reversed
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.Published == true);
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var contentTypeId in contentTypeIds)
|
||||
{
|
||||
//copy local
|
||||
var id = contentTypeId;
|
||||
var query = Query<IContent>.Builder.Where(x => x.Published == true && x.ContentTypeId == id && x.Trashed == false);
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
baseId = xmlItems.Last().NodeId;
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildXmlStructuresProcessQuery(Func<IContent, XElement> serializer, IQuery<IContent> query, Transaction tr, int pageSize)
|
||||
public override IEnumerable<IContent> GetAllVersions(int id)
|
||||
{
|
||||
var pageIndex = 0;
|
||||
var total = long.MinValue;
|
||||
var processed = 0;
|
||||
do
|
||||
{
|
||||
//NOTE: This is an important call, we cannot simply make a call to:
|
||||
// GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
|
||||
// because that method is used to query 'latest' content items where in this case we don't necessarily
|
||||
// want latest content items because a pulished content item might not actually be the latest.
|
||||
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
|
||||
var descendants = GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out total,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, "Path", Direction.Ascending, true);
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
|
||||
|
||||
//bulk insert it into the database
|
||||
Database.BulkInsertRecords(xmlItems, tr);
|
||||
|
||||
processed += xmlItems.Length;
|
||||
|
||||
pageIndex++;
|
||||
} while (processed < total);
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
public override IContent GetByVersion(Guid versionId)
|
||||
@@ -382,6 +342,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
nodeDto.ValidatePathWithException();
|
||||
Database.Update(nodeDto);
|
||||
|
||||
//Update entity with correct values
|
||||
@@ -519,6 +480,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Updates the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
|
||||
nodeDto.ValidatePathWithException();
|
||||
var o = Database.Update(nodeDto);
|
||||
|
||||
//Only update this DTO if the contentType has actually changed
|
||||
@@ -666,26 +628,77 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
foreach (var dto in dtos)
|
||||
/// <summary>
|
||||
/// This builds the Xml document used for the XML cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XmlDocument BuildXmlCache()
|
||||
{
|
||||
//TODO: This is what we should do , but converting to use XDocument would be breaking unless we convert
|
||||
// to XmlDocument at the end of this, but again, this would be bad for memory... though still not nearly as
|
||||
// bad as what is happening before!
|
||||
// We'll keep using XmlDocument for now though, but XDocument xml generation is much faster:
|
||||
// https://blogs.msdn.microsoft.com/codejunkie/2008/10/08/xmldocument-vs-xelement-performance/
|
||||
// I think we already have code in here to convert XDocument to XmlDocument but in case we don't here
|
||||
// it is: https://blogs.msdn.microsoft.com/marcelolr/2009/03/13/fast-way-to-convert-xmldocument-into-xdocument/
|
||||
|
||||
//// Prepare an XmlDocument with an appropriate inline DTD to match
|
||||
//// the expected content
|
||||
//var parent = new XElement("root", new XAttribute("id", "-1"));
|
||||
//var xmlDoc = new XDocument(
|
||||
// new XDocumentType("root", null, null, DocumentType.GenerateDtd()),
|
||||
// parent);
|
||||
|
||||
var xmlDoc = new XmlDocument();
|
||||
var doctype = xmlDoc.CreateDocumentType("root", null, null,
|
||||
ApplicationContext.Current.Services.ContentTypeService.GetContentTypesDtd());
|
||||
xmlDoc.AppendChild(doctype);
|
||||
var parent = xmlDoc.CreateElement("root");
|
||||
var pIdAtt = xmlDoc.CreateAttribute("id");
|
||||
pIdAtt.Value = "-1";
|
||||
parent.Attributes.Append(pIdAtt);
|
||||
xmlDoc.AppendChild(parent);
|
||||
|
||||
const string sql = @"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.xml, umbracoNode.level from umbracoNode
|
||||
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
|
||||
where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1)
|
||||
order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
|
||||
|
||||
XmlElement last = null;
|
||||
|
||||
//NOTE: Query creates a reader - does not load all into memory
|
||||
foreach (var row in Database.Query<dynamic>(sql, new { type = new Guid(Constants.ObjectTypes.Document) }))
|
||||
{
|
||||
//Check in the cache first. If it exists there AND it is published
|
||||
// then we can use that entity. Otherwise if it is not published (which can be the case
|
||||
// because we only store the 'latest' entries in the cache which might not be the published
|
||||
// version)
|
||||
var fromCache = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
|
||||
//var fromCache = TryGetFromCache(dto.NodeId);
|
||||
if (fromCache != null && fromCache.Published)
|
||||
string parentId = ((int)row.parentID).ToInvariantString();
|
||||
string xml = row.xml;
|
||||
int sortOrder = row.sortOrder;
|
||||
|
||||
//if the parentid is changing
|
||||
if (last != null && last.GetAttribute("parentID") != parentId)
|
||||
{
|
||||
yield return fromCache;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return CreateContentFromDto(dto, dto.VersionId, sql);
|
||||
parent = xmlDoc.GetElementById(parentId);
|
||||
if (parent == null)
|
||||
{
|
||||
//Need to short circuit here, if the parent is not there it means that the parent is unpublished
|
||||
// and therefore the child is not published either so cannot be included in the xml cache
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var xmlDocFragment = xmlDoc.CreateDocumentFragment();
|
||||
xmlDocFragment.InnerXml = xml;
|
||||
|
||||
last = (XmlElement)parent.AppendChild(xmlDocFragment);
|
||||
|
||||
// fix sortOrder - see notes in UpdateSortOrder
|
||||
last.Attributes["sortOrder"].Value = sortOrder.ToInvariantString();
|
||||
}
|
||||
|
||||
return xmlDoc;
|
||||
|
||||
}
|
||||
|
||||
public int CountPublished()
|
||||
@@ -791,7 +804,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -822,83 +835,80 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return base.GetDatabaseFieldNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sql)
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
|
||||
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
//nothing found
|
||||
if (dtos.Any() == false) return Enumerable.Empty<IContent>();
|
||||
var content = new IContent[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
var templateIds = new List<int>();
|
||||
|
||||
//content types
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
|
||||
.ToArray();
|
||||
|
||||
|
||||
var ids = dtos
|
||||
.Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
.Select(x => x.TemplateId.Value).ToArray();
|
||||
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var templates = ids.Length == 0 ? Enumerable.Empty<ITemplate>() : _templateRepository.GetAll(ids).ToArray();
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
|
||||
//Go get the property data for each document
|
||||
var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.VersionId,
|
||||
d.dto.ContentVersionDto.VersionDate,
|
||||
d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType));
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateContentFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId),
|
||||
templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId.HasValue ? d.dto.TemplateId.Value : -1)),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="template"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IContent CreateContentFromDto(DocumentDto dto,
|
||||
IContentType contentType,
|
||||
ITemplate template,
|
||||
Models.PropertyCollection propCollection)
|
||||
{
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var content = factory.BuildEntity(dto);
|
||||
|
||||
//Check if template id is set on DocumentDto, and get ITemplate if it is.
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure there isn't one set.
|
||||
content.Template = null;
|
||||
var dto = dtos[i];
|
||||
|
||||
// if the cache contains the published version, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
|
||||
//only use this cached version if the dto returned is also the publish version, they must match
|
||||
if (cached != null && cached.Published && dto.Published)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
// need template
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
templateIds.Add(dto.TemplateId.Value);
|
||||
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.VersionId,
|
||||
dto.ContentVersionDto.VersionDate,
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
content.Properties = propCollection;
|
||||
// load all required templates in 1 query
|
||||
var templates = _templateRepository.GetAll(templateIds.ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
var dto = dtos[dtoIndex];
|
||||
ITemplate template = null;
|
||||
if (dto.TemplateId.HasValue)
|
||||
templates.TryGetValue(dto.TemplateId.Value, out template); // else null
|
||||
cc.Template = template;
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)content).ResetDirtyProperties(false);
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,8 +251,8 @@ AND umbracoNode.id <> @id",
|
||||
var nodeDto = dto.NodeDto;
|
||||
Database.Update(nodeDto);
|
||||
|
||||
// look up ContentType entry to get PrimaryKey for updating the DTO
|
||||
// fixme - why? we are UPDATING so we should ALREADY have a PK!
|
||||
//Look up ContentType entry to get PrimaryKey for updating the DTO
|
||||
var dtoPk = Database.First<ContentTypeDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||
dto.PrimaryKey = dtoPk.PrimaryKey;
|
||||
Database.Update(dto);
|
||||
@@ -262,31 +262,30 @@ AND umbracoNode.id <> @id",
|
||||
foreach (var composition in entity.ContentTypeComposition)
|
||||
Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id });
|
||||
|
||||
//Removing a ContentType from a composition (U4-1690)
|
||||
//1. Find content based on the current ContentType: entity.Id
|
||||
//2. Find all PropertyTypes on the ContentType that was removed - tracked id (key)
|
||||
//3. Remove properties based on property types from the removed content type where the content ids correspond to those found in step one
|
||||
// removing a ContentType from a composition (U4-1690)
|
||||
// 1. Find content based on the current ContentType: entity.Id
|
||||
// 2. Find all PropertyTypes on the ContentType that was removed - tracked id (key)
|
||||
// 3. Remove properties based on property types from the removed content type where the content ids correspond to those found in step one
|
||||
var compositionBase = entity as ContentTypeCompositionBase;
|
||||
if (compositionBase != null && compositionBase.RemovedContentTypeKeyTracker != null &&
|
||||
compositionBase.RemovedContentTypeKeyTracker.Any())
|
||||
{
|
||||
//Find Content based on the current ContentType
|
||||
// find Content based on the current ContentType
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<ContentDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.From<ContentDto>(SqlSyntax)
|
||||
.InnerJoin<NodeDto>(SqlSyntax).On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == new Guid(Constants.ObjectTypes.Document))
|
||||
.Where<ContentDto>(x => x.ContentTypeId == entity.Id);
|
||||
|
||||
var contentDtos = Database.Fetch<ContentDto, NodeDto>(sql);
|
||||
//Loop through all tracked keys, which corresponds to the ContentTypes that has been removed from the composition
|
||||
|
||||
// loop through all tracked keys, which corresponds to the ContentTypes that has been removed from the composition
|
||||
foreach (var key in compositionBase.RemovedContentTypeKeyTracker)
|
||||
{
|
||||
//Find PropertyTypes for the removed ContentType
|
||||
// find PropertyTypes for the removed ContentType
|
||||
var propertyTypes = Database.Fetch<PropertyTypeDto>("WHERE contentTypeId = @Id", new { Id = key });
|
||||
//Loop through the Content that is based on the current ContentType in order to remove the Properties that are
|
||||
//based on the PropertyTypes that belong to the removed ContentType.
|
||||
// loop through the Content that is based on the current ContentType in order to remove the Properties that are
|
||||
// based on the PropertyTypes that belong to the removed ContentType.
|
||||
foreach (var contentDto in contentDtos)
|
||||
{
|
||||
foreach (var propertyType in propertyTypes)
|
||||
@@ -294,51 +293,47 @@ AND umbracoNode.id <> @id",
|
||||
var nodeId = contentDto.NodeId;
|
||||
var propertyTypeId = propertyType.Id;
|
||||
var propertySql = new Sql().Select("cmsPropertyData.id")
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<PropertyTypeDto>()
|
||||
.On<PropertyDataDto, PropertyTypeDto>(
|
||||
left => left.PropertyTypeId, right => right.Id)
|
||||
.Where<PropertyDataDto>(x => x.NodeId == nodeId)
|
||||
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
|
||||
.From<PropertyDataDto>(SqlSyntax)
|
||||
.InnerJoin<PropertyTypeDto>(SqlSyntax).On<PropertyDataDto, PropertyTypeDto>(SqlSyntax, left => left.PropertyTypeId, right => right.Id)
|
||||
.Where<PropertyDataDto>(x => x.NodeId == nodeId)
|
||||
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
|
||||
|
||||
//Finally delete the properties that match our criteria for removing a ContentType from the composition
|
||||
// finally delete the properties that match our criteria for removing a ContentType from the composition
|
||||
Database.Delete<PropertyDataDto>(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Delete the allowed content type entries before adding the updated collection
|
||||
Database.Delete<ContentTypeAllowedContentTypeDto>("WHERE Id = @Id", new { Id = entity.Id });
|
||||
//Insert collection of allowed content types
|
||||
// delete the allowed content type entries before re-inserting the collectino of allowed content types
|
||||
Database.Delete<ContentTypeAllowedContentTypeDto>("WHERE Id = @Id", new { entity.Id });
|
||||
foreach (var allowedContentType in entity.AllowedContentTypes)
|
||||
{
|
||||
Database.Insert(new ContentTypeAllowedContentTypeDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
AllowedId = allowedContentType.Id.Value,
|
||||
SortOrder = allowedContentType.SortOrder
|
||||
});
|
||||
{
|
||||
Id = entity.Id,
|
||||
AllowedId = allowedContentType.Id.Value,
|
||||
SortOrder = allowedContentType.SortOrder
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME below, manage the property types
|
||||
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
|
||||
// delete ??? fixme wtf is this?!
|
||||
// by excepting entries from db with entries from collections
|
||||
if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
|
||||
{
|
||||
//Delete PropertyTypes by excepting entries from db with entries from collections
|
||||
var dbPropertyTypes = Database.Fetch<PropertyTypeDto>("WHERE contentTypeId = @Id", new { Id = entity.Id });
|
||||
var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id);
|
||||
var entityPropertyTypes = entity.PropertyTypes.Where(x => x.HasIdentity).Select(x => x.Id);
|
||||
var items = dbPropertyTypeAlias.Except(entityPropertyTypes);
|
||||
foreach (var item in items)
|
||||
{
|
||||
//Before a PropertyType can be deleted, all Properties based on that PropertyType should be deleted.
|
||||
Database.Delete<TagRelationshipDto>("WHERE propertyTypeId = @Id", new { Id = item });
|
||||
Database.Delete<PropertyDataDto>("WHERE propertytypeid = @Id", new { Id = item });
|
||||
Database.Delete<PropertyTypeDto>("WHERE contentTypeId = @Id AND id = @PropertyTypeId",
|
||||
new { Id = entity.Id, PropertyTypeId = item });
|
||||
}
|
||||
DeletePropertyType(entity.Id, item);
|
||||
}
|
||||
|
||||
// delete tabs
|
||||
// by excepting entries from db with entries from collections
|
||||
List<int> orphanPropertyTypeIds = null;
|
||||
if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
|
||||
{
|
||||
// todo
|
||||
@@ -357,68 +352,97 @@ AND umbracoNode.id <> @id",
|
||||
// (all gone)
|
||||
|
||||
// delete tabs that do not exist anymore
|
||||
// get the tabs that are currently existing (in the db)
|
||||
// get the tabs that we want, now
|
||||
// and derive the tabs that we want to delete
|
||||
// get the tabs that are currently existing (in the db), get the tabs that we want,
|
||||
// now, and derive the tabs that we want to delete
|
||||
var existingPropertyGroups = Database.Fetch<PropertyTypeGroupDto>("WHERE contentTypeNodeId = @id", new { id = entity.Id })
|
||||
.Select(x => x.Id)
|
||||
.ToList();
|
||||
var newPropertyGroups = entity.PropertyGroups.Select(x => x.Id).ToList();
|
||||
var tabsToDelete = existingPropertyGroups
|
||||
var groupsToDelete = existingPropertyGroups
|
||||
.Except(newPropertyGroups)
|
||||
.ToArray();
|
||||
|
||||
// move properties to generic properties, and delete the tabs
|
||||
if (tabsToDelete.Length > 0)
|
||||
// delete the tabs
|
||||
if (groupsToDelete.Length > 0)
|
||||
{
|
||||
Database.Update<PropertyTypeDto>("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = tabsToDelete });
|
||||
Database.Delete<PropertyTypeGroupDto>("WHERE id IN (@ids)", new { ids = tabsToDelete });
|
||||
// if the tab contains properties, take care of them
|
||||
// - move them to 'generic properties' so they remain consistent
|
||||
// - keep track of them, later on we'll figure out what to do with them
|
||||
// see http://issues.umbraco.org/issue/U4-8663
|
||||
orphanPropertyTypeIds = Database.Fetch<PropertyTypeDto>("WHERE propertyTypeGroupId IN (@ids)", new { ids = groupsToDelete })
|
||||
.Select(x => x.Id).ToList();
|
||||
Database.Update<PropertyTypeDto>("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = groupsToDelete });
|
||||
|
||||
// now we can delete the tabs
|
||||
Database.Delete<PropertyTypeGroupDto>("WHERE id IN (@ids)", new { ids = groupsToDelete });
|
||||
}
|
||||
}
|
||||
|
||||
var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
|
||||
|
||||
//Run through all groups to insert or update entries
|
||||
// insert or update groups, assign properties
|
||||
foreach (var propertyGroup in entity.PropertyGroups)
|
||||
{
|
||||
var tabDto = propertyGroupFactory.BuildGroupDto(propertyGroup);
|
||||
int groupPrimaryKey = propertyGroup.HasIdentity
|
||||
? Database.Update(tabDto)
|
||||
: Convert.ToInt32(Database.Insert(tabDto));
|
||||
// insert or update group
|
||||
var groupDto = propertyGroupFactory.BuildGroupDto(propertyGroup);
|
||||
var groupId = propertyGroup.HasIdentity
|
||||
? Database.Update(groupDto)
|
||||
: Convert.ToInt32(Database.Insert(groupDto));
|
||||
if (propertyGroup.HasIdentity == false)
|
||||
propertyGroup.Id = groupPrimaryKey; //Set Id on new PropertyGroup
|
||||
propertyGroup.Id = groupId;
|
||||
else
|
||||
groupId = propertyGroup.Id;
|
||||
|
||||
//Ensure that the PropertyGroup's Id is set on the PropertyTypes within a group
|
||||
//unless the PropertyGroupId has already been changed.
|
||||
// assign properties to the group
|
||||
// (all of them, even those that have .IsPropertyDirty("PropertyGroupId") == true,
|
||||
// because it should have been set to this group anyways and better be safe)
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
if (propertyType.IsPropertyDirty("PropertyGroupId") == false)
|
||||
{
|
||||
var tempGroup = propertyGroup;
|
||||
propertyType.PropertyGroupId = new Lazy<int>(() => tempGroup.Id);
|
||||
}
|
||||
}
|
||||
propertyType.PropertyGroupId = new Lazy<int>(() => groupId);
|
||||
}
|
||||
|
||||
//Run through all PropertyTypes to insert or update entries
|
||||
// insert or update properties
|
||||
// all of them, no-group and in groups
|
||||
foreach (var propertyType in entity.PropertyTypes)
|
||||
{
|
||||
var tabId = propertyType.PropertyGroupId != null ? propertyType.PropertyGroupId.Value : default(int);
|
||||
//If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
|
||||
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
|
||||
{
|
||||
AssignDataTypeFromPropertyEditor(propertyType);
|
||||
}
|
||||
var groupId = propertyType.PropertyGroupId != null ? propertyType.PropertyGroupId.Value : default(int);
|
||||
|
||||
//validate the alias!
|
||||
// if the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
|
||||
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
|
||||
AssignDataTypeFromPropertyEditor(propertyType);
|
||||
|
||||
// validate the alias
|
||||
ValidateAlias(propertyType);
|
||||
|
||||
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(tabId, propertyType);
|
||||
int typePrimaryKey = propertyType.HasIdentity
|
||||
? Database.Update(propertyTypeDto)
|
||||
: Convert.ToInt32(Database.Insert(propertyTypeDto));
|
||||
// insert or update property
|
||||
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(groupId, propertyType);
|
||||
var typeId = propertyType.HasIdentity
|
||||
? Database.Update(propertyTypeDto)
|
||||
: Convert.ToInt32(Database.Insert(propertyTypeDto));
|
||||
if (propertyType.HasIdentity == false)
|
||||
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
|
||||
propertyType.Id = typeId;
|
||||
else
|
||||
typeId = propertyType.Id;
|
||||
|
||||
// not an orphan anymore
|
||||
if (orphanPropertyTypeIds != null)
|
||||
orphanPropertyTypeIds.Remove(typeId);
|
||||
}
|
||||
|
||||
// deal with orphan properties: those that were in a deleted tab,
|
||||
// and have not been re-mapped to another tab or to 'generic properties'
|
||||
if (orphanPropertyTypeIds != null)
|
||||
foreach (var id in orphanPropertyTypeIds)
|
||||
DeletePropertyType(entity.Id, id);
|
||||
}
|
||||
|
||||
private void DeletePropertyType(int contentTypeId, int propertyTypeId)
|
||||
{
|
||||
// first clear dependencies
|
||||
Database.Delete<TagRelationshipDto>("WHERE propertyTypeId = @Id", new { Id = propertyTypeId });
|
||||
Database.Delete<PropertyDataDto>("WHERE propertytypeid = @Id", new { Id = propertyTypeId });
|
||||
|
||||
// then delete the property type
|
||||
Database.Delete<PropertyTypeDto>("WHERE contentTypeId = @Id AND id = @PropertyTypeId", new { Id = contentTypeId, PropertyTypeId = propertyTypeId });
|
||||
}
|
||||
|
||||
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)
|
||||
@@ -649,7 +673,7 @@ AND umbracoNode.id <> @id",
|
||||
var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray();
|
||||
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
{
|
||||
var entityId = contentType.Id;
|
||||
|
||||
var parentContentTypes = allParentContentTypes.Where(x =>
|
||||
@@ -714,10 +738,10 @@ AND umbracoNode.id <> @id",
|
||||
out IDictionary<int, List<int>> parentMediaTypeIds)
|
||||
{
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
|
||||
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -741,7 +765,7 @@ AND umbracoNode.id <> @id",
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType) });
|
||||
|
||||
if (result.Any() == false)
|
||||
@@ -848,16 +872,16 @@ AND umbracoNode.id <> @id",
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<int, List<AssociatedTemplate>> associatedTemplates,
|
||||
out IDictionary<int, List<int>> parentContentTypeIds)
|
||||
{
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
|
||||
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
|
||||
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -890,7 +914,7 @@ AND umbracoNode.id <> @id",
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType)});
|
||||
|
||||
if (result.Any() == false)
|
||||
@@ -911,7 +935,7 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
var ct = queue.Dequeue();
|
||||
|
||||
//check for default templates
|
||||
//check for default templates
|
||||
bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault);
|
||||
int? templateId = ct.dtTemplateId;
|
||||
if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && isDefaultTemplate.Value && templateId.HasValue)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where(GetBaseWhereClause(), new { id = id, NodeObjectType = NodeObjectTypeId });
|
||||
|
||||
var nodeDto = Database.Fetch<NodeDto>(sql).FirstOrDefault();
|
||||
var nodeDto = Database.Fetch<NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
return nodeDto == null ? null : CreateEntity(nodeDto);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,12 +58,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var macroDto = Database.Fetch<ExternalLoginDto>(sql).FirstOrDefault();
|
||||
if (macroDto == null)
|
||||
var dto = Database.Fetch<ExternalLoginDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
var factory = new ExternalLoginFactory();
|
||||
var entity = factory.BuildEntity(macroDto);
|
||||
var entity = factory.BuildEntity(dto);
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -11,6 +12,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>, IDeleteMediaFilesRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// This builds the Xml document used for the XML cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
XmlDocument BuildXmlCache();
|
||||
|
||||
/// <summary>
|
||||
/// Get the count of published items
|
||||
/// </summary>
|
||||
|
||||
@@ -38,5 +38,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
|
||||
IEnumerable<IMedia> GetPagedResultsByQuery(IQuery<IMedia> query, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy, Direction orderDirection, bool orderBySystemField, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged media descendants as XML by path
|
||||
/// </summary>
|
||||
/// <param name="path">Path starts with</param>
|
||||
/// <param name="pageIndex">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records the query would return without paging</param>
|
||||
/// <returns>A paged enumerable of XML entries of media items</returns>
|
||||
IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IRedirectUrl"/> repository.
|
||||
/// </summary>
|
||||
public interface IRedirectUrlRepository : IRepositoryQueryable<int, IRedirectUrl>
|
||||
public interface IRedirectUrlRepository : IRepositoryQueryable<Guid, IRedirectUrl>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a redirect url.
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// Deletes a redirect url.
|
||||
/// </summary>
|
||||
/// <param name="id">The redirect url identifier.</param>
|
||||
void Delete(int id);
|
||||
void Delete(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all redirect urls.
|
||||
@@ -66,5 +66,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="total">The total count of redirect urls.</param>
|
||||
/// <returns>The redirect urls.</returns>
|
||||
IEnumerable<IRedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total);
|
||||
|
||||
/// <summary>
|
||||
/// Searches for all redirect urls that contain a given search term in their URL property.
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The term to search for.</param>
|
||||
/// <param name="pageIndex">The page index.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="total">The total count of redirect urls.</param>
|
||||
/// <returns>The redirect urls.</returns>
|
||||
IEnumerable<IRedirectUrl> SearchUrls(string searchTerm, long pageIndex, int pageSize, out long total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
int CountDescendants(int parentId, string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/>.
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/> ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="TEntity"/> to retrieve versions from</param>
|
||||
/// <returns>An enumerable list of the same <see cref="TEntity"/> object with different versions</returns>
|
||||
IEnumerable<TEntity> GetAllVersions(int id);
|
||||
IEnumerable<TEntity> GetAllVersions(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="maxRows">The maximum number of rows to return</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<Guid> GetVersionIds(int id, int maxRows);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific version of an <see cref="TEntity"/>.
|
||||
|
||||
@@ -4,21 +4,17 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -52,9 +48,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
|
||||
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
@@ -137,6 +133,74 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of VersionableRepositoryBase<IContent>
|
||||
|
||||
public override IEnumerable<IMedia> GetAllVersions(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
var content = new IMedia[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
|
||||
// if the cache contains the item, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
|
||||
if (cached != null)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.VersionId,
|
||||
dto.VersionDate,
|
||||
dto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity) cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public override IMedia GetByVersion(Guid versionId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
@@ -163,90 +227,51 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return media;
|
||||
}
|
||||
|
||||
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
|
||||
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
|
||||
{
|
||||
// the previous way of doing this was to run it all in one big transaction,
|
||||
// and to bulk-insert groups of xml rows - which works, until the transaction
|
||||
// times out - and besides, because v7 transactions are ReadCommited, it does
|
||||
// not bring much safety - so this reverts to updating each record individually,
|
||||
// and it may be slower in the end, but should be more resilient.
|
||||
|
||||
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
|
||||
using (var tr = Database.GetTransaction())
|
||||
var baseId = 0;
|
||||
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
|
||||
while (true)
|
||||
{
|
||||
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
|
||||
var subQuery = new Sql()
|
||||
.Select("DISTINCT cmsContentXml.nodeId")
|
||||
.From<ContentXmlDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType);
|
||||
// get the next group of nodes
|
||||
var query = GetBaseQuery(false);
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
query = query
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
else
|
||||
// no more nodes, break
|
||||
if (xmlItems.Count == 0) break;
|
||||
|
||||
foreach (var xmlItem in xmlItems)
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
try
|
||||
{
|
||||
var id1 = id;
|
||||
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
|
||||
var subQuery = new Sql()
|
||||
.Select("DISTINCT cmsContentXml.nodeId")
|
||||
.From<ContentXmlDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType)
|
||||
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
// InsertOrUpdate tries to update first, which is good since it is what
|
||||
// should happen in most cases, then it tries to insert, and it should work
|
||||
// unless the node has been deleted, and we just report the exception
|
||||
Database.InsertOrUpdate(xmlItem);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
|
||||
}
|
||||
}
|
||||
|
||||
//now insert the data, again if something fails here, the whole transaction is reversed
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var query = Query<IMedia>.Builder;
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var contentTypeId in contentTypeIds)
|
||||
{
|
||||
//copy local
|
||||
var id = contentTypeId;
|
||||
var query = Query<IMedia>.Builder.Where(x => x.ContentTypeId == id && x.Trashed == false);
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
baseId = xmlItems.Last().NodeId;
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildXmlStructuresProcessQuery(Func<IMedia, XElement> serializer, IQuery<IMedia> query, Transaction tr, int pageSize)
|
||||
{
|
||||
var pageIndex = 0;
|
||||
var total = long.MinValue;
|
||||
var processed = 0;
|
||||
do
|
||||
{
|
||||
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
|
||||
|
||||
//bulk insert it into the database
|
||||
Database.BulkInsertRecords(xmlItems, tr);
|
||||
|
||||
processed += xmlItems.Length;
|
||||
|
||||
pageIndex++;
|
||||
} while (processed < total);
|
||||
}
|
||||
|
||||
public void AddOrUpdateContentXml(IMedia content, Func<IMedia, XElement> xml)
|
||||
{
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMedia>(content, xml));
|
||||
@@ -299,6 +324,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
nodeDto.ValidatePathWithException();
|
||||
Database.Update(nodeDto);
|
||||
|
||||
//Update entity with correct values
|
||||
@@ -372,6 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Updates the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentDto.NodeDto;
|
||||
nodeDto.ValidatePathWithException();
|
||||
var o = Database.Update(nodeDto);
|
||||
|
||||
//Only update this DTO if the contentType has actually changed
|
||||
@@ -413,6 +440,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
foreach (var property in entity.Properties)
|
||||
{
|
||||
if (keyDictionary.ContainsKey(property.PropertyTypeId) == false) continue;
|
||||
|
||||
property.Id = keyDictionary[property.PropertyTypeId];
|
||||
}
|
||||
}
|
||||
@@ -453,72 +482,48 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Func<Tuple<string, object[]>> filterCallback = null;
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
sbWhere.Append("AND (umbracoNode." + SqlSyntax.GetQuotedColumnName("text") + " LIKE @" + args.Count + ")");
|
||||
sbWhere
|
||||
.Append("AND (")
|
||||
.Append(SqlSyntax.GetQuotedTableName("umbracoNode"))
|
||||
.Append(".")
|
||||
.Append(SqlSyntax.GetQuotedColumnName("text"))
|
||||
.Append(" LIKE @")
|
||||
.Append(args.Count)
|
||||
.Append(")");
|
||||
args.Add("%" + filter + "%");
|
||||
|
||||
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
|
||||
}
|
||||
|
||||
return GetPagedResultsByQuery<ContentVersionDto, Models.Media>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsContentVersion", "contentId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sql)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
|
||||
var ids = dtos.Select(x => x.ContentDto.ContentTypeId).ToArray();
|
||||
|
||||
//content types
|
||||
var contentTypes = ids.Length == 0 ? Enumerable.Empty<IMediaType>() : _mediaTypeRepository.GetAll(ids).ToArray();
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
|
||||
//Go get the property data for each document
|
||||
var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.VersionId,
|
||||
d.dto.VersionDate,
|
||||
d.dto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType))
|
||||
.ToArray();
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateMediaFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentDto.ContentTypeId),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a media object from a ContentDto
|
||||
/// Gets paged media descendants as XML by path
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IMedia CreateMediaFromDto(ContentVersionDto dto,
|
||||
IMediaType contentType,
|
||||
PropertyCollection propCollection)
|
||||
/// <param name="path">Path starts with</param>
|
||||
/// <param name="pageIndex">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records the query would return without paging</param>
|
||||
/// <returns>A paged enumerable of XML entries of media items</returns>
|
||||
public IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords)
|
||||
{
|
||||
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
var media = factory.BuildEntity(dto);
|
||||
|
||||
media.Properties = propCollection;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)media).ResetDirtyProperties(false);
|
||||
return media;
|
||||
Sql query;
|
||||
if (path == "-1")
|
||||
{
|
||||
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", Guid.Parse(Constants.ObjectTypes.Media)).OrderBy("nodeId");
|
||||
}
|
||||
else
|
||||
{
|
||||
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE @0)", path.EnsureEndsWith(",%")).OrderBy("nodeId");
|
||||
}
|
||||
var pagedResult = Database.Page<ContentXmlDto>(pageIndex+1, pageSize, query);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var dto = Database.Fetch<NodeDto>(sql).FirstOrDefault();
|
||||
var dto = Database.Fetch<NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
|
||||
return dto == null ? null : _modelFactory.BuildEntity(dto);
|
||||
}
|
||||
|
||||
@@ -2,24 +2,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Relators;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Dynamics;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -52,9 +47,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
|
||||
var dto = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
@@ -365,6 +360,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
foreach (var property in ((Member)entity).Properties)
|
||||
{
|
||||
if (keyDictionary.ContainsKey(property.PropertyTypeId) == false) continue;
|
||||
|
||||
property.Id = keyDictionary[property.PropertyTypeId];
|
||||
}
|
||||
}
|
||||
@@ -378,88 +375,57 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of VersionableRepositoryBase<IMembershipUser>
|
||||
|
||||
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
|
||||
public override IEnumerable<IMember> GetAllVersions(int id)
|
||||
{
|
||||
|
||||
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
|
||||
using (var tr = Database.GetTransaction())
|
||||
{
|
||||
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
|
||||
var subQuery = new Sql()
|
||||
.Select("DISTINCT cmsContentXml.nodeId")
|
||||
.From<ContentXmlDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
{
|
||||
var id1 = id;
|
||||
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
|
||||
var subQuery = new Sql()
|
||||
.Select("DISTINCT cmsContentXml.nodeId")
|
||||
.From<ContentXmlDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType)
|
||||
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
|
||||
|
||||
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
}
|
||||
|
||||
//now insert the data, again if something fails here, the whole transaction is reversed
|
||||
if (contentTypeIds == null)
|
||||
{
|
||||
var query = Query<IMember>.Builder;
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var contentTypeId in contentTypeIds)
|
||||
{
|
||||
//copy local
|
||||
var id = contentTypeId;
|
||||
var query = Query<IMember>.Builder.Where(x => x.ContentTypeId == id && x.Trashed == false);
|
||||
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
}
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
}
|
||||
|
||||
private void RebuildXmlStructuresProcessQuery(Func<IMember, XElement> serializer, IQuery<IMember> query, Transaction tr, int pageSize)
|
||||
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
|
||||
{
|
||||
var pageIndex = 0;
|
||||
var total = long.MinValue;
|
||||
var processed = 0;
|
||||
do
|
||||
// the previous way of doing this was to run it all in one big transaction,
|
||||
// and to bulk-insert groups of xml rows - which works, until the transaction
|
||||
// times out - and besides, because v7 transactions are ReadCommited, it does
|
||||
// not bring much safety - so this reverts to updating each record individually,
|
||||
// and it may be slower in the end, but should be more resilient.
|
||||
|
||||
var baseId = 0;
|
||||
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
|
||||
while (true)
|
||||
{
|
||||
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
|
||||
// get the next group of nodes
|
||||
var query = GetBaseQuery(false);
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
query = query
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
|
||||
// no more nodes, break
|
||||
if (xmlItems.Count == 0) break;
|
||||
|
||||
//bulk insert it into the database
|
||||
Database.BulkInsertRecords(xmlItems, tr);
|
||||
|
||||
processed += xmlItems.Length;
|
||||
|
||||
pageIndex++;
|
||||
} while (processed < total);
|
||||
foreach (var xmlItem in xmlItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
// InsertOrUpdate tries to update first, which is good since it is what
|
||||
// should happen in most cases, then it tries to insert, and it should work
|
||||
// unless the node has been deleted, and we just report the exception
|
||||
Database.InsertOrUpdate(xmlItem);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
|
||||
}
|
||||
}
|
||||
baseId = xmlItems.Last().NodeId;
|
||||
}
|
||||
}
|
||||
|
||||
public override IMember GetByVersion(Guid versionId)
|
||||
@@ -622,15 +588,38 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Func<Tuple<string, object[]>> filterCallback = null;
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
sbWhere.Append("AND ((umbracoNode. " + SqlSyntax.GetQuotedColumnName("text") + " LIKE @" + args.Count + ") " +
|
||||
"OR (cmsMember.LoginName LIKE @0" + args.Count + "))");
|
||||
args.Add("%" + filter + "%");
|
||||
//This will build up the where clause - even though the same 'filter' is being
|
||||
//applied to both columns, the parameters values passed to PetaPoco need to be
|
||||
//duplicated, otherwise it gets confused :/
|
||||
var columnFilters = new List<Tuple<string, string>>
|
||||
{
|
||||
new Tuple<string, string>("umbracoNode", "text"),
|
||||
new Tuple<string, string>("cmsMember", "LoginName")
|
||||
};
|
||||
sbWhere.Append("AND (");
|
||||
for (int i = 0; i < columnFilters.Count; i++)
|
||||
{
|
||||
sbWhere
|
||||
.Append("(")
|
||||
.Append(SqlSyntax.GetQuotedTableName(columnFilters[i].Item1))
|
||||
.Append(".")
|
||||
.Append(SqlSyntax.GetQuotedColumnName(columnFilters[i].Item2))
|
||||
.Append(" LIKE @")
|
||||
.Append(args.Count)
|
||||
.Append(") ");
|
||||
args.Add(string.Format("%{0}%", filter));
|
||||
if (i < (columnFilters.Count - 1))
|
||||
{
|
||||
sbWhere.Append("OR ");
|
||||
}
|
||||
}
|
||||
sbWhere.Append(")");
|
||||
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
|
||||
}
|
||||
|
||||
return GetPagedResultsByQuery<MemberDto, Member>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsMember", "nodeId"),
|
||||
ProcessQuery, orderBy, orderDirection, orderBySystemField,
|
||||
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
}
|
||||
|
||||
@@ -670,59 +659,65 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return base.GetEntityPropertyNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sql)
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
|
||||
{
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
|
||||
var ids = dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray();
|
||||
var content = new IMember[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
//content types
|
||||
var contentTypes = ids.Length == 0 ? Enumerable.Empty<IMemberType>() : _memberTypeRepository.GetAll(ids).ToArray();
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
|
||||
var dtosWithContentTypes = dtos
|
||||
//This select into and null check are required because we don't have a foreign damn key on the contentType column
|
||||
// http://issues.umbraco.org/issue/U4-5503
|
||||
.Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) })
|
||||
.Where(x => x.contentType != null)
|
||||
.ToArray();
|
||||
// if the cache contains the item, use it
|
||||
if (withCache)
|
||||
{
|
||||
var cached = RuntimeCache.GetCacheItem<IMember>(GetCacheIdKey<IMember>(dto.NodeId));
|
||||
if (cached != null)
|
||||
{
|
||||
content[i] = cached;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Go get the property data for each document
|
||||
IEnumerable<DocumentDefinition> docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition(
|
||||
d.dto.NodeId,
|
||||
d.dto.ContentVersionDto.VersionId,
|
||||
d.dto.ContentVersionDto.VersionDate,
|
||||
d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
d.contentType));
|
||||
// else, need to fetch from the database
|
||||
// content type repository is full-cache so OK to get each one independently
|
||||
var contentType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
|
||||
var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.NodeId);
|
||||
content[i] = factory.BuildEntity(dto);
|
||||
|
||||
var propertyData = GetPropertyCollection(sql, docDefs);
|
||||
// need properties
|
||||
defs.Add(new DocumentDefinition(
|
||||
dto.NodeId,
|
||||
dto.ContentVersionDto.VersionId,
|
||||
dto.ContentVersionDto.VersionDate,
|
||||
dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
|
||||
contentType
|
||||
));
|
||||
}
|
||||
|
||||
return dtosWithContentTypes.Select(d => CreateMemberFromDto(
|
||||
d.dto,
|
||||
contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId),
|
||||
propertyData[d.dto.NodeId]));
|
||||
}
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
|
||||
/// <summary>
|
||||
/// Private method to create a member object from a MemberDto
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="propCollection"></param>
|
||||
/// <returns></returns>
|
||||
private IMember CreateMemberFromDto(MemberDto dto,
|
||||
IMemberType contentType,
|
||||
PropertyCollection propCollection)
|
||||
{
|
||||
var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.ContentVersionDto.NodeId);
|
||||
var member = factory.BuildEntity(dto);
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
foreach (var def in defs)
|
||||
{
|
||||
// move to corresponding item (which has to exist)
|
||||
while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++;
|
||||
|
||||
member.Properties = propCollection;
|
||||
// complete the item
|
||||
var cc = content[dtoIndex];
|
||||
cc.Properties = propertyData[cc.Id];
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)member).ResetDirtyProperties(false);
|
||||
return member;
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
((Entity)cc).ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var dto = Database.First<MigrationDto>(sql);
|
||||
var dto = Database.Fetch<MigrationDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -18,6 +18,27 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public IEnumerable<Notification> GetUsersNotifications(IEnumerable<int> userIds, string action, IEnumerable<int> nodeIds, Guid objectType)
|
||||
{
|
||||
var nodeIdsA = nodeIds.ToArray();
|
||||
var syntax = ApplicationContext.Current.DatabaseContext.SqlSyntax; // bah
|
||||
var sql = new Sql()
|
||||
.Select("DISTINCT umbracoNode.id nodeId, umbracoUser.id userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action")
|
||||
.From<User2NodeNotifyDto>(syntax)
|
||||
.InnerJoin<NodeDto>(syntax).On<User2NodeNotifyDto, NodeDto>(syntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<UserDto>(syntax).On<User2NodeNotifyDto, UserDto>(syntax, left => left.UserId, right => right.Id)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == objectType)
|
||||
.Where<UserDto>(x => x.Disabled == false) // only approved users
|
||||
.Where<User2NodeNotifyDto>(x => x.Action == action); // on the specified action
|
||||
if (nodeIdsA.Length > 0)
|
||||
sql
|
||||
.WhereIn<NodeDto>(x => x.NodeId, nodeIdsA); // for the specified nodes
|
||||
sql
|
||||
.OrderBy<UserDto>(x => x.Id, syntax)
|
||||
.OrderBy<NodeDto>(dto => dto.NodeId, syntax);
|
||||
return _unitOfWork.Database.Fetch<dynamic>(sql).Select(x => new Notification(x.nodeId, x.userId, x.action, objectType));
|
||||
}
|
||||
|
||||
public IEnumerable<Notification> GetUserNotifications(IUser user)
|
||||
{
|
||||
var sql = new Sql()
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<PublicAccessEntry, Guid> CachePolicyFactory
|
||||
@@ -46,6 +45,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.Where("umbracoAccess.id IN (@ids)", new { ids = ids });
|
||||
}
|
||||
|
||||
sql.OrderBy<AccessDto>(x => x.NodeId, SqlSyntax);
|
||||
|
||||
var factory = new PublicAccessEntryFactory();
|
||||
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
|
||||
return dtos.Select(factory.BuildEntity);
|
||||
@@ -69,7 +70,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.From<AccessDto>(SqlSyntax)
|
||||
.LeftJoin<AccessRuleDto>(SqlSyntax)
|
||||
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
|
||||
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -162,7 +163,5 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return entity.Key;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id
|
||||
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
FormatDeleteStatement("umbracoAccess", "nodeId"),
|
||||
@"DELETE FROM umbracoRedirectUrl WHERE umbracoRedirectUrl.id IN(
|
||||
SELECT TB1.id FROM umbracoRedirectUrl as TB1
|
||||
INNER JOIN umbracoNode as TB2 ON TB1.contentKey = TB2.uniqueId
|
||||
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
FormatDeleteStatement("umbracoRelation", "parentId"),
|
||||
FormatDeleteStatement("umbracoRelation", "childId"),
|
||||
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
|
||||
|
||||
@@ -12,9 +12,9 @@ using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal class RedirectUrlRepository : PetaPocoRepositoryBase<int, IRedirectUrl>, IRedirectUrlRepository
|
||||
internal class RedirectUrlRepository : PetaPocoRepositoryBase<Guid, IRedirectUrl>, IRedirectUrlRepository
|
||||
{
|
||||
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{ }
|
||||
|
||||
@@ -23,19 +23,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
throw new NotSupportedException("This repository does not support this method.");
|
||||
}
|
||||
|
||||
protected override bool PerformExists(int id)
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return PerformGet(id) != null;
|
||||
}
|
||||
|
||||
protected override IRedirectUrl PerformGet(int id)
|
||||
protected override IRedirectUrl PerformGet(Guid id)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Id == id);
|
||||
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
|
||||
var dto = Database.Fetch<RedirectUrlDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
return dto == null ? null : Map(dto);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IRedirectUrl> PerformGetAll(params int[] ids)
|
||||
protected override IEnumerable<IRedirectUrl> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
if (ids.Length > 2000)
|
||||
throw new NotSupportedException("This repository does not support more than 2000 ids.");
|
||||
@@ -86,7 +86,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
{
|
||||
var dto = Map(entity);
|
||||
Database.Insert(dto);
|
||||
entity.Id = dto.Id;
|
||||
entity.Id = entity.Key.GetHashCode();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IRedirectUrl entity)
|
||||
@@ -101,11 +101,11 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
|
||||
return new RedirectUrlDto
|
||||
{
|
||||
Id = redirectUrl.Id,
|
||||
Id = redirectUrl.Key,
|
||||
ContentKey = redirectUrl.ContentKey,
|
||||
CreateDateUtc = redirectUrl.CreateDateUtc,
|
||||
Url = redirectUrl.Url,
|
||||
UrlHash = HashUrl(redirectUrl.Url)
|
||||
UrlHash = redirectUrl.Url.ToSHA1()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,7 +117,8 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
try
|
||||
{
|
||||
url.DisableChangeTracking();
|
||||
url.Id = dto.Id;
|
||||
url.Key = dto.Id;
|
||||
url.Id = dto.Id.GetHashCode();
|
||||
url.ContentId = dto.ContentId;
|
||||
url.ContentKey = dto.ContentKey;
|
||||
url.CreateDateUtc = dto.CreateDateUtc;
|
||||
@@ -132,7 +133,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
|
||||
public IRedirectUrl Get(string url, Guid contentKey)
|
||||
{
|
||||
var urlHash = HashUrl(url);
|
||||
var urlHash = url.ToSHA1();
|
||||
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash && x.ContentKey == contentKey);
|
||||
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
|
||||
return dto == null ? null : Map(dto);
|
||||
@@ -148,14 +149,14 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
Database.Execute("DELETE FROM umbracoRedirectUrl WHERE contentKey=@contentKey", new { contentKey });
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
Database.Delete<RedirectUrlDto>(id);
|
||||
}
|
||||
|
||||
public IRedirectUrl GetMostRecentUrl(string url)
|
||||
{
|
||||
var urlHash = HashUrl(url);
|
||||
var urlHash = url.ToSHA1();
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash)
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
|
||||
@@ -185,7 +186,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
public IEnumerable<IRedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where("umbracoNode.path LIKE @path", new { path = "%," + rootContentId + ",%" })
|
||||
.Where(string.Format("{0}.{1} LIKE @path", SqlSyntax.GetQuotedTableName("umbracoNode"), SqlSyntax.GetQuotedColumnName("path")), new { path = "%," + rootContentId + ",%" })
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
|
||||
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
|
||||
total = Convert.ToInt32(result.TotalItems);
|
||||
@@ -194,12 +195,16 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
return rules;
|
||||
}
|
||||
|
||||
private static string HashUrl(string url)
|
||||
public IEnumerable<IRedirectUrl> SearchUrls(string searchTerm, long pageIndex, int pageSize, out long total)
|
||||
{
|
||||
var crypto = new MD5CryptoServiceProvider();
|
||||
var inputBytes = Encoding.UTF8.GetBytes(url);
|
||||
var hashedBytes = crypto.ComputeHash(inputBytes);
|
||||
return Encoding.UTF8.GetString(hashedBytes);
|
||||
}
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(string.Format("{0}.{1} LIKE @url", SqlSyntax.GetQuotedTableName("umbracoRedirectUrl"), SqlSyntax.GetQuotedColumnName("Url")), new { url = "%" + searchTerm.Trim().ToLowerInvariant() + "%" })
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
|
||||
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
|
||||
total = Convert.ToInt32(result.TotalItems);
|
||||
|
||||
var rules = result.Items.Select(Map);
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user