Compare commits
706 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3cc5f9183 | |||
| 2b3397120f | |||
| ce99ec6d31 | |||
| 350f5c88c7 | |||
| 928c0dc535 | |||
| cc7a28db65 | |||
| 065e764957 | |||
| 637abc384b | |||
| 8959b81f6a | |||
| 32bb8ac5a4 | |||
| 016caba35f | |||
| d522dd7a31 | |||
| 95f632e1ea | |||
| e72ec171f7 | |||
| 2538e2e9c9 | |||
| b1ceeb23b3 | |||
| 579c572dd8 | |||
| bbf4c18825 | |||
| 9a94ac5317 | |||
| 0b904e82b5 | |||
| 51fec0c7d3 | |||
| 87ace12a4f | |||
| a1fd73cdb0 | |||
| 403502ef1b | |||
| c1769aa299 | |||
| 143d127fb9 | |||
| 01cf2e240a | |||
| 49689d5057 | |||
| f722d7731d | |||
| ac42f335c2 | |||
| f0eefbd5af | |||
| a8b660bfbf | |||
| dc7b5bae9b | |||
| 30b391f3fb | |||
| ff3b7963b1 | |||
| 84b380028c | |||
| f0b5c831f1 | |||
| d3232a09fb | |||
| 7cbaecc53a | |||
| e84a963d66 | |||
| 88d61a4d37 | |||
| a36184a261 | |||
| 75f4535157 | |||
| 9c50e1c48d | |||
| 78bf75615f | |||
| 9411a22a0d | |||
| 51649879cd | |||
| 461ce64feb | |||
| ab050eb7ac | |||
| c5a55b17e6 | |||
| fd577afe2c | |||
| f46ef0b006 | |||
| dc937ec942 | |||
| cefd9e7323 | |||
| 315690e758 | |||
| fb8c519f71 | |||
| 5df4d3509d | |||
| 4bd263e7bc | |||
| 916bc6badb | |||
| 2461924a89 | |||
| 581f15b172 | |||
| 33d7598d3c | |||
| 476773fce5 | |||
| 953355cddd | |||
| d2d43255aa | |||
| b39e218d51 | |||
| 4b581a2182 | |||
| 581eac5369 | |||
| 33cf983cae | |||
| 3ce6eb09aa | |||
| 963015cf4a | |||
| cc35af4eab | |||
| 349d89e4d7 | |||
| 1b3fc9fb21 | |||
| 2e98651d62 | |||
| 704e981284 | |||
| eda7f1e527 | |||
| 018a066f86 | |||
| e33a28ac19 | |||
| 0ef57d819c | |||
| c321d6dd44 | |||
| 5d74e61bcf | |||
| 8b39f495e5 | |||
| d468e346f1 | |||
| 2a10eed059 | |||
| 4ec04c779a | |||
| 26034fa33f | |||
| 97ddbdb1f0 | |||
| 731c51bfc7 | |||
| 78cb085e00 | |||
| f2ea4596d7 | |||
| f9d5de7af4 | |||
| 04c6fd0947 | |||
| 8bcf35734d | |||
| 4de7da2ac1 | |||
| 67d12ae2ca | |||
| e8ba069051 | |||
| a3457d2498 | |||
| 388d660e11 | |||
| b6dbc245e2 | |||
| 3efba6b576 | |||
| 54407a66a4 | |||
| bca46f11ad | |||
| a86b8f786d | |||
| ac3eaf43cb | |||
| f6d4170010 | |||
| 2b0ea45da9 | |||
| 07d05c9e0d | |||
| 26a5682a14 | |||
| f3fc2bcfb8 | |||
| c8d49ed5a7 | |||
| 9c2b0a3ef6 | |||
| 9355a81716 | |||
| 87575e9067 | |||
| a5b31e94b6 | |||
| f9e094a00a | |||
| 053175a5a6 | |||
| ce7488a924 | |||
| f442599633 | |||
| 37b6b42570 | |||
| f1149fb826 | |||
| 3f8f96e8c3 | |||
| a75181bd7f | |||
| 00b469b61b | |||
| 83f08538c2 | |||
| ef91e152b4 | |||
| add0685d3f | |||
| 27fc3a9bb3 | |||
| ac391f3bcd | |||
| 0700e37830 | |||
| 5f4985b4eb | |||
| d179792cc3 | |||
| 4cb7b29f60 | |||
| fb2f9cea64 | |||
| 394f21f4be | |||
| 32cd9d44cb | |||
| 237b7176bf | |||
| 1fa2c16668 | |||
| 1c5e2b5f73 | |||
| 30af8cb928 | |||
| 65b44137d4 | |||
| 41eeb38c5f | |||
| 4356d3f94a | |||
| ba072aec7c | |||
| f1d3b1a6d3 | |||
| 45f9919e57 | |||
| 857b7cc608 | |||
| c23160f915 | |||
| 3e3a180fe4 | |||
| c55cf75ada | |||
| 23950c0cac | |||
| 082e399293 | |||
| c4ab25f1ac | |||
| 3f55abaf59 | |||
| 001e15f83c | |||
| 34e3257ddb | |||
| 2b64cfb4b9 | |||
| a499744084 | |||
| c72ecda18c | |||
| 3423d8b2cc | |||
| 37c4fc5f74 | |||
| 406a206b03 | |||
| 55e0af118a | |||
| 718ed19fb9 | |||
| 32999b7e19 | |||
| ce63251206 | |||
| 7b641f4771 | |||
| 708d060c3b | |||
| e254dddbd4 | |||
| 87c2c5455c | |||
| 8544aa685b | |||
| 09c23e93c2 | |||
| 9946d36d00 | |||
| 7012633fdd | |||
| 6e348a3a1a | |||
| 733c920867 | |||
| 35fc997d9f | |||
| 921e8a2529 | |||
| 22bd6cd989 | |||
| 0713f12ba6 | |||
| dad4eafebd | |||
| 96c9ecbdd7 | |||
| e7e04de22e | |||
| 7003ef8d26 | |||
| 49a48bbfce | |||
| 663d9fcfe8 | |||
| d6f8b878d2 | |||
| 598944d3d8 | |||
| 3caecf5161 | |||
| b02b2a61f0 | |||
| 60f7211a80 | |||
| 56dc10a4f7 | |||
| dbda6689b6 | |||
| 8d6c8ef282 | |||
| df8c75438a | |||
| 2d53da55a7 | |||
| 18d0fec014 | |||
| 851d587d1f | |||
| 6a3f94d0e1 | |||
| b1358936b4 | |||
| b2798e38f7 | |||
| a603fd0c46 | |||
| 4b5c49907c | |||
| 1578bc8cc7 | |||
| e7c231c5a8 | |||
| a9cda9d651 | |||
| ad19eb8dff | |||
| e511554a42 | |||
| 84fd5dc2ee | |||
| 344b52d10f | |||
| 2121b7c8a0 | |||
| 895faf41b1 | |||
| 9a06c3a5ff | |||
| 44102df36f | |||
| 207d6b53b5 | |||
| 972d5f8296 | |||
| 617a91c57e | |||
| cb0e5414b9 | |||
| 22cef52261 | |||
| 41e419a49e | |||
| efbe97a01d | |||
| 0babb7f28a | |||
| 5ba801386c | |||
| 73937172dc | |||
| 8c1996a7a1 | |||
| 039ab8f1d2 | |||
| 8c998fd4e6 | |||
| 88f332f5c7 | |||
| 06a87bc341 | |||
| b10e646ade | |||
| 110a3b2a12 | |||
| 1bda9f32c1 | |||
| ce8390768e | |||
| 7ccfc79080 | |||
| ed850629da | |||
| 1307cb1173 | |||
| a1b9842215 | |||
| d300bf8d6d | |||
| 70231fcee2 | |||
| 78e66ce25f | |||
| dcc0374464 | |||
| a95afa610f | |||
| 13e2112c3c | |||
| cf10f5f0ee | |||
| ca00246236 | |||
| d71b8a139f | |||
| 6bf0d6e175 | |||
| aa5d7fc9fc | |||
| 5cbe2b6ce0 | |||
| 7a0c6ab975 | |||
| 68fd74897f | |||
| 047a41c05e | |||
| 49d5005453 | |||
| f2db0fe80e | |||
| 9917fb20ca | |||
| 0089f79bd7 | |||
| c468e4736e | |||
| 84f684ed31 | |||
| e71e9d7efb | |||
| 93cd03da28 | |||
| 1dade7bfd2 | |||
| c3b1aa36b8 | |||
| 9cc466775d | |||
| d0a91bf98b | |||
| 085c06e043 | |||
| 1e45f85f7c | |||
| a0785f2e0d | |||
| 583302faa2 | |||
| 4404ce94c5 | |||
| 86bc701511 | |||
| 54f86c06f0 | |||
| a86902bd91 | |||
| 7bdf76ffa7 | |||
| 528656a337 | |||
| 5ef46ad8df | |||
| 168dc0e5da | |||
| 5a1fc91f3e | |||
| 5e6b5a5cc1 | |||
| 7213e3a6a0 | |||
| 217ca62ea0 | |||
| d216d5755c | |||
| 8db748b738 | |||
| 300c9aa258 | |||
| f7097d571c | |||
| b151c39c3b | |||
| bb01eca7b7 | |||
| 542406c402 | |||
| c44b903261 | |||
| c68d7a33ca | |||
| 2773102441 | |||
| ef0d6eff2c | |||
| 1efad78214 | |||
| 46b2d8a4aa | |||
| 7a5cb7058b | |||
| 13f76d2af9 | |||
| c1c2339827 | |||
| 0e273d1a08 | |||
| 125c378274 | |||
| ca80a0cddc | |||
| 21707f9e08 | |||
| d8cfd8f12e | |||
| 08150c05e9 | |||
| 688f8c4170 | |||
| 31cbb79ede | |||
| 305ce84ef5 | |||
| c6d7056a64 | |||
| 7db1055a0d | |||
| d625388786 | |||
| fa1663ed51 | |||
| ca0de5acd3 | |||
| 27168722ab | |||
| babcbd2f7a | |||
| ba82885b6d | |||
| 6dbeba0763 | |||
| 00e0f83c8e | |||
| 68ba0ca1e2 | |||
| b22e9d0911 | |||
| 6a59a90964 | |||
| 26a042f291 | |||
| 136b3db098 | |||
| 21f46d0a3c | |||
| 7fafea2370 | |||
| 86c37c7f6d | |||
| 7dd9f3e766 | |||
| 6c91d54372 | |||
| d4a2244190 | |||
| 1ccc8cc161 | |||
| c5e7e1e39e | |||
| db2a35dbce | |||
| 3d006f1e08 | |||
| e0c891719f | |||
| 89a14c13e5 | |||
| ca9aa21c7b | |||
| 14de3fee46 | |||
| c0eaeb3168 | |||
| 74e16c0f8d | |||
| 17d3548bbb | |||
| 9754c4511b | |||
| 442d359ab0 | |||
| bbed2fa8f6 | |||
| 3089a0ab3a | |||
| f8fac9f84e | |||
| 83ce359234 | |||
| 2c64f91b25 | |||
| e702fe2877 | |||
| 9d55656ae9 | |||
| d57de05453 | |||
| 11e4bf5de8 | |||
| 82661d9af4 | |||
| 56cff6bba8 | |||
| 6b289e1a7d | |||
| 50d7114209 | |||
| 91fa583f62 | |||
| 23d91fd350 | |||
| d4714e1580 | |||
| 2b22e6a784 | |||
| 59840926c8 | |||
| 338c7cc5a6 | |||
| 77850aea18 | |||
| 895f0a9dfe | |||
| ec03a3dc42 | |||
| 5a4dbba21b | |||
| 04546dd827 | |||
| 4ecf1f919e | |||
| bdc4e1ae6a | |||
| 3cb05870af | |||
| 95b0c342bb | |||
| 3a5584a1c1 | |||
| b425edbb45 | |||
| b403283368 | |||
| cb1d347769 | |||
| b3ac3a03f3 | |||
| 4d0a2c85b7 | |||
| e1957b895f | |||
| bd0ca43b83 | |||
| f39531216c | |||
| 84de083d52 | |||
| 6ccb822cb5 | |||
| c94338d2b6 | |||
| 3ba001164f | |||
| a8c0367492 | |||
| 91646212fd | |||
| 4c9b2f552a | |||
| f24effdf0e | |||
| e9a4321ee8 | |||
| c696c62185 | |||
| 0dfbb95033 | |||
| 17969e4537 | |||
| 3395445d7c | |||
| 374fdcf29e | |||
| 9ea2fa9d8c | |||
| bcb19eea35 | |||
| da00ad1444 | |||
| 7bc52b3948 | |||
| a77aec4156 | |||
| f7f3dccfc8 | |||
| ab3469d703 | |||
| c78ea90cf5 | |||
| 3de0b85ae3 | |||
| cddc6f3116 | |||
| 769f0d97d2 | |||
| 87b51d1418 | |||
| 8ea1af56ab | |||
| 219eb61194 | |||
| 1d3279fe07 | |||
| 88bb796f4d | |||
| 97878ac25a | |||
| c87e880b64 | |||
| f7d11bf936 | |||
| 0fc868c9ba | |||
| a3f4275b30 | |||
| cfd3301965 | |||
| e28ba446a0 | |||
| c645a2cf15 | |||
| 5fb8d990f8 | |||
| 8626e4337f | |||
| 0f22c41eee | |||
| e73fda14b5 | |||
| a04df8070a | |||
| 60c1052db1 | |||
| dd041a6f09 | |||
| 733aab6eb5 | |||
| 48041c3163 | |||
| 1e6d50a86b | |||
| 0445588c6b | |||
| 4d316cf0ce | |||
| 864650193a | |||
| cfa7b0a092 | |||
| 5726420848 | |||
| cc57cd6eb7 | |||
| 193eb259a6 | |||
| 6db18d83be | |||
| ca752f74bc | |||
| b840a0992c | |||
| 51997d0651 | |||
| 9482104ec5 | |||
| 7053c175f8 | |||
| b2116480fc | |||
| caef6cc623 | |||
| 280ad246ab | |||
| 43e0ef2472 | |||
| 44cfe5da84 | |||
| d5fd266b19 | |||
| 5768992ccc | |||
| 872530296f | |||
| 81aae79b42 | |||
| 9edbb3fc26 | |||
| 7fdd2bcdfc | |||
| ba90fdeb56 | |||
| d6bae1b58f | |||
| ef53db52f7 | |||
| 863b97039c | |||
| 1a1adbf647 | |||
| 5c745565da | |||
| 5dec54badb | |||
| e5e725b376 | |||
| f0f25c67c6 | |||
| da8c0726b1 | |||
| 2fbb9721fa | |||
| de6ffd456c | |||
| 5b3ff58ad8 | |||
| 71b5e5223f | |||
| 9a4c1972b2 | |||
| b993ce953b | |||
| f3935b5007 | |||
| dde0f1aa36 | |||
| 3fd6754057 | |||
| 8fbb459233 | |||
| e35a748c92 | |||
| cb6f934792 | |||
| 7adbecf9a1 | |||
| 2fdf148610 | |||
| 9d9e5e8274 | |||
| 89895dd592 | |||
| f9252372ea | |||
| 02a2178029 | |||
| ee775a2fde | |||
| 22af206626 | |||
| 67e109c0e6 | |||
| 8a4d85771b | |||
| 4688ac104e | |||
| 032dbe5bf5 | |||
| a7695163b9 | |||
| cb4569677e | |||
| 4be704b968 | |||
| b7a5cb046a | |||
| eb52dafb33 | |||
| af382c171b | |||
| 54fdfdd639 | |||
| c4c99ef0b8 | |||
| 0bd233815e | |||
| 4c9fd4029f | |||
| 3d65afabbf | |||
| ef9bf155ef | |||
| e947f661a9 | |||
| 0eb8ebd82e | |||
| bc76fa8687 | |||
| 8681d535f2 | |||
| 6d81705ece | |||
| b433af8611 | |||
| b0f4d0e16b | |||
| 2f24dfb8f0 | |||
| ad63050078 | |||
| 552c2ed4dc | |||
| d7e522132e | |||
| ca7c43d944 | |||
| c35e842391 | |||
| dae20cc986 | |||
| c466514e7f | |||
| 5e2aa7fa68 | |||
| 11ccc88bc8 | |||
| c83009c879 | |||
| d6b9b41776 | |||
| 508a4c32b4 | |||
| dbf2538aae | |||
| 5293b3bcc7 | |||
| 940caba2c4 | |||
| e31daef4fa | |||
| 7984626433 | |||
| ffa557b097 | |||
| 9236bcbcd8 | |||
| 1b875b0c67 | |||
| ecce68da0a | |||
| e27a882d2b | |||
| c7ae379fd1 | |||
| 8ad13f6853 | |||
| e4ae40c2fe | |||
| be61270e42 | |||
| bd00b532eb | |||
| ebc9478f2d | |||
| ab72808a10 | |||
| 068e82172a | |||
| 45988d4f0c | |||
| db330b3223 | |||
| 03fd06cfa7 | |||
| 775148d241 | |||
| 8e65ecdc4a | |||
| 6a43dd41c6 | |||
| 82ae6a1aef | |||
| a127683d92 | |||
| 06f3f4b191 | |||
| a2f45a38f7 | |||
| 4b966ebe91 | |||
| da9e87d0ab | |||
| 84a206ce5e | |||
| 3c3c31c36e | |||
| 585f8e07f7 | |||
| f7c0e02bfd | |||
| e767eaa08b | |||
| ef32eda874 | |||
| 01ccf8a6a6 | |||
| c27ca1fb0d | |||
| 68e287cf12 | |||
| ad6946aa26 | |||
| 42d7c83a6d | |||
| 1a435148d8 | |||
| de348a9946 | |||
| 791e6a5f92 | |||
| 1f6f8df5b5 | |||
| 7f5eff6e53 | |||
| d885265818 | |||
| be6140a38b | |||
| 8ee05063a2 | |||
| 7920383904 | |||
| 1a3d9e27f4 | |||
| 2654f9189f | |||
| d7453b5a6e | |||
| 5ed8ceb85e | |||
| b7ea0840a9 | |||
| 6b65f1c197 | |||
| bc7dc8629e | |||
| d3c2b0a247 | |||
| 464711d3c8 | |||
| a3af1d478d | |||
| 7d8fdff6c1 | |||
| b15fd4496a | |||
| a813c0bfea | |||
| b3ea0c83f9 | |||
| c1aefe0686 | |||
| 907dd8092a | |||
| 4870554f47 | |||
| 7b5568ecb4 | |||
| 2fd383960d | |||
| 33f76d25f7 | |||
| a2c5c35394 | |||
| c0616a37bf | |||
| da4be3be65 | |||
| 08c4a75af7 | |||
| 047eb893eb | |||
| 308c381e17 | |||
| e99309aa2f | |||
| 917a2b78dd | |||
| e559a6d485 | |||
| 160518e977 | |||
| f985725118 | |||
| 5ec67ab3fd | |||
| 993513b138 | |||
| 7f75802e4e | |||
| 5eb330b7d7 | |||
| 547767fc0f | |||
| ce618d289b | |||
| 2191d33844 | |||
| 92a507bc87 | |||
| 74126134b1 | |||
| 6136118699 | |||
| 114b58c5ca | |||
| 3019d67922 | |||
| 1b844421fc | |||
| df9c6baed7 | |||
| 97518c837f | |||
| 727250d2ab | |||
| 66e3a00e56 | |||
| 53357b0582 | |||
| 1164f1cdbb | |||
| aebea2da31 | |||
| 1634789641 | |||
| 7f05b80a22 | |||
| 20239a9b71 | |||
| 70a0bbbe61 | |||
| c4b07cc3ae | |||
| 771935b8a5 | |||
| 73bb401f81 | |||
| 8ccbe4c1e4 | |||
| cf3343749f | |||
| 079df4a8b7 | |||
| 7612884705 | |||
| 74dc2d80fd | |||
| 647ebee34d | |||
| bc444847f4 | |||
| 24d9c60bdf | |||
| b5c2fc82bb | |||
| a50256d381 | |||
| 290f34f034 | |||
| 8b299a070f | |||
| 782193b17a | |||
| 4b945f355a | |||
| 3cf8ef9513 | |||
| 65654de06a | |||
| 2334596bf3 | |||
| 1d664bce52 | |||
| 9a515de43f | |||
| b55d065d45 | |||
| 83db63c5a8 | |||
| 1fb0b95428 | |||
| 53e23f09dc | |||
| 9e781173fc | |||
| fead30db38 | |||
| d8811d6b46 | |||
| dd40dd964c | |||
| bc51a98cfd | |||
| 75e375ff54 | |||
| 1a29a3237d | |||
| 631a9f92ea | |||
| 229a15be4f | |||
| 28d7a4a6dc | |||
| 21f003e705 | |||
| 0d80bf6a84 | |||
| e88f5c8fe2 | |||
| 7cb1a497e5 | |||
| b72c8ef953 | |||
| 04a09bd4d6 | |||
| 311fcfe328 | |||
| 33d09b14be | |||
| cdab89062e | |||
| 38571f2cdd | |||
| fcd90aa702 | |||
| ae17172126 | |||
| ecca480334 | |||
| 095a7c0aa7 | |||
| 8d8bb4e237 | |||
| 6772b1de35 | |||
| 169c6b6372 | |||
| 8ab2f7c257 | |||
| 51215f11af | |||
| 3df103eae2 | |||
| ea13989b7e | |||
| f726dd59bf | |||
| 1757e7b4c8 | |||
| a68eb91c14 | |||
| 7c9476ca41 | |||
| 5cee5a27c9 | |||
| 1e34570a1f | |||
| 7e930330e2 | |||
| cc3355b1f9 | |||
| ee9deacf92 | |||
| 4e9a8623f0 | |||
| 904265cdf5 | |||
| 4a191f0711 | |||
| 6be9a25d99 | |||
| 9589500412 | |||
| 1c3b870ad2 | |||
| 6a5002495c | |||
| 89cbe53b63 | |||
| b3938cd7b2 | |||
| 3ce3a6f6ea | |||
| b03e9f8fd5 | |||
| 1e73bb12ac | |||
| 662bf16a4f | |||
| 26d450ad9a | |||
| bab4c7715d | |||
| 27f121f25c | |||
| a58bf507ed | |||
| b199f4072c | |||
| 53928c02f8 | |||
| 756d554b47 | |||
| fb4de621c0 |
+6
-11
@@ -46,13 +46,7 @@ src/Umbraco.Web.UI/Web.*.config.transformed
|
||||
|
||||
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
|
||||
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
|
||||
_BuildOutput/*
|
||||
*.ncrunchsolution
|
||||
build/UmbracoCms.AllBinaries*zip
|
||||
build/UmbracoCms.WebPI*zip
|
||||
build/UmbracoCms*zip
|
||||
build/UmbracoExamine.PDF*zip
|
||||
build/*.nupkg
|
||||
src/Umbraco.Tests/config/applications.config
|
||||
src/Umbraco.Tests/config/trees.config
|
||||
src/Umbraco.Web.UI/web.config
|
||||
@@ -68,9 +62,9 @@ src/packages/repositories.config
|
||||
|
||||
src/Umbraco.Web.UI/[Ww]eb.config
|
||||
*.transformed
|
||||
webpihash.txt
|
||||
|
||||
node_modules
|
||||
lib-bower
|
||||
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
|
||||
@@ -95,7 +89,6 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
|
||||
src/Umbraco.Web.UI.Client/[Bb]uild/*
|
||||
src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
|
||||
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
|
||||
build/_BuildOutput/
|
||||
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
|
||||
tools/NDepend/
|
||||
|
||||
@@ -130,7 +123,6 @@ src/*.boltdata/
|
||||
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
|
||||
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
|
||||
src/umbraco.sln.ide/*
|
||||
build/UmbracoCms.*/
|
||||
src/.vs/
|
||||
src/Umbraco.Web.UI/umbraco/js/install.loader.js
|
||||
src/Umbraco.Tests/media
|
||||
@@ -140,8 +132,11 @@ apidocs/api/*
|
||||
build/docs.zip
|
||||
build/ui-docs.zip
|
||||
build/csharp-docs.zip
|
||||
build/msbuild.log
|
||||
.vs/
|
||||
src/packages/
|
||||
build/tools/
|
||||
src/PrecompiledWeb/*
|
||||
|
||||
|
||||
build.out/
|
||||
build.tmp/
|
||||
build/Modules/*/temp/
|
||||
@@ -0,0 +1,149 @@
|
||||
Umbraco Cms Build
|
||||
--
|
||||
----
|
||||
|
||||
# Quick!
|
||||
|
||||
To build Umbraco, fire PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `README.md`...). There, trigger the build with the following command:
|
||||
|
||||
build\build.ps1
|
||||
|
||||
By default, this builds the current version. It is possible to specify a different version as a parameter to the build script:
|
||||
|
||||
build\build.ps1 7.6.44
|
||||
|
||||
Valid version strings are defined in the `Set-UmbracoVersion` documentation below.
|
||||
|
||||
## Notes
|
||||
|
||||
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).
|
||||
|
||||
# Build
|
||||
|
||||
The Umbraco Build solution relies on a PowerShell module. The module needs to be imported into PowerShell. From within Umbraco's repository root:
|
||||
|
||||
build\build.ps1 -ModuleOnly
|
||||
|
||||
Or the abbreviated form:
|
||||
|
||||
build\build.ps1 -mo
|
||||
|
||||
Once the module has been imported, a set of commands are added to PowerShell.
|
||||
|
||||
## Get-UmbracoBuildEnv
|
||||
|
||||
Gets the Umbraco build environment ie NuGet, Semver, Visual Studio, etc. Downloads things that can be downloaded such as NuGet. Examples:
|
||||
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
Write-Host $uenv.SolutionRoot
|
||||
&$uenv.NuGet help
|
||||
|
||||
The object exposes the following properties:
|
||||
|
||||
* `SolutionRoot`: the absolute path to the solution root
|
||||
* `VisualStudio`: a Visual Studio object (see below)
|
||||
* `NuGet`: the absolute path to the NuGet executable
|
||||
* `Zip`: the absolute path to the 7Zip executable
|
||||
* `VsWhere`: the absolute path to the VsWhere executable
|
||||
* `NodePath`: the absolute path to the Node install
|
||||
* `NpmPath`: the absolute path to the Npm install
|
||||
|
||||
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
|
||||
|
||||
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
|
||||
* `Major`: Visual Studio major version (eg `15` for VS 2017)
|
||||
* `Minor`: Visual Studio minor version
|
||||
* `MsBUild`: the absolute path to the MsBuild executable
|
||||
|
||||
## Get-UmbracoVersion
|
||||
|
||||
Gets an object representing the current Umbraco version. Example:
|
||||
|
||||
$v = Get-UmbracoVersion
|
||||
Write-Host $v.Semver
|
||||
|
||||
The object exposes the following properties:
|
||||
|
||||
* `Semver`: the semver object representing the version
|
||||
* `Release`: the main part of the version (eg `7.6.33`)
|
||||
* `Comment`: the pre release part of the version (eg `alpha02`)
|
||||
* `Build`: the build number part of the version (eg `1234`)
|
||||
|
||||
## Set-UmbracoVersion
|
||||
|
||||
Modifies Umbraco files with the new version.
|
||||
|
||||
>This entirely replaces the legacy `UmbracoVersion.txt` file.
|
||||
|
||||
The version must be a valid semver version. It can include a *pre release* part (eg `alpha02`) and/or a *build number* (eg `1234`). Examples:
|
||||
|
||||
Set-UmbracoVersion 7.6.33
|
||||
Set-UmbracoVersion 7.6.33-alpha02
|
||||
Set-UmbracoVersion 7.6.33+1234
|
||||
Set-UmbracoVersion 7.6.33-beta05+5678
|
||||
|
||||
Note that `Set-UmbracoVersion` enforces a slightly more restrictive naming scheme than what semver would tolerate. The pre release part can only be composed of a-z and 0-9, therefore `alpha033` is considered valid but not `alpha.033` nor `alpha033-preview` nor `RC2` (would need to be lowercased `rc2`).
|
||||
|
||||
>It is considered best to add trailing zeroes to pre releases, else NuGet gets the order of versions wrong. So if you plan to have more than 10, but no more that 100 alpha versions, number the versions `alpha00`, `alpha01`, etc.
|
||||
|
||||
## Build-Umbraco
|
||||
|
||||
Builds Umbraco. Temporary files are generated in `build.tmp` while the actual artifacts (zip files, NuGet packages...) are produced in `build.out`. Example:
|
||||
|
||||
Build-Umbraco
|
||||
|
||||
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
|
||||
|
||||
### web.config
|
||||
|
||||
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
|
||||
|
||||
## Build-UmbracoDocs
|
||||
|
||||
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
|
||||
|
||||
Build-UmbracoDocs
|
||||
|
||||
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
|
||||
|
||||
## Verify-NuGet
|
||||
|
||||
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
|
||||
|
||||
Verify-NuGet
|
||||
|
||||
# VSTS
|
||||
|
||||
Continuous integration, nightly builds and release builds run on VSTS.
|
||||
|
||||
VSTS uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
|
||||
|
||||
* `pre-build`: prepares the build
|
||||
* `compile-belle`: compiles Belle
|
||||
* `compile-umbraco`: compiles Umbraco
|
||||
* `pre-tests`: prepares the tests
|
||||
* `compile-tests`: compiles the tests
|
||||
* `pre-packages`: prepares the packages
|
||||
* `pkg-zip`: creates the zip files
|
||||
* `pre-nuget`: prepares NuGet packages
|
||||
* `pkg-nuget`: creates NuGet packages
|
||||
|
||||
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated VSTS tasks. Similarly, creating the NuGet packages is also performed by dedicated VSTS tasks.
|
||||
|
||||
Finally, the produced artifacts are published in two containers that can be downloaded from VSTS: `zips` contains the zip files while `nuget` contains the NuGet packages.
|
||||
|
||||
>During a VSTS build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in VSTS tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with VSTS at the moment.
|
||||
|
||||
# Notes
|
||||
|
||||
*This part needs to be cleaned up*
|
||||
|
||||
Nightlies should use some sort of build number.
|
||||
|
||||
We should increment versions as soon as a version is released. Ie, as soon as `7.6.33` is released, we should `Set-UmbracoVersion 7.6.34-alpha` and push.
|
||||
|
||||
NuGet / NuSpec consistency checks are performed in tests. We should move it so it is done as part of the PowerShell script even before we try to compile and run the tests.
|
||||
|
||||
There are still a few commands in `build` (to build docs, install Git or cleanup the install) that will need to be migrated to PowerShell.
|
||||
|
||||
/eof
|
||||
@@ -12,9 +12,7 @@ Umbraco is a free open source Content Management System built on the ASP.NET pla
|
||||
|
||||
## Building Umbraco from source ##
|
||||
|
||||
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt vs` in `src\Umbraco.Web.UI.Client`.
|
||||
|
||||
If you're interested in making changes to Belle without running Visual Studio make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md).
|
||||
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 `gulp dev` in `src\Umbraco.Web.UI.Client`.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
version: '{build}'
|
||||
shallow_clone: true
|
||||
|
||||
init:
|
||||
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
build_script:
|
||||
- cmd: >-
|
||||
SET SLN=%CD%
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
@ECHO OFF
|
||||
powershell .\build\build.ps1
|
||||
|
||||
IF ERRORLEVEL 1 (
|
||||
GOTO :error
|
||||
) ELSE (
|
||||
GOTO :EOF
|
||||
)
|
||||
|
||||
:error
|
||||
ECHO.
|
||||
ECHO Can not run build\build.ps1.
|
||||
ECHO If this is due to a SecurityError then make sure to run the following command from an administrator command prompt:
|
||||
ECHO.
|
||||
ECHO powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
|
||||
-217
@@ -1,217 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: UMBRACO BUILD FILE
|
||||
|
||||
|
||||
:: ensure we have UmbracoVersion.txt
|
||||
IF NOT EXIST UmbracoVersion.txt (
|
||||
ECHO UmbracoVersion.txt is missing!
|
||||
GOTO error
|
||||
)
|
||||
|
||||
REM Get the version and comment from UmbracoVersion.txt lines 2 and 3
|
||||
SET RELEASE=
|
||||
SET COMMENT=
|
||||
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED RELEASE SET RELEASE=%%i
|
||||
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED COMMENT SET COMMENT=%%i
|
||||
|
||||
REM process args
|
||||
|
||||
SET INTEGRATION=0
|
||||
SET nuGetFolder=%CD%\..\src\packages
|
||||
SET SKIPNUGET=0
|
||||
|
||||
:processArgs
|
||||
|
||||
:: grab the first parameter as a whole eg "/action:start"
|
||||
:: end if no more parameter
|
||||
SET SWITCHPARSE=%1
|
||||
IF [%SWITCHPARSE%] == [] goto endProcessArgs
|
||||
|
||||
:: get switch and value
|
||||
SET SWITCH=
|
||||
SET VALUE=
|
||||
FOR /F "tokens=1,* delims=: " %%a IN ("%SWITCHPARSE%") DO SET SWITCH=%%a& SET VALUE=%%b
|
||||
|
||||
:: route arg
|
||||
IF '%SWITCH%'=='/release' GOTO argRelease
|
||||
IF '%SWITCH%'=='-release' GOTO argRelease
|
||||
IF '%SWITCH%'=='/comment' GOTO argComment
|
||||
IF '%SWITCH%'=='-comment' GOTO argComment
|
||||
IF '%SWITCH%'=='/integration' GOTO argIntegration
|
||||
IF '%SWITCH%'=='-integration' GOTO argIntegration
|
||||
IF '%SWITCH%'=='/nugetfolder' GOTO argNugetFolder
|
||||
IF '%SWITCH%'=='-nugetfolder' GOTO argNugetFolder
|
||||
IF '%SWITCH%'=='/skipnuget' GOTO argSkipNuget
|
||||
IF '%SWITCH%'=='-skipnuget' GOTO argSkipNuget
|
||||
ECHO "Invalid switch %SWITCH%"
|
||||
GOTO error
|
||||
|
||||
:: handle each arg
|
||||
|
||||
:argRelease
|
||||
set RELEASE=%VALUE%
|
||||
SHIFT
|
||||
goto processArgs
|
||||
|
||||
:argComment
|
||||
SET COMMENT=%VALUE%
|
||||
SHIFT
|
||||
GOTO processArgs
|
||||
|
||||
:argIntegration
|
||||
SET INTEGRATION=1
|
||||
SHIFT
|
||||
GOTO processArgs
|
||||
|
||||
:argNugetFolder
|
||||
SET nuGetFolder=%VALUE%
|
||||
SHIFT
|
||||
GOTO processArgs
|
||||
|
||||
:argSkipNuget
|
||||
SET SKIPNUGET=1
|
||||
SHIFT
|
||||
GOTO processArgs
|
||||
|
||||
:endProcessArgs
|
||||
|
||||
REM run
|
||||
|
||||
SET VERSION=%RELEASE%
|
||||
IF [%COMMENT%] EQU [] (SET VERSION=%RELEASE%) ELSE (SET VERSION=%RELEASE%-%COMMENT%)
|
||||
|
||||
ECHO ################################################################
|
||||
ECHO Building Umbraco %VERSION%
|
||||
ECHO ################################################################
|
||||
|
||||
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
|
||||
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
|
||||
SET PATH="%MSBUILDPATH%";%PATH%
|
||||
|
||||
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %RELEASE%
|
||||
|
||||
ECHO.
|
||||
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
|
||||
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
|
||||
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
|
||||
|
||||
ECHO.
|
||||
ECHO Removing existing built files to make sure everything is clean as a whistle
|
||||
RMDIR /Q /S _BuildOutput
|
||||
DEL /F /Q UmbracoCms.*.zip 2>NUL
|
||||
DEL /F /Q UmbracoExamine.*.zip 2>NUL
|
||||
DEL /F /Q UmbracoCms.*.nupkg 2>NUL
|
||||
DEL /F /Q webpihash.txt 2>NUL
|
||||
|
||||
ECHO.
|
||||
ECHO Making sure Git is in the path so that the build can succeed
|
||||
CALL InstallGit.cmd
|
||||
|
||||
REM Adding the default Git path so that if it's installed it can actually be found
|
||||
REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might find Git,
|
||||
REM but the path setting is lost due to SETLOCAL
|
||||
SET PATH="C:\Program Files (x86)\Git\cmd";"C:\Program Files\Git\cmd";%PATH%
|
||||
|
||||
SET toolsFolder=%CD%\tools\
|
||||
IF NOT EXIST "%toolsFolder%" (
|
||||
MD tools
|
||||
)
|
||||
|
||||
SET nuGetExecutable=%CD%\tools\nuget.exe
|
||||
IF NOT EXIST "%nuGetExecutable%" (
|
||||
ECHO Getting NuGet so we can fetch some tools
|
||||
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
|
||||
)
|
||||
|
||||
:: We need 7za.exe for BuildBelle.bat
|
||||
IF NOT EXIST "%toolsFolder%7za.exe" (
|
||||
ECHO 7zip not found - fetching now
|
||||
"%nuGetExecutable%" install 7-Zip.CommandLine -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
:: We need vswhere.exe for VS2017+
|
||||
IF NOT EXIST "%toolsFolder%vswhere.exe" (
|
||||
ECHO vswhere not found - fetching now
|
||||
"%nuGetExecutable%" install vswhere -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
:: Put 7za.exe and vswhere.exe in a predictable path (not version specific)
|
||||
FOR /f "delims=" %%A in ('dir "%toolsFolder%7-Zip.CommandLine.*" /b') DO SET "sevenZipExePath=%toolsFolder%%%A\"
|
||||
MOVE "%sevenZipExePath%tools\7za.exe" "%toolsFolder%7za.exe"
|
||||
|
||||
FOR /f "delims=" %%A in ('dir "%toolsFolder%vswhere.*" /b') DO SET "vswhereExePath=%toolsFolder%%%A\"
|
||||
MOVE "%vswhereExePath%tools\vswhere.exe" "%toolsFolder%vswhere.exe"
|
||||
|
||||
ECHO.
|
||||
ECHO Making sure we have a web.config
|
||||
IF NOT EXIST "%CD%\..\src\Umbraco.Web.UI\web.config" COPY "%CD%\..\src\Umbraco.Web.UI\web.Template.config" "%CD%\..\src\Umbraco.Web.UI\web.config"
|
||||
|
||||
for /f "usebackq tokens=1* delims=: " %%i in (`"%CD%\tools\vswhere.exe" -latest -requires Microsoft.Component.MSBuild`) do (
|
||||
if /i "%%i"=="installationPath" set InstallDir=%%j
|
||||
)
|
||||
|
||||
SET VSWherePath="%InstallDir%\MSBuild"
|
||||
|
||||
ECHO.
|
||||
ECHO Visual Studio is installed in: %InstallDir%
|
||||
|
||||
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
|
||||
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
|
||||
|
||||
ECHO.
|
||||
ECHO Reporting NuGet version
|
||||
"%nuGetExecutable%" help | findstr "^NuGet Version:"
|
||||
|
||||
ECHO.
|
||||
ECHO Restoring NuGet packages
|
||||
ECHO Into %nuGetFolder%
|
||||
"%nuGetExecutable%" restore "%CD%\..\src\umbraco.sln" -Verbosity Quiet -NonInteractive -PackagesDirectory "%nuGetFolder%"
|
||||
IF ERRORLEVEL 1 GOTO :error
|
||||
|
||||
ECHO.
|
||||
ECHO.
|
||||
ECHO Performing MSBuild and producing Umbraco binaries zip files
|
||||
ECHO This takes a few minutes and logging is set to report warnings
|
||||
ECHO and errors only so it might seems like nothing is happening for a while.
|
||||
ECHO You can check the msbuild.log file for progress.
|
||||
ECHO.
|
||||
%MSBUILD% "Build.proj" /p:BUILD_RELEASE=%RELEASE% /p:BUILD_COMMENT=%COMMENT% /p:NugetPackagesDirectory="%nuGetFolder%" /p:VSWherePath=%VSWherePath% /consoleloggerparameters:Summary;ErrorsOnly /fileLogger
|
||||
IF ERRORLEVEL 1 GOTO error
|
||||
|
||||
ECHO.
|
||||
ECHO Setting node_modules folder to hidden to prevent VS13 from crashing on it while loading the websites project
|
||||
attrib +h ..\src\Umbraco.Web.UI.Client\node_modules
|
||||
|
||||
IF %SKIPNUGET% EQU 1 GOTO success
|
||||
|
||||
ECHO.
|
||||
ECHO Adding Web.config transform files to the NuGet package
|
||||
REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
|
||||
REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
|
||||
|
||||
ECHO.
|
||||
ECHO Packing the NuGet release files
|
||||
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %VERSION% -Symbols -Verbosity quiet
|
||||
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %VERSION% -Verbosity quiet
|
||||
IF ERRORLEVEL 1 GOTO error
|
||||
|
||||
:success
|
||||
ECHO.
|
||||
ECHO No errors were detected!
|
||||
ECHO There may still be some in the output, which you would need to investigate.
|
||||
ECHO Warnings are usually normal.
|
||||
ECHO.
|
||||
ECHO.
|
||||
GOTO :EOF
|
||||
|
||||
:error
|
||||
|
||||
ECHO.
|
||||
ECHO Errors were detected!
|
||||
ECHO.
|
||||
|
||||
REM don't pause if continuous integration else the build server waits forever
|
||||
REM before cancelling the build (and, there is noone to read the output anyways)
|
||||
IF %INTEGRATION% NEQ 1 PAUSE
|
||||
@@ -1,345 +0,0 @@
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
****************************************************
|
||||
INCLUDES
|
||||
*****************************************************
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildCommunityTasksPath>..\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
|
||||
<UmbracoMSBuildTasksPath>..\UmbracoMSBuildTasks</UmbracoMSBuildTasksPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\tools\UmbracoMSBuildTasks\Umbraco.MSBuild.Tasks.Targets" />
|
||||
<Import Project="..\tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
|
||||
|
||||
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
<ParameterGroup>
|
||||
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
||||
<OutputFile ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Using Namespace="System.IO" />
|
||||
<Using Namespace="System.Linq" />
|
||||
<Using Namespace="System.Security.Cryptography" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
foreach (var item in InputFiles)
|
||||
{
|
||||
string path = item.ItemSpec;
|
||||
|
||||
using (FileStream stream = new FileStream(path, FileMode.Open))
|
||||
{
|
||||
using (var cryptoProvider = new SHA1CryptoServiceProvider())
|
||||
{
|
||||
|
||||
var fileHash = cryptoProvider.ComputeHash(stream);
|
||||
|
||||
using (TextWriter w = new StreamWriter(OutputFile, false))
|
||||
{
|
||||
w.WriteLine(string.Join("", fileHash.Select(b => b.ToString("x2"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
|
||||
|
||||
<!--
|
||||
****************************************************
|
||||
VARIABLES
|
||||
*****************************************************
|
||||
-->
|
||||
|
||||
<!-- NB: BUILD_NUMBER is passed in by the build server -->
|
||||
<PropertyGroup Condition="'$(BUILD_NUMBER)'!=''">
|
||||
<DECIMAL_BUILD_NUMBER>.$(BUILD_NUMBER)</DECIMAL_BUILD_NUMBER>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(BUILD_RELEASE)'!=''">
|
||||
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)</DECIMAL_BUILD_NUMBER>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_COMMENT)'!=''">
|
||||
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_COMMENT)</DECIMAL_BUILD_NUMBER>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_NIGHTLY)'!=''">
|
||||
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_NIGHTLY)</DECIMAL_BUILD_NUMBER>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''">
|
||||
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)</DECIMAL_BUILD_NUMBER>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<BuildConfiguration>Release</BuildConfiguration>
|
||||
<BuildFolder>_BuildOutput\</BuildFolder>
|
||||
<BuildZipFileName>UmbracoCms$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileName>
|
||||
<BuildZipFileNameBin>UmbracoCms.AllBinaries$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameBin>
|
||||
<BuildZipFileNameWebPi>UmbracoCms.WebPI$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameWebPi>
|
||||
<IncludeSymbols>False</IncludeSymbols>
|
||||
<BuildFolderRelativeToProjects>..\..\build\$(BuildFolder)</BuildFolderRelativeToProjects>
|
||||
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
|
||||
<SolutionBinFolder>$(BuildFolder)bin\</SolutionBinFolder>
|
||||
<WebAppFolder>$(BuildFolder)WebApp\</WebAppFolder>
|
||||
<WebPiFolder>$(BuildFolder)WebPi\</WebPiFolder>
|
||||
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
|
||||
<SolutionBinFolderRelativeToProjects>$(BuildFolderRelativeToProjects)bin\</SolutionBinFolderRelativeToProjects>
|
||||
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)bin\</SolutionBinFolderAbsolutePath>
|
||||
<WebAppFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebApp\</WebAppFolderRelativeToProjects>
|
||||
<WebAppFolderAbsolutePath>$(BuildFolderAbsolutePath)WebApp\</WebAppFolderAbsolutePath>
|
||||
<WebPiFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebPi\</WebPiFolderRelativeToProjects>
|
||||
<WebPiFolderAbsolutePath>$(BuildFolderAbsolutePath)WebPi\</WebPiFolderAbsolutePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<SystemFolders Include="$(WebAppFolder)App_Data" />
|
||||
<SystemFolders Include="$(WebAppFolder)Media" />
|
||||
<SystemFolders Include="$(WebAppFolder)Views" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
****************************************************
|
||||
TARGETS
|
||||
*****************************************************
|
||||
-->
|
||||
|
||||
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
|
||||
<Message Text="Build finished" />
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
|
||||
<ItemGroup>
|
||||
<WebPiFile Include="$(BuildZipFileNameWebPi)" />
|
||||
</ItemGroup>
|
||||
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
|
||||
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanUp" DependsOnTargets="ZipWebPiApp">
|
||||
<Message Text="Deleting $(BuildFolder)" Importance="high" />
|
||||
<RemoveDir Directories="$(BuildFolder)" />
|
||||
<Message Text="Finished deleting $(BuildFolder)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="ZipWebPiApp" DependsOnTargets="ZipWebApp" >
|
||||
<!-- Clean folders -->
|
||||
<RemoveDir Directories="$(WebPiFolder)" />
|
||||
<MakeDir Directories="$(WebPiFolder)" />
|
||||
<MakeDir Directories="$(WebPiFolder)umbraco" />
|
||||
|
||||
<!-- Copy fresh built umbraco files -->
|
||||
<Exec Command="xcopy %22$(WebAppFolderAbsolutePath)*%22 %22$(WebPiFolderAbsolutePath)umbraco%22 /S /E /Y /I" />
|
||||
|
||||
<!-- Copy Web Pi template files -->
|
||||
<ItemGroup>
|
||||
<WebPiFiles Include="..\src\WebPi\**\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(WebPiFiles)"
|
||||
DestinationFiles="@(WebPiFiles->'$(WebPiFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
|
||||
|
||||
<!-- Zip the files -->
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameWebPi)%22 %22$(WebPiFolderAbsolutePath)*%22 -x!dotLess.Core.dll -x![Content_Types].xml >NUL" />
|
||||
</Target>
|
||||
|
||||
<Target Name="ZipWebApp" DependsOnTargets="CreateSystemFolders" >
|
||||
<Message Text="Starting to zip to $(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22 -x!dotLess.Core.dll >NUL" />
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileName)%22 %22$(WebAppFolderAbsolutePath)*%22 -x!dotLess.Core.dll -x![Content_Types].xml >NUL" />
|
||||
|
||||
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CreateSystemFolders" DependsOnTargets="CopyBelleBuild" Inputs="@(SystemFolders)" Outputs="%(Identity).Dummy">
|
||||
<MakeDir Directories="@(SystemFolders)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyBelleBuild" DependsOnTargets="CopyLibraries" >
|
||||
<ItemGroup>
|
||||
<BelleFiles Include="..\src\Umbraco.Web.UI.Client\build\belle\**\*.*" Exclude="..\src\Umbraco.Web.UI.Client\build\belle\index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(BelleFiles)"
|
||||
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyLibraries" DependsOnTargets="OffsetTimestamps" >
|
||||
<!-- Copy SQL CE -->
|
||||
<ItemGroup>
|
||||
<SQLCE4Files
|
||||
Include="..\src\packages\SqlServerCE.4.0.0.1\**\*.*"
|
||||
Exclude="..\src\packages\SqlServerCE.4.0.0.1\lib\**\*;..\src\packages\SqlServerCE.4.0.0.1\**\*.nu*"
|
||||
/>
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(SQLCE4Files)"
|
||||
DestinationFiles="@(SQLCE4Files->'$(SolutionBinFolder)%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
<Copy SourceFiles="@(SQLCE4Files)"
|
||||
DestinationFiles="@(SQLCE4Files->'$(WebAppFolder)bin\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
</Target>
|
||||
|
||||
<!-- Offset the modified timestamps on all umbraco dlls, as WebResources break if date is in the future, which, due to timezone offsets can happen. -->
|
||||
<Target Name="OffsetTimestamps" DependsOnTargets="CopyTransformedWebConfig">
|
||||
<CreateItem Include="$(BuildFolder)**\umbraco.*.dll">
|
||||
<Output TaskParameter="Include" ItemName="FilesToOffsetTimestamp" />
|
||||
</CreateItem>
|
||||
<Message Text="Starting to offset timestamps" Importance="high" />
|
||||
<Umbraco.MSBuild.Tasks.TimestampOffset Files="@(FilesToOffsetTimestamp)" Offset="-11" />
|
||||
<Message Text="Finished offsetting timestamps" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<!-- Copy the transformed web.config file to the root -->
|
||||
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
|
||||
<ItemGroup>
|
||||
<WebConfigFile Include="..\src\Umbraco.Web.UI\web.$(BuildConfiguration).Config.transformed" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(WebConfigFile)"
|
||||
DestinationFiles="$(WebAppFolder)Web.config"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
<!-- Copy the config files and rename them to *.config.transform -->
|
||||
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
|
||||
<ItemGroup>
|
||||
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
|
||||
<CustomLanguageFiles Include="$(WebAppFolder)config\lang\*.xml" />
|
||||
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(ConfigFiles)"
|
||||
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
<Copy SourceFiles="@(CustomLanguageFiles)"
|
||||
DestinationFiles="@(CustomLanguageFiles->'$(ConfigsFolder)Lang\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
<Copy SourceFiles="@(WebConfigTransformFile)"
|
||||
DestinationFiles="$(ConfigsFolder)Web.config.transform"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
<!-- Copy the xml documentation to the bin folder -->
|
||||
<Target Name="CopyXmlDocumentation" DependsOnTargets="CleanupPresentation">
|
||||
<ItemGroup>
|
||||
<XmlDocumentationFiles Include="$(SolutionBinFolder)*.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(XmlDocumentationFiles)"
|
||||
DestinationFiles="@(XmlDocumentationFiles->'$(WebAppFolder)bin\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
<Message Text="CopyXmlDocumentation" />
|
||||
</Target>
|
||||
|
||||
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
|
||||
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
|
||||
<ItemGroup>
|
||||
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(PresentationFolderToDelete)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CompileProjects" DependsOnTargets="SetVersionNumber">
|
||||
|
||||
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
|
||||
<!-- For UseWPP_CopyWebApplication=True see http://stackoverflow.com/questions/1983575/copywebapplication-with-web-config-transformations -->
|
||||
<!-- Build the Umbraco.Web.UI project -->
|
||||
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath);Verbosity=minimal" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
|
||||
|
||||
<!-- DONE -->
|
||||
<Message Text="Finished compiling projects" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
|
||||
<PropertyGroup>
|
||||
<NewVersion>$(BUILD_RELEASE)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_NIGHTLY)'!=''">$(BUILD_RELEASE)-$(BUILD_NIGHTLY)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_COMMENT)'!='' And '$(BUILD_NIGHTLY)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)</NewVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Match & replace 3 and 4 digit version numbers and -beta and +nightly (if they're there) -->
|
||||
<FileUpdate
|
||||
Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Regex="(\d+)\.(\d+)\.(\d+)(.(\d+))?"
|
||||
ReplacementText="$(BUILD_RELEASE)"/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Condition="'$(BUILD_NIGHTLY)'!=''"
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_NIGHTLY)""/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)-$(BUILD_NIGHTLY)""/>
|
||||
|
||||
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyFileVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyFileVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_NIGHTLY)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_NIGHTLY)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'=='' AND '$(BUILD_NIGHTLY)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the copyright year-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyCopyright\("Copyright © Umbraco (\d{4})"\)"
|
||||
ReplacementText="AssemblyCopyright("Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))")"/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,59 +0,0 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
:: SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
|
||||
SET toolsFolder=%CD%\tools\
|
||||
ECHO Current folder: %CD%
|
||||
|
||||
SET nodeFileName=node-v6.9.1-win-x86.7z
|
||||
SET nodeExtractFolder=%toolsFolder%node.js.691
|
||||
|
||||
IF NOT EXIST "%nodeExtractFolder%" (
|
||||
ECHO Downloading http://nodejs.org/dist/v6.9.1/%nodeFileName% to %toolsFolder%%nodeFileName%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v6.9.1/%nodeFileName%', '%toolsFolder%%nodeFileName%')"
|
||||
ECHO Extracting %nodeFileName% to %nodeExtractFolder%
|
||||
"%toolsFolder%\7za.exe" x "%toolsFolder%\%nodeFileName%" -o"%nodeExtractFolder%" -aos > nul
|
||||
)
|
||||
FOR /f "delims=" %%A in ('dir "%nodeExtractFolder%\node*" /b') DO SET "nodePath=%nodeExtractFolder%\%%A"
|
||||
|
||||
|
||||
SET nuGetExecutable=%CD%\tools\nuget.exe
|
||||
IF NOT EXIST "%nuGetExecutable%" (
|
||||
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
|
||||
)
|
||||
|
||||
SET drive=%CD:~0,2%
|
||||
SET nuGetFolder=%drive%\packages\
|
||||
FOR /f "delims=" %%A in ('dir "%nuGetFolder%npm.*" /b') DO SET "npmPath=%nuGetFolder%%%A\"
|
||||
|
||||
IF [%npmPath%] == [] GOTO :installnpm
|
||||
IF NOT [%npmPath%] == [] GOTO :build
|
||||
|
||||
:installnpm
|
||||
ECHO Downloading npm
|
||||
ECHO Configured packages folder: %nuGetFolder%
|
||||
ECHO Installing Npm NuGet Package
|
||||
"%nuGetExecutable%" install Npm -OutputDirectory %nuGetFolder% -Verbosity detailed
|
||||
REM Ensures that we look for the just downloaded NPM, not whatever the user has installed on their machine
|
||||
FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\"
|
||||
GOTO :build
|
||||
|
||||
:build
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH="%npmPath%";"%nodePath%";%PATH%
|
||||
SET buildFolder=%CD%
|
||||
|
||||
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
CD %CD%\..\src\Umbraco.Web.UI.Client\
|
||||
|
||||
ECHO Do npm install and the grunt build of Belle
|
||||
call npm cache clean --quiet
|
||||
call npm install --quiet
|
||||
call npm install -g grunt-cli --quiet
|
||||
call npm install -g bower --quiet
|
||||
call grunt build --buildversion=%release%
|
||||
|
||||
ECHO Move back to the build folder
|
||||
CD "%buildFolder%"
|
||||
@@ -1,20 +0,0 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
|
||||
SET release=%1
|
||||
ECHO Installing Npm NuGet Package
|
||||
|
||||
SET nuGetFolder=%CD%\..\src\packages\
|
||||
ECHO Configured packages folder: %nuGetFolder%
|
||||
ECHO Current folder: %CD%
|
||||
|
||||
%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet
|
||||
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
|
||||
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
Powershell.exe -ExecutionPolicy Unrestricted -File .\BuildDocs.ps1
|
||||
@@ -1,114 +0,0 @@
|
||||
$PSScriptFilePath = (Get-Item $MyInvocation.MyCommand.Path);
|
||||
$RepoRoot = (get-item $PSScriptFilePath).Directory.Parent.FullName;
|
||||
$SolutionRoot = Join-Path -Path $RepoRoot "src";
|
||||
$ToolsRoot = Join-Path -Path $RepoRoot "tools";
|
||||
$DocFx = Join-Path -Path $ToolsRoot "docfx\docfx.exe"
|
||||
$DocFxFolder = (Join-Path -Path $ToolsRoot "docfx")
|
||||
$DocFxJson = Join-Path -Path $RepoRoot "apidocs\docfx.json"
|
||||
$7Zip = Join-Path -Path $ToolsRoot "7zip\7za.exe"
|
||||
$DocFxSiteOutput = Join-Path -Path $RepoRoot "apidocs\_site\*.*"
|
||||
$NgDocsSiteOutput = Join-Path -Path $RepoRoot "src\Umbraco.Web.UI.Client\docs\api\*.*"
|
||||
$ProgFiles86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)");
|
||||
$MSBuild = "$ProgFiles86\MSBuild\14.0\Bin\MSBuild.exe"
|
||||
|
||||
|
||||
################ Do the UI docs
|
||||
|
||||
"Changing to Umbraco.Web.UI.Client folder"
|
||||
cd ..
|
||||
cd src\Umbraco.Web.UI.Client
|
||||
Write-Host $(Get-Location)
|
||||
|
||||
"Creating build folder so MSBuild doesn't run the whole grunt build"
|
||||
if (-Not (Test-Path "build")) {
|
||||
md "build"
|
||||
}
|
||||
|
||||
"Installing node"
|
||||
# Check if Install-Product exists, should only exist on the build server
|
||||
if (Get-Command Install-Product -errorAction SilentlyContinue)
|
||||
{
|
||||
Install-Product node ''
|
||||
}
|
||||
|
||||
"Installing node modules"
|
||||
& npm install
|
||||
|
||||
"Installing grunt"
|
||||
& npm install -g grunt-cli
|
||||
|
||||
"Moving back to build folder"
|
||||
cd ..
|
||||
cd ..
|
||||
cd build
|
||||
Write-Host $(Get-Location)
|
||||
|
||||
& grunt --gruntfile ../src/umbraco.web.ui.client/gruntfile.js docs
|
||||
|
||||
# change baseUrl
|
||||
$BaseUrl = "https://our.umbraco.org/apidocs/ui/"
|
||||
$IndexPath = "../src/umbraco.web.ui.client/docs/api/index.html"
|
||||
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
|
||||
# zip it
|
||||
|
||||
& $7Zip a -tzip ui-docs.zip $NgDocsSiteOutput -r
|
||||
|
||||
################ Do the c# docs
|
||||
|
||||
# Build the solution in debug mode
|
||||
$SolutionPath = Join-Path -Path $SolutionRoot -ChildPath "umbraco.sln"
|
||||
|
||||
# Go get nuget.exe if we don't hae it
|
||||
$NuGet = "$ToolsRoot\nuget.exe"
|
||||
$FileExists = Test-Path $NuGet
|
||||
If ($FileExists -eq $False) {
|
||||
Write-Host "Retrieving nuget.exe..."
|
||||
$SourceNugetExe = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
Invoke-WebRequest $SourceNugetExe -OutFile $NuGet
|
||||
}
|
||||
|
||||
#restore nuget packages
|
||||
Write-Host "Restoring nuget packages..."
|
||||
& $NuGet restore $SolutionPath
|
||||
|
||||
& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount /t:Clean
|
||||
if (-not $?)
|
||||
{
|
||||
throw "The MSBuild process returned an error code."
|
||||
}
|
||||
& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount
|
||||
if (-not $?)
|
||||
{
|
||||
throw "The MSBuild process returned an error code."
|
||||
}
|
||||
|
||||
# Go get docfx if we don't hae it
|
||||
$FileExists = Test-Path $DocFx
|
||||
If ($FileExists -eq $False) {
|
||||
|
||||
If(!(Test-Path $DocFxFolder))
|
||||
{
|
||||
New-Item $DocFxFolder -type directory
|
||||
}
|
||||
|
||||
$DocFxZip = Join-Path -Path $ToolsRoot "docfx\docfx.zip"
|
||||
$DocFxSource = "https://github.com/dotnet/docfx/releases/download/v1.9.4/docfx.zip"
|
||||
Invoke-WebRequest $DocFxSource -OutFile $DocFxZip
|
||||
|
||||
#unzip it
|
||||
& $7Zip e $DocFxZip "-o$DocFxFolder"
|
||||
}
|
||||
|
||||
#clear site
|
||||
If(Test-Path(Join-Path -Path $RepoRoot "apidocs\_site"))
|
||||
{
|
||||
Remove-Item $DocFxSiteOutput -recurse
|
||||
}
|
||||
|
||||
# run it!
|
||||
& $DocFx metadata $DocFxJson
|
||||
& $DocFx build $DocFxJson
|
||||
|
||||
# zip it
|
||||
|
||||
& $7Zip a -tzip csharp-docs.zip $DocFxSiteOutput -r
|
||||
@@ -0,0 +1,111 @@
|
||||
#
|
||||
|
||||
function Build-UmbracoDocs
|
||||
{
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
|
||||
$buildTemp = "$PSScriptRoot\temp"
|
||||
$cache = 2
|
||||
|
||||
Prepare-Build -keep $uenv
|
||||
|
||||
################ Do the UI docs
|
||||
# get a temp clean node env (will restore)
|
||||
Sandbox-Node $uenv
|
||||
|
||||
Write-Host "Executing gulp docs"
|
||||
|
||||
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
|
||||
write "node version is:" > $tmp\belle-docs.log
|
||||
&node -v >> $tmp\belle-docs.log 2>&1
|
||||
write "npm version is:" >> $tmp\belle-docs.log 2>&1
|
||||
&npm -v >> $tmp\belle-docs.log 2>&1
|
||||
write "executing npm install" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install >> $tmp\belle-docs.log 2>&1
|
||||
write "executing bower install" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g bower >> $tmp\belle-docs.log 2>&1
|
||||
write "installing gulp" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g gulp >> $tmp\belle-docs.log 2>&1
|
||||
write "installing gulp-cli" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g gulp-cli --quiet >> $tmp\belle-docs.log 2>&1
|
||||
write "building docs using gulp" >> $tmp\belle-docs.log 2>&1
|
||||
&gulp docs >> $tmp\belle-docs.log 2>&1
|
||||
pop-location
|
||||
|
||||
Write-Host "Completed gulp docs build"
|
||||
|
||||
# fixme - should we filter the log to find errors?
|
||||
#get-content .\build.tmp\belle-docs.log | %{ if ($_ -match "build") { write $_}}
|
||||
|
||||
# change baseUrl
|
||||
$baseUrl = "https://our.umbraco.org/apidocs/ui/"
|
||||
$indexPath = "$src/Umbraco.Web.UI.Client/docs/api/index.html"
|
||||
(Get-Content $indexPath).Replace("location.href.replace(rUrl, indexFile)", "'$baseUrl'") `
|
||||
| Set-Content $indexPath
|
||||
|
||||
# restore
|
||||
Restore-Node
|
||||
|
||||
# zip
|
||||
&$uenv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*" `
|
||||
> $null
|
||||
|
||||
################ Do the c# docs
|
||||
|
||||
Write-Host "Build C# documentation"
|
||||
|
||||
# Build the solution in debug mode
|
||||
# FIXME no only a simple compilation should be enough!
|
||||
# FIXME we MUST handle msbuild & co error codes!
|
||||
# FIXME deal with weird things in gitconfig?
|
||||
#Build-Umbraco -Configuration Debug
|
||||
Restore-NuGet $uenv
|
||||
Compile-Umbraco $uenv "Debug" # FIXME different log file!
|
||||
Restore-WebConfig "$src\Umbraco.Web.UI"
|
||||
|
||||
# ensure we have docfx
|
||||
Get-DocFx $uenv $buildTemp
|
||||
|
||||
# clear
|
||||
$docFxOutput = "$($uenv.SolutionRoot)\apidocs\_site"
|
||||
if (test-path($docFxOutput))
|
||||
{
|
||||
Remove-Directory $docFxOutput
|
||||
}
|
||||
|
||||
# run
|
||||
$docFxJson = "$($uenv.SolutionRoot)\apidocs\docfx.json"
|
||||
push-location "$($uenv.SolutionRoot)\build" # silly docfx.json wants this
|
||||
|
||||
Write-Host "Run DocFx metadata"
|
||||
Write-Host "Logging to $tmp\docfx.metadata.log"
|
||||
&$uenv.DocFx metadata $docFxJson > "$tmp\docfx.metadata.log"
|
||||
Write-Host "Run DocFx build"
|
||||
Write-Host "Logging to $tmp\docfx.build.log"
|
||||
&$uenv.DocFx build $docFxJson > "$tmp\docfx.build.log"
|
||||
|
||||
pop-location
|
||||
|
||||
# zip
|
||||
&$uenv.Zip a -tzip -r "$out\csharp-docs.zip" "$docFxOutput\*.*" `
|
||||
> $null
|
||||
}
|
||||
|
||||
function Get-DocFx($uenv, $buildTemp)
|
||||
{
|
||||
$docFx = "$buildTemp\docfx"
|
||||
if (-not (test-path $docFx))
|
||||
{
|
||||
Write-Host "Download DocFx..."
|
||||
$source = "https://github.com/dotnet/docfx/releases/download/v2.19.2/docfx.zip"
|
||||
Invoke-WebRequest $source -OutFile "$buildTemp\docfx.zip"
|
||||
|
||||
&$uenv.Zip x "$buildTemp\docfx.zip" -o"$buildTemp\docfx" -aos > $nul
|
||||
Remove-File "$buildTemp\docfx.zip"
|
||||
}
|
||||
$uenv | add-member -memberType NoteProperty -name DocFx -value "$docFx\docfx.exe"
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
#
|
||||
# Get-UmbracoBuildEnv
|
||||
# Gets the Umbraco build environment
|
||||
# Downloads tools if necessary
|
||||
#
|
||||
function Get-UmbracoBuildEnv
|
||||
{
|
||||
# store tools in the module's directory
|
||||
# and cache them for two days
|
||||
$path = "$PSScriptRoot\temp"
|
||||
$cache = 2
|
||||
|
||||
if (-not (test-path $path))
|
||||
{
|
||||
mkdir $path > $null
|
||||
}
|
||||
|
||||
# ensure we have NuGet
|
||||
$nuget = "$path\nuget.exe"
|
||||
$source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
if ((test-path $nuget) -and ((ls $nuget).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
|
||||
{
|
||||
Remove-File $nuget
|
||||
}
|
||||
if (-not (test-path $nuget))
|
||||
{
|
||||
Write-Host "Download NuGet..."
|
||||
Invoke-WebRequest $source -OutFile $nuget
|
||||
}
|
||||
|
||||
# ensure we have 7-Zip
|
||||
$sevenZip = "$path\7za.exe"
|
||||
if ((test-path $sevenZip) -and ((ls $sevenZip).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
|
||||
{
|
||||
Remove-File $sevenZip
|
||||
}
|
||||
if (-not (test-path $sevenZip))
|
||||
{
|
||||
Write-Host "Download 7-Zip..."
|
||||
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
|
||||
$dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1
|
||||
$file = ls -path "$dir" -name 7za.exe -recurse
|
||||
$file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe
|
||||
mv "$dir\$file" $sevenZip
|
||||
Remove-Directory $dir
|
||||
}
|
||||
|
||||
# ensure we have vswhere
|
||||
$vswhere = "$path\vswhere.exe"
|
||||
if ((test-path $vswhere) -and ((ls $vswhere).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
|
||||
{
|
||||
Remove-File $vswhere
|
||||
}
|
||||
if (-not (test-path $vswhere))
|
||||
{
|
||||
Write-Host "Download VsWhere..."
|
||||
&$nuget install vswhere -OutputDirectory $path -Verbosity quiet
|
||||
$dir = ls "$path\vswhere.*" | sort -property Name -descending | select -first 1
|
||||
$file = ls -path "$dir" -name vswhere.exe -recurse
|
||||
mv "$dir\$file" $vswhere
|
||||
Remove-Directory $dir
|
||||
}
|
||||
|
||||
# ensure we have semver
|
||||
$semver = "$path\Semver.dll"
|
||||
if ((test-path $semver) -and ((ls $semver).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
|
||||
{
|
||||
Remove-File $semver
|
||||
}
|
||||
if (-not (test-path $semver))
|
||||
{
|
||||
Write-Host "Download Semver..."
|
||||
&$nuget install semver -OutputDirectory $path -Verbosity quiet
|
||||
$dir = ls "$path\semver.*" | sort -property Name -descending | select -first 1
|
||||
$file = "$dir\lib\net452\Semver.dll"
|
||||
if (-not (test-path $file))
|
||||
{
|
||||
Write-Error "Failed to file $file"
|
||||
return
|
||||
}
|
||||
mv "$file" $semver
|
||||
Remove-Directory $dir
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
[Reflection.Assembly]::LoadFile($semver) > $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Error -Exception $_.Exception -Message "Failed to load $semver"
|
||||
break
|
||||
}
|
||||
|
||||
# ensure we have node
|
||||
$node = "$path\node-v6.9.1-win-x86"
|
||||
$source = "http://nodejs.org/dist/v6.9.1/node-v6.9.1-win-x86.7z"
|
||||
if (-not (test-path $node))
|
||||
{
|
||||
Write-Host "Download Node..."
|
||||
Invoke-WebRequest $source -OutFile "$path\node-v6.9.1-win-x86.7z"
|
||||
&$sevenZip x "$path\node-v6.9.1-win-x86.7z" -o"$path" -aos > $nul
|
||||
Remove-File "$path\node-v6.9.1-win-x86.7z"
|
||||
}
|
||||
|
||||
# note: why? node already brings everything we need!
|
||||
## ensure we have npm
|
||||
#$npm = "$path\npm.*"
|
||||
#$getNpm = $true
|
||||
#if (test-path $npm)
|
||||
#{
|
||||
# $getNpm = $false
|
||||
# $tmpNpm = ls "$path\npm.*" | sort -property Name -descending | select -first 1
|
||||
# if ($tmpNpm.CreationTime -lt [DateTime]::Now.AddDays(-$cache))
|
||||
# {
|
||||
# $getNpm = $true
|
||||
# }
|
||||
# else
|
||||
# {
|
||||
# $npm = $tmpNpm.ToString()
|
||||
# }
|
||||
#}
|
||||
#if ($getNpm)
|
||||
#{
|
||||
# Write-Host "Download Npm..."
|
||||
# &$nuget install npm -OutputDirectory $path -Verbosity quiet
|
||||
# $npm = ls "$path\npm.*" | sort -property Name -descending | select -first 1
|
||||
# $npm.CreationTime = [DateTime]::Now
|
||||
# $npm = $npm.ToString()
|
||||
#}
|
||||
|
||||
# find visual studio
|
||||
# will not work on VSO but VSO does not need it
|
||||
$vsPath = ""
|
||||
$vsVer = ""
|
||||
$msBuild = $null
|
||||
&$vswhere | foreach {
|
||||
if ($_.StartsWith("installationPath:")) { $vsPath = $_.SubString("installationPath:".Length).Trim() }
|
||||
if ($_.StartsWith("installationVersion:")) { $vsVer = $_.SubString("installationVersion:".Length).Trim() }
|
||||
}
|
||||
if ($vsPath -ne "")
|
||||
{
|
||||
$vsVerParts = $vsVer.Split('.')
|
||||
$vsMajor = [int]::Parse($vsVerParts[0])
|
||||
$vsMinor = [int]::Parse($vsVerParts[1])
|
||||
if ($vsMajor -eq 15) {
|
||||
$msBuild = "$vsPath\MSBuild\$vsMajor.0\Bin"
|
||||
}
|
||||
elseif ($vsMajor -eq 14) {
|
||||
$msBuild = "c:\Program Files (x86)\MSBuild\$vsMajor\Bin"
|
||||
}
|
||||
else
|
||||
{
|
||||
$msBuild = $null
|
||||
}
|
||||
}
|
||||
|
||||
$vs = $null
|
||||
if ($msBuild)
|
||||
{
|
||||
$vs = new-object -typeName PsObject
|
||||
$vs | add-member -memberType NoteProperty -name Path -value $vsPath
|
||||
$vs | add-member -memberType NoteProperty -name Major -value $vsMajor
|
||||
$vs | add-member -memberType NoteProperty -name Minor -value $vsMinor
|
||||
$vs | add-member -memberType NoteProperty -name MsBuild -value "$msBuild\MsBuild.exe"
|
||||
}
|
||||
|
||||
$solutionRoot = Get-FullPath "$PSScriptRoot\..\..\.."
|
||||
|
||||
$uenv = new-object -typeName PsObject
|
||||
$uenv | add-member -memberType NoteProperty -name SolutionRoot -value $solutionRoot
|
||||
$uenv | add-member -memberType NoteProperty -name VisualStudio -value $vs
|
||||
$uenv | add-member -memberType NoteProperty -name NuGet -value $nuget
|
||||
$uenv | add-member -memberType NoteProperty -name Zip -value $sevenZip
|
||||
$uenv | add-member -memberType NoteProperty -name VsWhere -value $vswhere
|
||||
$uenv | add-member -memberType NoteProperty -name Semver -value $semver
|
||||
$uenv | add-member -memberType NoteProperty -name NodePath -value $node
|
||||
#$uenv | add-member -memberType NoteProperty -name NpmPath -value $npm
|
||||
|
||||
return $uenv
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Get-UmbracoVersion
|
||||
# Gets the Umbraco version
|
||||
#
|
||||
function Get-UmbracoVersion
|
||||
{
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
|
||||
# parse SolutionInfo and retrieve the version string
|
||||
$filepath = "$($uenv.SolutionRoot)\src\SolutionInfo.cs"
|
||||
$text = [System.IO.File]::ReadAllText($filepath)
|
||||
$match = [System.Text.RegularExpressions.Regex]::Matches($text, "AssemblyInformationalVersion\(`"(.+)?`"\)")
|
||||
$version = $match.Groups[1]
|
||||
|
||||
# semver-parse the version string
|
||||
$semver = [SemVer.SemVersion]::Parse($version)
|
||||
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
|
||||
|
||||
$versions = new-object -typeName PsObject
|
||||
$versions | add-member -memberType NoteProperty -name Semver -value $semver
|
||||
$versions | add-member -memberType NoteProperty -name Release -value $release
|
||||
$versions | add-member -memberType NoteProperty -name Comment -value $semver.PreRelease
|
||||
$versions | add-member -memberType NoteProperty -name Build -value $semver.Build
|
||||
|
||||
return $versions
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# finds msbuild
|
||||
function Get-VisualStudio($vswhere)
|
||||
{
|
||||
$vsPath = ""
|
||||
$vsVer = ""
|
||||
&$vswhere | foreach {
|
||||
if ($_.StartsWith("installationPath:")) { $vsPath = $_.SubString("installationPath:".Length).Trim() }
|
||||
if ($_.StartsWith("installationVersion:")) { $vsVer = $_.SubString("installationVersion:".Length).Trim() }
|
||||
}
|
||||
if ($vsPath -eq "") { return $null }
|
||||
|
||||
$vsVerParts = $vsVer.Split('.')
|
||||
$vsMajor = [int]::Parse($vsVerParts[0])
|
||||
$vsMinor = [int]::Parse($vsVerParts[1])
|
||||
if ($vsMajor -eq 15) {
|
||||
$msBuild = "$vsPath\MSBuild\$vsMajor.$vsMinor\Bin"
|
||||
}
|
||||
elseif ($vsMajor -eq 14) {
|
||||
$msBuild = "c:\Program Files (x86)\MSBuild\$vsMajor\Bin"
|
||||
}
|
||||
else { return $null }
|
||||
$msBuild = "$msBuild\MsBuild.exe"
|
||||
|
||||
$vs = new-object -typeName PsObject
|
||||
$vs | add-member -memberType NoteProperty -name Path -value $vsPath
|
||||
$vs | add-member -memberType NoteProperty -name Major -value $vsMajor
|
||||
$vs | add-member -memberType NoteProperty -name Minor -value $vsMinor
|
||||
$vs | add-member -memberType NoteProperty -name MsBuild -value $msBuild
|
||||
return $vs
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Set-UmbracoContinuousVersion
|
||||
# Sets the Umbraco version for continuous integration
|
||||
#
|
||||
# -Version <version>
|
||||
# where <version> is a Semver valid version
|
||||
# eg 1.2.3, 1.2.3-alpha, 1.2.3-alpha+456
|
||||
#
|
||||
# -BuildNumber <buildNumber>
|
||||
# where <buildNumber> is a string coming from the build server
|
||||
# eg 34, 126, 1
|
||||
#
|
||||
function Set-UmbracoContinuousVersion
|
||||
{
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$version,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$buildNumber
|
||||
)
|
||||
|
||||
Write-Host "Version is currently set to $version"
|
||||
|
||||
$umbracoVersion = "$($version.Trim())-alpha$($buildNumber)"
|
||||
Write-Host "Setting Umbraco Version to $umbracoVersion"
|
||||
|
||||
Set-UmbracoVersion $umbracoVersion
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#
|
||||
# Set-UmbracoVersion
|
||||
# Sets the Umbraco version
|
||||
#
|
||||
# -Version <version>
|
||||
# where <version> is a Semver valid version
|
||||
# eg 1.2.3, 1.2.3-alpha, 1.2.3-alpha+456
|
||||
#
|
||||
function Set-UmbracoVersion
|
||||
{
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$version
|
||||
)
|
||||
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
|
||||
try
|
||||
{
|
||||
[Reflection.Assembly]::LoadFile($uenv.Semver) > $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Error "Failed to load $uenv.Semver"
|
||||
break
|
||||
}
|
||||
|
||||
# validate input
|
||||
$ok = [Regex]::Match($version, "^[0-9]+\.[0-9]+\.[0-9]+(\-[a-z0-9]+)?(\+[0-9]+)?$")
|
||||
if (-not $ok.Success)
|
||||
{
|
||||
Write-Error "Invalid version $version"
|
||||
break
|
||||
}
|
||||
|
||||
# parse input
|
||||
try
|
||||
{
|
||||
$semver = [SemVer.SemVersion]::Parse($version)
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Error "Invalid version $version"
|
||||
break
|
||||
}
|
||||
|
||||
#
|
||||
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
|
||||
|
||||
# edit files and set the proper versions and dates
|
||||
Write-Host "Update UmbracoVersion.cs"
|
||||
Replace-FileText "$($uenv.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs" `
|
||||
"(\d+)\.(\d+)\.(\d+)(.(\d+))?" `
|
||||
"$release"
|
||||
Replace-FileText "$($uenv.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs" `
|
||||
"CurrentComment { get { return `"(.+)`"" `
|
||||
"CurrentComment { get { return `"$($semver.PreRelease)`""
|
||||
Write-Host "Update SolutionInfo.cs"
|
||||
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
|
||||
"AssemblyFileVersion\(`"(.+)?`"\)" `
|
||||
"AssemblyFileVersion(`"$release`")"
|
||||
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
|
||||
"AssemblyInformationalVersion\(`"(.+)?`"\)" `
|
||||
"AssemblyInformationalVersion(`"$semver`")"
|
||||
$year = [System.DateTime]::Now.ToString("yyyy")
|
||||
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
|
||||
"AssemblyCopyright\(`"Copyright © Umbraco (\d{4})`"\)" `
|
||||
"AssemblyCopyright(`"Copyright © Umbraco $year`")"
|
||||
|
||||
# edit csproj and set IIS Express port number
|
||||
# this is a raw copy of ReplaceIISExpressPortNumber.exe
|
||||
# it probably can be achieved in a much nicer way - l8tr
|
||||
$source = @"
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Umbraco
|
||||
{
|
||||
public static class PortUpdater
|
||||
{
|
||||
public static void Update(string path, string release)
|
||||
{
|
||||
XmlDocument xmlDocument = new XmlDocument();
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
xmlDocument.Load(fullPath);
|
||||
int result = 1;
|
||||
int.TryParse(release.Replace(`".`", `"`"), out result);
|
||||
while (result < 1024)
|
||||
result *= 10;
|
||||
XmlNode xmlNode1 = xmlDocument.GetElementsByTagName(`"IISUrl`").Item(0);
|
||||
if (xmlNode1 != null)
|
||||
xmlNode1.InnerText = `"http://localhost:`" + (object) result;
|
||||
XmlNode xmlNode2 = xmlDocument.GetElementsByTagName(`"DevelopmentServerPort`").Item(0);
|
||||
if (xmlNode2 != null)
|
||||
xmlNode2.InnerText = result.ToString((IFormatProvider) CultureInfo.InvariantCulture);
|
||||
xmlDocument.Save(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
$assem = (
|
||||
"System.Xml",
|
||||
"System.IO",
|
||||
"System.Globalization"
|
||||
)
|
||||
|
||||
Write-Host "Update Umbraco.Web.UI.csproj"
|
||||
add-type -referencedAssemblies $assem -typeDefinition $source -language CSharp
|
||||
$csproj = "$($uenv.SolutionRoot)\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
|
||||
[Umbraco.PortUpdater]::Update($csproj, $release)
|
||||
|
||||
return $semver
|
||||
}
|
||||
@@ -0,0 +1,611 @@
|
||||
|
||||
# Umbraco.Build.psm1
|
||||
#
|
||||
# $env:PSModulePath = "$pwd\build\Modules\;$env:PSModulePath"
|
||||
# Import-Module Umbraco.Build -Force -DisableNameChecking
|
||||
#
|
||||
# PowerShell Modules:
|
||||
# https://msdn.microsoft.com/en-us/library/dd878324%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
#
|
||||
# PowerShell Module Manifest:
|
||||
# https://msdn.microsoft.com/en-us/library/dd878337%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
#
|
||||
# See also
|
||||
# http://www.powershellmagazine.com/2014/08/15/pstip-taking-control-of-verbose-and-debug-output-part-5/
|
||||
|
||||
|
||||
. "$PSScriptRoot\Utilities.ps1"
|
||||
. "$PSScriptRoot\Get-VisualStudio.ps1"
|
||||
|
||||
. "$PSScriptRoot\Get-UmbracoBuildEnv.ps1"
|
||||
. "$PSScriptRoot\Set-UmbracoVersion.ps1"
|
||||
. "$PSScriptRoot\Set-UmbracoContinuousVersion.ps1"
|
||||
. "$PSScriptRoot\Get-UmbracoVersion.ps1"
|
||||
. "$PSScriptRoot\Verify-NuGet.ps1"
|
||||
|
||||
. "$PSScriptRoot\Build-UmbracoDocs.ps1"
|
||||
|
||||
#
|
||||
# Prepares the build
|
||||
#
|
||||
function Prepare-Build
|
||||
{
|
||||
param (
|
||||
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
|
||||
[Alias("k")]
|
||||
[switch]
|
||||
$keep = $false
|
||||
)
|
||||
|
||||
Write-Host ">> Prepare Build"
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
|
||||
# clear
|
||||
Write-Host "Clear folders and files"
|
||||
|
||||
Remove-Directory "$src\Umbraco.Web.UI.Client\bower_components"
|
||||
|
||||
if (-not $keep)
|
||||
{
|
||||
Remove-Directory "$tmp"
|
||||
Remove-Directory "$out"
|
||||
}
|
||||
|
||||
if (-not (Test-Path "$tmp"))
|
||||
{
|
||||
mkdir "$tmp" > $null
|
||||
}
|
||||
if (-not (Test-Path "$out"))
|
||||
{
|
||||
mkdir "$out" > $null
|
||||
}
|
||||
|
||||
# ensure proper web.config
|
||||
$webUi = "$src\Umbraco.Web.UI"
|
||||
Store-WebConfig $webUi
|
||||
Write-Host "Create clean web.config"
|
||||
Copy-File "$webUi\web.Template.config" "$webUi\web.config"
|
||||
}
|
||||
|
||||
function Clear-EnvVar($var)
|
||||
{
|
||||
$value = [Environment]::GetEnvironmentVariable($var)
|
||||
if (test-path "env:$var") { rm "env:$var" }
|
||||
return $value
|
||||
}
|
||||
|
||||
function Set-EnvVar($var, $value)
|
||||
{
|
||||
if ($value)
|
||||
{
|
||||
[Environment]::SetEnvironmentVariable($var, $value)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test-path "env:$var") { rm "env:$var" }
|
||||
}
|
||||
}
|
||||
|
||||
function Sandbox-Node
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
$global:node_path = $env:path
|
||||
$nodePath = $uenv.NodePath
|
||||
$gitExe = (get-command git).Source
|
||||
$gitPath = [System.IO.Path]::GetDirectoryName($gitExe)
|
||||
$env:path = "$nodePath;$gitPath"
|
||||
|
||||
$global:node_nodepath = Clear-EnvVar "NODEPATH"
|
||||
$global:node_npmcache = Clear-EnvVar "NPM_CONFIG_CACHE"
|
||||
$global:node_npmprefix = Clear-EnvVar "NPM_CONFIG_PREFIX"
|
||||
}
|
||||
|
||||
function Restore-Node
|
||||
{
|
||||
$env:path = $node_path
|
||||
|
||||
Set-EnvVar "NODEPATH" $node_nodepath
|
||||
Set-EnvVar "NPM_CONFIG_CACHE" $node_npmcache
|
||||
Set-EnvVar "NPM_CONFIG_PREFIX" $node_npmprefix
|
||||
}
|
||||
|
||||
#
|
||||
# Builds the Belle UI project
|
||||
#
|
||||
function Compile-Belle
|
||||
{
|
||||
param (
|
||||
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
$version # an Umbraco version object (see Get-UmbracoVersion)
|
||||
)
|
||||
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
|
||||
Write-Host ">> Compile Belle"
|
||||
Write-Host "Logging to $tmp\belle.log"
|
||||
|
||||
# get a temp clean node env (will restore)
|
||||
Sandbox-Node $uenv
|
||||
|
||||
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
|
||||
write "node version is:" > $tmp\belle.log
|
||||
&node -v >> $tmp\belle.log 2>&1
|
||||
write "npm version is:" >> $tmp\belle.log 2>&1
|
||||
&npm -v >> $tmp\belle.log 2>&1
|
||||
write "cleaning npm cache" >> $tmp\belle.log 2>&1
|
||||
&npm cache clean >> $tmp\belle.log 2>&1
|
||||
write "installing bower" >> $tmp\belle.log 2>&1
|
||||
&npm install -g bower >> $tmp\belle.log 2>&1
|
||||
write "installing gulp" >> $tmp\belle.log 2>&1
|
||||
&npm install -g gulp >> $tmp\belle.log 2>&1
|
||||
write "installing gulp-cli" >> $tmp\belle.log 2>&1
|
||||
&npm install -g gulp-cli --quiet >> $tmp\belle.log 2>&1
|
||||
write "executing npm install" >> $tmp\belle.log 2>&1
|
||||
&npm install >> $tmp\belle.log 2>&1
|
||||
write "executing gulp build for version $version" >> $tmp\belle.log 2>&1
|
||||
&gulp build --buildversion=$version.Release >> $tmp\belle.log 2>&1
|
||||
pop-location
|
||||
|
||||
# fixme - should we filter the log to find errors?
|
||||
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
|
||||
|
||||
# restore
|
||||
Restore-Node
|
||||
|
||||
# setting node_modules folder to hidden
|
||||
# used to prevent VS13 from crashing on it while loading the websites project
|
||||
# also makes sure aspnet compiler does not try to handle rogue files and chokes
|
||||
# in VSO with Microsoft.VisualC.CppCodeProvider -related errors
|
||||
# use get-item -force 'cos it might be hidden already
|
||||
write "Set hidden attribute on node_modules"
|
||||
$dir = get-item -force "$src\Umbraco.Web.UI.Client\node_modules"
|
||||
$dir.Attributes = $dir.Attributes -bor ([System.IO.FileAttributes]::Hidden)
|
||||
}
|
||||
|
||||
#
|
||||
# Compiles Umbraco
|
||||
#
|
||||
function Compile-Umbraco
|
||||
{
|
||||
param (
|
||||
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
[string] $buildConfiguration = "Release"
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
|
||||
if ($uenv.VisualStudio -eq $null)
|
||||
{
|
||||
Write-Error "Build environment does not provide VisualStudio."
|
||||
break
|
||||
}
|
||||
|
||||
$toolsVersion = "4.0"
|
||||
if ($uenv.VisualStudio.Major -eq 15)
|
||||
{
|
||||
$toolsVersion = "15.0"
|
||||
}
|
||||
|
||||
Write-Host ">> Compile Umbraco"
|
||||
Write-Host "Logging to $tmp\msbuild.umbraco.log"
|
||||
|
||||
# beware of the weird double \\ at the end of paths
|
||||
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
|
||||
&$uenv.VisualStudio.MsBuild "$src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" `
|
||||
/p:WarningLevel=0 `
|
||||
/p:Configuration=$buildConfiguration `
|
||||
/p:Platform=AnyCPU `
|
||||
/p:UseWPP_CopyWebApplication=True `
|
||||
/p:PipelineDependsOnBuild=False `
|
||||
/p:OutDir=$tmp\bin\\ `
|
||||
/p:WebProjectOutputDir=$tmp\WebApp\\ `
|
||||
/p:Verbosity=minimal `
|
||||
/t:Clean`;Rebuild `
|
||||
/tv:$toolsVersion `
|
||||
/p:UmbracoBuild=True `
|
||||
> $tmp\msbuild.umbraco.log
|
||||
|
||||
# /p:UmbracoBuild tells the csproj that we are building from PS
|
||||
}
|
||||
|
||||
#
|
||||
# Prepare Tests
|
||||
#
|
||||
function Prepare-Tests
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
|
||||
Write-Host ">> Prepare Tests"
|
||||
|
||||
# fixme - idea is to avoid rebuilding everything for tests
|
||||
# but because of our weird assembly versioning (with .* stuff)
|
||||
# everything gets rebuilt all the time...
|
||||
#Copy-Files "$tmp\bin" "." "$tmp\tests"
|
||||
|
||||
# data
|
||||
Write-Host "Copy data files"
|
||||
if( -Not (Test-Path -Path "$tmp\tests\Packaging" ) )
|
||||
{
|
||||
Write-Host "Create packaging directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\Packaging"
|
||||
}
|
||||
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
|
||||
|
||||
# required for package install tests
|
||||
if( -Not (Test-Path -Path "$tmp\tests\bin" ) )
|
||||
{
|
||||
Write-Host "Create bin directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\bin"
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Compiles Tests
|
||||
#
|
||||
function Compile-Tests
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$tmp\tests"
|
||||
|
||||
$buildConfiguration = "Release"
|
||||
|
||||
if ($uenv.VisualStudio -eq $null)
|
||||
{
|
||||
Write-Error "Build environment does not provide VisualStudio."
|
||||
break
|
||||
}
|
||||
|
||||
$toolsVersion = "4.0"
|
||||
if ($uenv.VisualStudio.Major -eq 15)
|
||||
{
|
||||
$toolsVersion = "15.0"
|
||||
}
|
||||
|
||||
Write-Host ">> Compile Tests"
|
||||
Write-Host "Logging to $tmp\msbuild.tests.log"
|
||||
|
||||
# beware of the weird double \\ at the end of paths
|
||||
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
|
||||
&$uenv.VisualStudio.MsBuild "$src\Umbraco.Tests\Umbraco.Tests.csproj" `
|
||||
/p:WarningLevel=0 `
|
||||
/p:Configuration=$buildConfiguration `
|
||||
/p:Platform=AnyCPU `
|
||||
/p:UseWPP_CopyWebApplication=True `
|
||||
/p:PipelineDependsOnBuild=False `
|
||||
/p:OutDir=$out\\ `
|
||||
/p:Verbosity=minimal `
|
||||
/t:Build `
|
||||
/tv:$toolsVersion `
|
||||
/p:UmbracoBuild=True `
|
||||
/p:NugetPackages=$src\packages `
|
||||
> $tmp\msbuild.tests.log
|
||||
|
||||
# /p:UmbracoBuild tells the csproj that we are building from PS
|
||||
}
|
||||
|
||||
#
|
||||
# Cleans things up and prepare files after compilation
|
||||
#
|
||||
function Prepare-Packages
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
Write-Host ">> Prepare Packages"
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
|
||||
$buildConfiguration = "Release"
|
||||
|
||||
# restore web.config
|
||||
Restore-WebConfig "$src\Umbraco.Web.UI"
|
||||
|
||||
# cleanup build
|
||||
Write-Host "Clean build"
|
||||
Remove-File "$tmp\bin\*.dll.config"
|
||||
Remove-File "$tmp\WebApp\bin\*.dll.config"
|
||||
|
||||
# cleanup presentation
|
||||
Write-Host "Cleanup presentation"
|
||||
Remove-Directory "$tmp\WebApp\umbraco.presentation"
|
||||
|
||||
# create directories
|
||||
Write-Host "Create directories"
|
||||
mkdir "$tmp\Configs" > $null
|
||||
mkdir "$tmp\Configs\Lang" > $null
|
||||
mkdir "$tmp\WebApp\App_Data" > $null
|
||||
#mkdir "$tmp\WebApp\Media" > $null
|
||||
#mkdir "$tmp\WebApp\Views" > $null
|
||||
|
||||
# copy various files
|
||||
Write-Host "Copy xml documentation"
|
||||
cp -force "$tmp\bin\*.xml" "$tmp\WebApp\bin"
|
||||
|
||||
Write-Host "Copy transformed configs and langs"
|
||||
# note: exclude imageprocessor/*.config as imageprocessor pkg installs them
|
||||
Copy-Files "$tmp\WebApp\config" "*.config" "$tmp\Configs" `
|
||||
{ -not $_.RelativeName.StartsWith("imageprocessor") }
|
||||
Copy-Files "$tmp\WebApp\config" "*.js" "$tmp\Configs"
|
||||
Copy-Files "$tmp\WebApp\config\lang" "*.xml" "$tmp\Configs\Lang"
|
||||
Copy-File "$tmp\WebApp\web.config" "$tmp\Configs\web.config.transform"
|
||||
|
||||
Write-Host "Copy transformed web.config"
|
||||
Copy-File "$src\Umbraco.Web.UI\web.$buildConfiguration.Config.transformed" "$tmp\WebApp\web.config"
|
||||
|
||||
# offset the modified timestamps on all umbraco dlls, as WebResources
|
||||
# break if date is in the future, which, due to timezone offsets can happen.
|
||||
Write-Host "Offset dlls timestamps"
|
||||
ls -r "$tmp\*.dll" | foreach {
|
||||
$_.CreationTime = $_.CreationTime.AddHours(-11)
|
||||
$_.LastWriteTime = $_.LastWriteTime.AddHours(-11)
|
||||
}
|
||||
|
||||
# copy libs
|
||||
Write-Host "Copy SqlCE libraries"
|
||||
Copy-Files "$src\packages\SqlServerCE.4.0.0.1" "*.*" "$tmp\bin" `
|
||||
{ -not $_.Extension.StartsWith(".nu") -and -not $_.RelativeName.StartsWith("lib\") }
|
||||
Copy-Files "$src\packages\SqlServerCE.4.0.0.1" "*.*" "$tmp\WebApp\bin" `
|
||||
{ -not $_.Extension.StartsWith(".nu") -and -not $_.RelativeName.StartsWith("lib\") }
|
||||
|
||||
# copy Belle
|
||||
Write-Host "Copy Belle"
|
||||
Copy-Files "$src\Umbraco.Web.UI\umbraco\assets" "*" "$tmp\WebApp\umbraco\assets"
|
||||
Copy-Files "$src\Umbraco.Web.UI\umbraco\js" "*" "$tmp\WebApp\umbraco\js"
|
||||
Copy-Files "$src\Umbraco.Web.UI\umbraco\lib" "*" "$tmp\WebApp\umbraco\lib"
|
||||
Copy-Files "$src\Umbraco.Web.UI\umbraco\views" "*" "$tmp\WebApp\umbraco\views"
|
||||
Copy-Files "$src\Umbraco.Web.UI\umbraco\preview" "*" "$tmp\WebApp\umbraco\preview"
|
||||
|
||||
# prepare WebPI
|
||||
Write-Host "Prepare WebPI"
|
||||
Remove-Directory "$tmp\WebPi"
|
||||
mkdir "$tmp\WebPi" > $null
|
||||
mkdir "$tmp\WebPi\umbraco" > $null
|
||||
Copy-Files "$tmp\WebApp" "*" "$tmp\WebPi\umbraco"
|
||||
Copy-Files "$src\WebPi" "*" "$tmp\WebPi"
|
||||
}
|
||||
|
||||
#
|
||||
# Creates the Zip packages
|
||||
#
|
||||
function Package-Zip
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
Write-Host ">> Create Zip packages"
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
|
||||
Write-Host "Zip all binaries"
|
||||
&$uenv.Zip a -r "$out\UmbracoCms.AllBinaries.$($version.Semver).zip" `
|
||||
"$tmp\bin\*" `
|
||||
"-x!dotless.Core.*" `
|
||||
> $null
|
||||
|
||||
Write-Host "Zip cms"
|
||||
&$uenv.Zip a -r "$out\UmbracoCms.$($version.Semver).zip" `
|
||||
"$tmp\WebApp\*" `
|
||||
"-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb"`
|
||||
> $null
|
||||
|
||||
Write-Host "Zip WebPI"
|
||||
&$uenv.Zip a -r "$out\UmbracoCms.WebPI.$($version.Semver).zip" "-x!*.pdb"`
|
||||
"$tmp\WebPi\*" `
|
||||
"-x!dotless.Core.*" `
|
||||
> $null
|
||||
|
||||
# hash the webpi file
|
||||
Write-Host "Hash WebPI"
|
||||
$hash = Get-FileHash "$out\UmbracoCms.WebPI.$($version.Semver).zip"
|
||||
Write $hash | out-file "$out\webpihash.txt" -encoding ascii
|
||||
}
|
||||
|
||||
#
|
||||
# Prepares NuGet
|
||||
#
|
||||
function Prepare-NuGet
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
Write-Host ">> Prepare NuGet"
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
|
||||
# add Web.config transform files to the NuGet package
|
||||
Write-Host "Add web.config transforms to NuGet package"
|
||||
mv "$tmp\WebApp\Views\Web.config" "$tmp\WebApp\Views\Web.config.transform"
|
||||
|
||||
# fixme - that one does not exist in .bat build either?
|
||||
#mv "$tmp\WebApp\Xslt\Web.config" "$tmp\WebApp\Xslt\Web.config.transform"
|
||||
}
|
||||
|
||||
#
|
||||
# Restores NuGet
|
||||
#
|
||||
function Restore-NuGet
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
|
||||
Write-Host ">> Restore NuGet"
|
||||
Write-Host "Logging to $tmp\nuget.restore.log"
|
||||
|
||||
&$uenv.NuGet restore "$src\Umbraco.sln" > "$tmp\nuget.restore.log"
|
||||
}
|
||||
|
||||
#
|
||||
# Creates the NuGet packages
|
||||
#
|
||||
function Package-NuGet
|
||||
{
|
||||
param (
|
||||
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
$version # an Umbraco version object (see Get-UmbracoVersion)
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
$nuspecs = "$($uenv.SolutionRoot)\build\NuSpecs"
|
||||
|
||||
Write-Host ">> Create NuGet packages"
|
||||
|
||||
# see https://docs.microsoft.com/en-us/nuget/schema/nuspec
|
||||
# note - warnings about SqlCE native libs being outside of 'lib' folder,
|
||||
# nothing much we can do about it as it's intentional yet there does not
|
||||
# seem to be a way to disable the warning
|
||||
|
||||
&$uenv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
|
||||
-Properties BuildTmp="$tmp" `
|
||||
-Version $version.Semver.ToString() `
|
||||
-Symbols -Verbosity quiet -outputDirectory $out
|
||||
|
||||
&$uenv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" `
|
||||
-Properties BuildTmp="$tmp" `
|
||||
-Version $version.Semver.ToString() `
|
||||
-Verbosity quiet -outputDirectory $out
|
||||
}
|
||||
|
||||
#
|
||||
# Builds Umbraco
|
||||
#
|
||||
function Build-Umbraco
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]
|
||||
$target = "all",
|
||||
[string]
|
||||
$buildConfiguration = "Release"
|
||||
)
|
||||
|
||||
$target = $target.ToLowerInvariant()
|
||||
Write-Host ">> Build-Umbraco <$target> <$buildConfiguration>"
|
||||
|
||||
Write-Host "Get Build Environment"
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
|
||||
Write-Host "Get Version"
|
||||
$version = Get-UmbracoVersion
|
||||
Write-Host "Version $($version.Semver)"
|
||||
|
||||
if ($target -eq "pre-build")
|
||||
{
|
||||
Prepare-Build $uenv
|
||||
#Compile-Belle $uenv $version
|
||||
|
||||
# set environment variables
|
||||
$env:UMBRACO_VERSION=$version.Semver.ToString()
|
||||
$env:UMBRACO_RELEASE=$version.Release
|
||||
$env:UMBRACO_COMMENT=$version.Comment
|
||||
$env:UMBRACO_BUILD=$version.Build
|
||||
|
||||
# set environment variable for VSO
|
||||
# https://github.com/Microsoft/vsts-tasks/issues/375
|
||||
# https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md
|
||||
Write-Host ("##vso[task.setvariable variable=UMBRACO_VERSION;]$($version.Semver.ToString())")
|
||||
Write-Host ("##vso[task.setvariable variable=UMBRACO_RELEASE;]$($version.Release)")
|
||||
Write-Host ("##vso[task.setvariable variable=UMBRACO_COMMENT;]$($version.Comment)")
|
||||
Write-Host ("##vso[task.setvariable variable=UMBRACO_BUILD;]$($version.Build)")
|
||||
|
||||
Write-Host ("##vso[task.setvariable variable=UMBRACO_TMP;]$($uenv.SolutionRoot)\build.tmp")
|
||||
}
|
||||
elseif ($target -eq "pre-tests")
|
||||
{
|
||||
Prepare-Tests $uenv
|
||||
}
|
||||
elseif ($target -eq "compile-tests")
|
||||
{
|
||||
Compile-Tests $uenv
|
||||
}
|
||||
elseif ($target -eq "compile-umbraco")
|
||||
{
|
||||
Compile-Umbraco $uenv $buildConfiguration
|
||||
}
|
||||
elseif ($target -eq "pre-packages")
|
||||
{
|
||||
Prepare-Packages $uenv
|
||||
}
|
||||
elseif ($target -eq "pre-nuget")
|
||||
{
|
||||
Prepare-NuGet $uenv
|
||||
}
|
||||
elseif ($target -eq "restore-nuget")
|
||||
{
|
||||
Restore-NuGet $uenv
|
||||
}
|
||||
elseif ($target -eq "pkg-zip")
|
||||
{
|
||||
Package-Zip $uenv
|
||||
}
|
||||
elseif ($target -eq "compile-belle")
|
||||
{
|
||||
Compile-Belle $uenv $version
|
||||
}
|
||||
elseif ($target -eq "all")
|
||||
{
|
||||
Prepare-Build $uenv
|
||||
Restore-NuGet $uenv
|
||||
Compile-Belle $uenv $version
|
||||
Compile-Umbraco $uenv $buildConfiguration
|
||||
Prepare-Tests $uenv
|
||||
Compile-Tests $uenv
|
||||
# not running tests...
|
||||
Prepare-Packages $uenv
|
||||
Package-Zip $uenv
|
||||
Verify-NuGet $uenv
|
||||
Prepare-NuGet $uenv
|
||||
Package-NuGet $uenv $version
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Error "Unsupported target `"$target`"."
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# export functions
|
||||
#
|
||||
Export-ModuleMember -function Get-UmbracoBuildEnv
|
||||
Export-ModuleMember -function Set-UmbracoVersion
|
||||
Export-ModuleMember -function Set-UmbracoContinuousVersion
|
||||
Export-ModuleMember -function Get-UmbracoVersion
|
||||
Export-ModuleMember -function Build-Umbraco
|
||||
Export-ModuleMember -function Build-UmbracoDocs
|
||||
Export-ModuleMember -function Verify-NuGet
|
||||
|
||||
#eof
|
||||
@@ -0,0 +1,120 @@
|
||||
# returns a string containing the hash of $file
|
||||
function Get-FileHash($file)
|
||||
{
|
||||
try
|
||||
{
|
||||
$crypto = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
|
||||
$stream = [System.IO.File]::OpenRead($file)
|
||||
$hash = $crypto.ComputeHash($stream)
|
||||
$text = ""
|
||||
$hash | foreach `
|
||||
{
|
||||
$text = $text + $_.ToString("x2")
|
||||
}
|
||||
return $text
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($stream)
|
||||
{
|
||||
$stream.Dispose()
|
||||
}
|
||||
$crypto.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
# returns the full path if $file is relative to $pwd
|
||||
function Get-FullPath($file)
|
||||
{
|
||||
$path = [System.IO.Path]::Combine($pwd, $file)
|
||||
$path = [System.IO.Path]::GetFullPath($path)
|
||||
return $path
|
||||
}
|
||||
|
||||
# removes a directory, doesn't complain if it does not exist
|
||||
function Remove-Directory($dir)
|
||||
{
|
||||
remove-item $dir -force -recurse -errorAction SilentlyContinue > $null
|
||||
}
|
||||
|
||||
# removes a file, doesn't complain if it does not exist
|
||||
function Remove-File($file)
|
||||
{
|
||||
remove-item $file -force -errorAction SilentlyContinue > $null
|
||||
}
|
||||
|
||||
# copies a file, creates target dir if needed
|
||||
function Copy-File($source, $target)
|
||||
{
|
||||
$ignore = new-item -itemType file -path $target -force
|
||||
cp -force $source $target
|
||||
}
|
||||
|
||||
# copies files to a directory
|
||||
function Copy-Files($source, $select, $target, $filter)
|
||||
{
|
||||
$files = ls -r "$source\$select"
|
||||
$files | foreach {
|
||||
$relative = $_.FullName.SubString($source.Length+1)
|
||||
$_ | add-member -memberType NoteProperty -name RelativeName -value $relative
|
||||
}
|
||||
if ($filter -ne $null) {
|
||||
$files = $files | where $filter
|
||||
}
|
||||
$files |
|
||||
foreach {
|
||||
if ($_.PsIsContainer) {
|
||||
$ignore = new-item -itemType directory -path "$target\$($_.RelativeName)" -force
|
||||
}
|
||||
else {
|
||||
Copy-File $_.FullName "$target\$($_.RelativeName)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# regex-replaces content in a file
|
||||
function Replace-FileText($filename, $source, $replacement)
|
||||
{
|
||||
$filepath = Get-FullPath $filename
|
||||
$text = [System.IO.File]::ReadAllText($filepath)
|
||||
$text = [System.Text.RegularExpressions.Regex]::Replace($text, $source, $replacement)
|
||||
$utf8bom = New-Object System.Text.UTF8Encoding $true
|
||||
[System.IO.File]::WriteAllText($filepath, $text, $utf8bom)
|
||||
}
|
||||
|
||||
# store web.config
|
||||
function Store-WebConfig($webUi)
|
||||
{
|
||||
if (test-path "$webUi\web.config")
|
||||
{
|
||||
if (test-path "$webUi\web.config.temp-build")
|
||||
{
|
||||
Write-Host "Found existing web.config.temp-build"
|
||||
$i = 0
|
||||
while (test-path "$webUi\web.config.temp-build.$i")
|
||||
{
|
||||
$i = $i + 1
|
||||
}
|
||||
Write-Host "Save existing web.config as web.config.temp-build.$i"
|
||||
Write-Host "(WARN: the original web.config.temp-build will be restored during post-build)"
|
||||
mv "$webUi\web.config" "$webUi\web.config.temp-build.$i"
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "Save existing web.config as web.config.temp-build"
|
||||
Write-Host "(will be restored during post-build)"
|
||||
mv "$webUi\web.config" "$webUi\web.config.temp-build"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# restore web.config
|
||||
function Restore-WebConfig($webUi)
|
||||
{
|
||||
if (test-path "$webUi\web.config.temp-build")
|
||||
{
|
||||
Write-Host "Restoring existing web.config"
|
||||
Remove-File "$webUi\web.config"
|
||||
mv "$webUi\web.config.temp-build" "$webUi\web.config"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
#
|
||||
# Verify-NuGet
|
||||
#
|
||||
|
||||
function Format-Dependency
|
||||
{
|
||||
param ( $d )
|
||||
|
||||
$m = $d.Id + " "
|
||||
if ($d.MinInclude) { $m = $m + "[" }
|
||||
else { $m = $m + "(" }
|
||||
$m = $m + $d.MinVersion
|
||||
if ($d.MaxVersion -ne $d.MinVersion) { $m = $m + "," + $d.MaxVersion }
|
||||
if ($d.MaxInclude) { $m = $m + "]" }
|
||||
else { $m = $m + ")" }
|
||||
|
||||
return $m
|
||||
}
|
||||
|
||||
function Write-NuSpec
|
||||
{
|
||||
param ( $name, $deps )
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "$name NuSpec dependencies:"
|
||||
|
||||
foreach ($d in $deps)
|
||||
{
|
||||
$m = Format-Dependency $d
|
||||
Write-Host " $m"
|
||||
}
|
||||
}
|
||||
|
||||
function Write-Package
|
||||
{
|
||||
param ( $name, $pkgs )
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "$name packages:"
|
||||
|
||||
foreach ($p in $pkgs)
|
||||
{
|
||||
Write-Host " $($p.Id) $($p.Version)"
|
||||
}
|
||||
}
|
||||
|
||||
function Verify-NuGet
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
if ($uenv -eq $null)
|
||||
{
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
}
|
||||
|
||||
$source = @"
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Semver;
|
||||
|
||||
namespace Umbraco.Build
|
||||
{
|
||||
public class NuGet
|
||||
{
|
||||
public static Dependency[] GetNuSpecDependencies(string filename)
|
||||
{
|
||||
NuSpec nuspec;
|
||||
var serializer = new XmlSerializer(typeof(NuSpec));
|
||||
using (var reader = new StreamReader(filename))
|
||||
{
|
||||
nuspec = (NuSpec) serializer.Deserialize(reader);
|
||||
}
|
||||
var nudeps = nuspec.Metadata.Dependencies;
|
||||
var deps = new List<Dependency>();
|
||||
foreach (var nudep in nudeps)
|
||||
{
|
||||
var dep = new Dependency();
|
||||
dep.Id = nudep.Id;
|
||||
|
||||
var parts = nudep.Version.Split(',');
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
dep.MinInclude = parts[0].StartsWith("[");
|
||||
dep.MaxInclude = parts[0].EndsWith("]");
|
||||
|
||||
SemVersion version;
|
||||
if (!SemVersion.TryParse(parts[0].Substring(1, parts[0].Length-2).Trim(), out version)) continue;
|
||||
dep.MinVersion = dep.MaxVersion = version; //parts[0].Substring(1, parts[0].Length-2).Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
SemVersion version;
|
||||
if (!SemVersion.TryParse(parts[0].Substring(1).Trim(), out version)) continue;
|
||||
dep.MinVersion = version; //parts[0].Substring(1).Trim();
|
||||
if (!SemVersion.TryParse(parts[1].Substring(0, parts[1].Length-1).Trim(), out version)) continue;
|
||||
dep.MaxVersion = version; //parts[1].Substring(0, parts[1].Length-1).Trim();
|
||||
dep.MinInclude = parts[0].StartsWith("[");
|
||||
dep.MaxInclude = parts[1].EndsWith("]");
|
||||
}
|
||||
|
||||
deps.Add(dep);
|
||||
}
|
||||
return deps.ToArray();
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(/*this*/ IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
HashSet<TKey> knownKeys = new HashSet<TKey>();
|
||||
foreach (TSource element in source)
|
||||
{
|
||||
if (knownKeys.Add(keySelector(element)))
|
||||
{
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Package[] GetProjectsPackages(string src, string[] projects)
|
||||
{
|
||||
var l = new List<Package>();
|
||||
foreach (var project in projects)
|
||||
{
|
||||
var path = Path.Combine(src, project);
|
||||
var packageConfig = Path.Combine(path, "packages.config");
|
||||
if (File.Exists(packageConfig))
|
||||
ReadPackagesConfig(packageConfig, l);
|
||||
var csprojs = Directory.GetFiles(path, "*.csproj");
|
||||
foreach (var csproj in csprojs)
|
||||
{
|
||||
ReadCsProj(csproj, l);
|
||||
}
|
||||
}
|
||||
IEnumerable<Package> p = l.OrderBy(x => x.Id);
|
||||
p = DistinctBy(p, x => x.Id + ":::" + x.Version);
|
||||
return p.ToArray();
|
||||
}
|
||||
|
||||
public static object[] GetPackageErrors(Package[] pkgs)
|
||||
{
|
||||
return pkgs
|
||||
.GroupBy(x => x.Id)
|
||||
.Where(x => x.Count() > 1)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static object[] GetNuSpecErrors(Package[] pkgs, Dependency[] deps)
|
||||
{
|
||||
var d = pkgs.ToDictionary(x => x.Id, x => x.Version);
|
||||
return deps
|
||||
.Select(x =>
|
||||
{
|
||||
SemVersion v;
|
||||
if (!d.TryGetValue(x.Id, out v)) return null;
|
||||
|
||||
var ok = true;
|
||||
|
||||
/*
|
||||
if (x.MinInclude)
|
||||
{
|
||||
if (v < x.MinVersion) ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v <= x.MinVersion) ok = false;
|
||||
}
|
||||
|
||||
if (x.MaxInclude)
|
||||
{
|
||||
if (v > x.MaxVersion) ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v >= x.MaxVersion) ok = false;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!x.MinInclude || v != x.MinVersion) ok = false;
|
||||
|
||||
return ok ? null : new { Dependency = x, Version = v };
|
||||
})
|
||||
.Where(x => x != null)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/*
|
||||
public static Package[] GetProjectPackages(string path)
|
||||
{
|
||||
var l = new List<Package>();
|
||||
var packageConfig = Path.Combine(path, "packages.config");
|
||||
if (File.Exists(packageConfig))
|
||||
ReadPackagesConfig(packageConfig, l);
|
||||
var csprojs = Directory.GetFiles(path, "*.csproj");
|
||||
foreach (var csproj in csprojs)
|
||||
{
|
||||
ReadCsProj(csproj, l);
|
||||
}
|
||||
return l.ToArray();
|
||||
}
|
||||
*/
|
||||
|
||||
public static string GetDirectoryName(string filename)
|
||||
{
|
||||
return Path.GetFileName(Path.GetDirectoryName(filename));
|
||||
}
|
||||
|
||||
public static void ReadPackagesConfig(string filename, List<Package> packages)
|
||||
{
|
||||
//Console.WriteLine("read " + filename);
|
||||
|
||||
PackagesConfigPackages pkgs;
|
||||
var serializer = new XmlSerializer(typeof(PackagesConfigPackages));
|
||||
using (var reader = new StreamReader(filename))
|
||||
{
|
||||
pkgs = (PackagesConfigPackages) serializer.Deserialize(reader);
|
||||
}
|
||||
foreach (var p in pkgs.Packages)
|
||||
{
|
||||
SemVersion version;
|
||||
if (!SemVersion.TryParse(p.Version, out version)) continue;
|
||||
packages.Add(new Package { Id = p.Id, Version = version, Project = GetDirectoryName(filename) });
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReadCsProj(string filename, List<Package> packages)
|
||||
{
|
||||
//Console.WriteLine("read " + filename);
|
||||
|
||||
// if xmlns then it's not a VS2017 with PackageReference
|
||||
var text = File.ReadAllLines(filename);
|
||||
var line = text.FirstOrDefault(x => x.Contains("<Project"));
|
||||
if (line == null) return;
|
||||
if (line.Contains("xmlns")) return;
|
||||
|
||||
CsProjProject proj;
|
||||
var serializer = new XmlSerializer(typeof(CsProjProject));
|
||||
using (var reader = new StreamReader(filename))
|
||||
{
|
||||
proj = (CsProjProject) serializer.Deserialize(reader);
|
||||
}
|
||||
foreach (var p in proj.ItemGroups.Where(x => x.Packages != null).SelectMany(x => x.Packages))
|
||||
{
|
||||
var sversion = p.VersionE ?? p.VersionA;
|
||||
SemVersion version;
|
||||
if (!SemVersion.TryParse(sversion, out version)) continue;
|
||||
packages.Add(new Package { Id = p.Id, Version = version, Project = GetDirectoryName(filename) });
|
||||
}
|
||||
}
|
||||
|
||||
public class Dependency
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public SemVersion MinVersion { get; set; }
|
||||
public SemVersion MaxVersion { get; set; }
|
||||
public bool MinInclude { get; set; }
|
||||
public bool MaxInclude { get; set; }
|
||||
}
|
||||
|
||||
public class Package
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public SemVersion Version { get; set; }
|
||||
public string Project { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd")]
|
||||
[XmlRoot(Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", IsNullable = false, ElementName = "package")]
|
||||
public class NuSpec
|
||||
{
|
||||
[XmlElement("metadata")]
|
||||
public NuSpecMetadata Metadata { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "metadata")]
|
||||
public class NuSpecMetadata
|
||||
{
|
||||
[XmlArray("dependencies")]
|
||||
[XmlArrayItem("dependency", IsNullable = false)]
|
||||
public NuSpecDependency[] Dependencies { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "dependencies")]
|
||||
public class NuSpecDependency
|
||||
{
|
||||
[XmlAttribute(AttributeName = "id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlAttribute(AttributeName = "version")]
|
||||
public string Version { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true)]
|
||||
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "packages")]
|
||||
public class PackagesConfigPackages
|
||||
{
|
||||
[XmlElement("package")]
|
||||
public PackagesConfigPackage[] Packages { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, TypeName = "package")]
|
||||
public class PackagesConfigPackage
|
||||
{
|
||||
[XmlAttribute(AttributeName = "id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlAttribute(AttributeName = "version")]
|
||||
public string Version { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true)]
|
||||
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "Project")]
|
||||
public class CsProjProject
|
||||
{
|
||||
[XmlElement("ItemGroup")]
|
||||
public CsProjItemGroup[] ItemGroups { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, TypeName = "ItemGroup")]
|
||||
public class CsProjItemGroup
|
||||
{
|
||||
[XmlElement("PackageReference")]
|
||||
public CsProjPackageReference[] Packages { get; set; }
|
||||
}
|
||||
|
||||
[XmlType(AnonymousType = true, TypeName = "PackageReference")]
|
||||
public class CsProjPackageReference
|
||||
{
|
||||
[XmlAttribute(AttributeName = "Include")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlAttribute(AttributeName = "Version")]
|
||||
public string VersionA { get; set; }
|
||||
|
||||
[XmlElement("Version")]
|
||||
public string VersionE { get; set;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"@
|
||||
|
||||
Write-Host ">> Verify NuGet consistency"
|
||||
|
||||
$assem = (
|
||||
"System.Xml",
|
||||
"System.Core", # "System.Collections.Generic"
|
||||
"System.Linq",
|
||||
"System.Xml.Serialization",
|
||||
"System.IO",
|
||||
"System.Globalization",
|
||||
$uenv.Semver
|
||||
)
|
||||
|
||||
try
|
||||
{
|
||||
# as long as the code hasn't changed it's fine to re-add, but if the code
|
||||
# has changed this will throw - better warn the dev that we have an issue
|
||||
add-type -referencedAssemblies $assem -typeDefinition $source -language CSharp
|
||||
}
|
||||
catch
|
||||
{
|
||||
if ($_.FullyQualifiedErrorId.StartsWith("TYPE_ALREADY_EXISTS,"))
|
||||
{ Write-Error "Failed to add type, did you change the code?" }
|
||||
else
|
||||
{ Write-Error $_ }
|
||||
}
|
||||
if (-not $?) { break }
|
||||
|
||||
$nuspecs = (
|
||||
"UmbracoCms",
|
||||
"UmbracoCms.Core"
|
||||
)
|
||||
|
||||
$projects = (
|
||||
"Umbraco.Core",
|
||||
"Umbraco.Web",
|
||||
"Umbraco.Web.UI",
|
||||
"UmbracoExamine"#,
|
||||
#"Umbraco.Tests",
|
||||
#"Umbraco.Tests.Benchmarks"
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$pkgs = [Umbraco.Build.NuGet]::GetProjectsPackages($src, $projects)
|
||||
if (-not $?) { break }
|
||||
#Write-Package "All" $pkgs
|
||||
|
||||
$errs = [Umbraco.Build.NuGet]::GetPackageErrors($pkgs)
|
||||
if (-not $?) { break }
|
||||
|
||||
if ($errs.Length -gt 0)
|
||||
{
|
||||
Write-Host ""
|
||||
}
|
||||
foreach ($err in $errs)
|
||||
{
|
||||
Write-Host $err.Key
|
||||
foreach ($e in $err)
|
||||
{
|
||||
Write-Host " $($e.Version) required by $($e.Project)"
|
||||
}
|
||||
}
|
||||
if ($errs.Length -gt 0)
|
||||
{
|
||||
Write-Error "Found non-consolidated package dependencies"
|
||||
break
|
||||
}
|
||||
|
||||
$nuerr = $false
|
||||
$nupath = "$($uenv.SolutionRoot)\build\NuSpecs"
|
||||
foreach ($nuspec in $nuspecs)
|
||||
{
|
||||
$deps = [Umbraco.Build.NuGet]::GetNuSpecDependencies("$nupath\$nuspec.nuspec")
|
||||
if (-not $?) { break }
|
||||
#Write-NuSpec $nuspec $deps
|
||||
|
||||
$errs = [Umbraco.Build.NuGet]::GetNuSpecErrors($pkgs, $deps)
|
||||
if (-not $?) { break }
|
||||
|
||||
if ($errs.Length -gt 0)
|
||||
{
|
||||
Write-Host ""
|
||||
Write-Host "$nuspec requires:"
|
||||
$nuerr = $true
|
||||
}
|
||||
foreach ($err in $errs)
|
||||
{
|
||||
$m = Format-Dependency $err.Dependency
|
||||
Write-Host " $m but projects require $($err.Version)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($nuerr)
|
||||
{
|
||||
Write-Error "Found inconsistent NuGet dependencies"
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,9 @@
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.82, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.3, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.3, 5.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.88, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
@@ -44,65 +44,65 @@
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
|
||||
<file src="$BuildTmp$\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
<file src="..\_BuildOutput\bin\SQLCE4Umbraco.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\SQLCE4Umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\SQLCE4Umbraco\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\SQLCE4Umbraco" />
|
||||
<file src="..\_BuildOutput\bin\businesslogic.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\businesslogic.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.businesslogic\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.businesslogic" />
|
||||
<file src="..\_BuildOutput\bin\cms.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\cms.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.cms\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.cms" />
|
||||
<file src="..\_BuildOutput\bin\controls.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\controls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.controls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.controls" />
|
||||
<file src="..\_BuildOutput\bin\interfaces.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\interfaces.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.interfaces\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.interfaces" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Core.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Core\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.DataLayer.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\umbraco.DataLayer.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.datalayer\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.datalayer" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.editorControls.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\umbraco.editorControls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.editorControls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.editorControls" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.MacroEngines.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\umbraco.MacroEngines.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.MacroEngines\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.MacroEngines" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.providers.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\umbraco.providers.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.providers\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.providers" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Web.UI.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Web.UI.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web.UI\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web.UI" />
|
||||
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -19,22 +19,22 @@
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\Configs\**" target="Content\Config" exclude="..\_BuildOutput\Configs\Web.config.transform" />
|
||||
<file src="..\_BuildOutput\WebApp\Views\**" target="Content\Views" exclude="..\_BuildOutput\WebApp\Views\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="..\_BuildOutput\WebApp\Media\Web.config" target="Content\Media\Web.config" />
|
||||
<file src="$BuildTmp$\Configs\**" target="Content\Config" exclude="$BuildTmp$\Configs\Web.config.transform" />
|
||||
<file src="$BuildTmp$\WebApp\Views\**" target="Content\Views" exclude="$BuildTmp$\WebApp\Views\Web.config" />
|
||||
<file src="$BuildTmp$\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="$BuildTmp$\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="$BuildTmp$\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="$BuildTmp$\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="$BuildTmp$\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
|
||||
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="$BuildTmp$\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
|
||||
<file src="tools\install.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\Readme.txt" target="tools\Readme.txt" />
|
||||
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" alias="dictionary" title="Dictionary" type="umbraco.loadDictionary, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6"
|
||||
|
||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.3
|
||||
@@ -0,0 +1,67 @@
|
||||
param (
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]
|
||||
$version,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[Alias("mo")]
|
||||
[switch]
|
||||
$moduleOnly = $false
|
||||
)
|
||||
|
||||
# the script can run either from the solution root,
|
||||
# or from the ./build directory - anything else fails
|
||||
if ([System.IO.Path]::GetFileName($pwd) -eq "build")
|
||||
{
|
||||
$mpath = [System.IO.Path]::GetDirectoryName($pwd) + "\build\Modules\"
|
||||
}
|
||||
else
|
||||
{
|
||||
$mpath = "$pwd\build\Modules\"
|
||||
}
|
||||
|
||||
# look for the module and throw if not found
|
||||
if (-not [System.IO.Directory]::Exists($mpath + "Umbraco.Build"))
|
||||
{
|
||||
Write-Error "Could not locate Umbraco build Powershell module."
|
||||
break
|
||||
}
|
||||
|
||||
# add the module path (if not already there)
|
||||
if (-not $env:PSModulePath.Contains($mpath))
|
||||
{
|
||||
$env:PSModulePath = "$mpath;$env:PSModulePath"
|
||||
}
|
||||
|
||||
# force-import (or re-import) the module
|
||||
Write-Host "Import Umbraco build Powershell module"
|
||||
Import-Module Umbraco.Build -Force -DisableNameChecking
|
||||
|
||||
# module only?
|
||||
if ($moduleOnly)
|
||||
{
|
||||
if (-not [string]::IsNullOrWhiteSpace($version))
|
||||
{
|
||||
Write-Host "(module only: ignoring version parameter)"
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "(module only)"
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
# get build environment
|
||||
Write-Host "Setup Umbraco build Environment"
|
||||
$uenv = Get-UmbracoBuildEnv
|
||||
|
||||
# set the version if any
|
||||
if (-not [string]::IsNullOrWhiteSpace($version))
|
||||
{
|
||||
Write-Host "Set Umbraco version to $version"
|
||||
Set-UmbracoVersion $version
|
||||
}
|
||||
|
||||
# full umbraco build
|
||||
Write-Host "Build Umbraco"
|
||||
Build-Umbraco
|
||||
@@ -0,0 +1,18 @@
|
||||
# Usage: powershell .\setversion.ps1 7.6.8
|
||||
# Or: powershell .\setversion 7.6.8-beta001
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$version
|
||||
)
|
||||
|
||||
# report
|
||||
Write-Host "Setting Umbraco version to $version"
|
||||
|
||||
# import Umbraco Build PowerShell module - $pwd is ./build
|
||||
$env:PSModulePath = "$pwd\Modules\;$env:PSModulePath"
|
||||
Import-Module Umbraco.Build -Force -DisableNameChecking
|
||||
|
||||
# run commands
|
||||
$version = Set-UmbracoVersion -Version $version
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
</configuration>
|
||||
Binary file not shown.
@@ -1,138 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
|
||||
|
||||
<!-- Enable the restore command to run before builds -->
|
||||
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
|
||||
|
||||
<!-- Property that enables building a package from a project -->
|
||||
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
|
||||
|
||||
<!-- Determines if package restore consent is required to restore packages -->
|
||||
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
|
||||
|
||||
<!-- Download NuGet.exe if it does not already exist -->
|
||||
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(PackageSources)' == '' ">
|
||||
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
|
||||
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
|
||||
<!--
|
||||
<PackageSource Include="https://www.nuget.org/api/v2/" />
|
||||
<PackageSource Include="https://my-nuget-source/nuget/" />
|
||||
-->
|
||||
<PackageSource Include="https://nuget.org/api/v2/" />
|
||||
<PackageSource Include="http://www.myget.org/F/umbracocore/" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
|
||||
<!-- Windows specific commands -->
|
||||
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
|
||||
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
|
||||
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
|
||||
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
|
||||
<PackagesConfig>packages.config</PackagesConfig>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- NuGet command -->
|
||||
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
|
||||
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
|
||||
|
||||
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
|
||||
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
|
||||
|
||||
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
|
||||
|
||||
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
|
||||
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
|
||||
|
||||
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
|
||||
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
|
||||
|
||||
<!-- Commands -->
|
||||
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
|
||||
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
|
||||
|
||||
<!-- We need to ensure packages are restored prior to assembly resolve -->
|
||||
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
|
||||
RestorePackages;
|
||||
$(BuildDependsOn);
|
||||
</BuildDependsOn>
|
||||
|
||||
<!-- Make the build depend on restore packages -->
|
||||
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
|
||||
$(BuildDependsOn);
|
||||
BuildPackage;
|
||||
</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CheckPrerequisites">
|
||||
<!-- Raise an error if we're unable to locate nuget.exe -->
|
||||
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
|
||||
<!--
|
||||
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
|
||||
This effectively acts as a lock that makes sure that the download operation will only happen once and all
|
||||
parallel builds will have to wait for it to complete.
|
||||
-->
|
||||
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_DownloadNuGet">
|
||||
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
|
||||
<Exec Command="$(RestoreCommand)"
|
||||
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||
|
||||
<Exec Command="$(RestoreCommand)"
|
||||
LogStandardErrorAsError="true"
|
||||
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
|
||||
<Exec Command="$(BuildCommand)"
|
||||
Condition=" '$(OS)' != 'Windows_NT' " />
|
||||
|
||||
<Exec Command="$(BuildCommand)"
|
||||
LogStandardErrorAsError="true"
|
||||
Condition=" '$(OS)' == 'Windows_NT' " />
|
||||
</Target>
|
||||
|
||||
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
<ParameterGroup>
|
||||
<OutputFilename ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Reference Include="System.Core" />
|
||||
<Using Namespace="System" />
|
||||
<Using Namespace="System.IO" />
|
||||
<Using Namespace="System.Net" />
|
||||
<Using Namespace="Microsoft.Build.Framework" />
|
||||
<Using Namespace="Microsoft.Build.Utilities" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
try {
|
||||
OutputFilename = Path.GetFullPath(OutputFilename);
|
||||
|
||||
Log.LogMessage("Downloading latest version of NuGet.exe...");
|
||||
WebClient webClient = new WebClient();
|
||||
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Log.LogErrorFromException(ex);
|
||||
return false;
|
||||
}
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
</Project>
|
||||
@@ -93,7 +93,6 @@
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.3")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.3")]
|
||||
[assembly: AssemblyFileVersion("7.6.12")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.12")]
|
||||
@@ -23,12 +23,12 @@ namespace Umbraco.Core
|
||||
: base(serviceProvider, logger, packageActions)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPackageAction"/> implementations.
|
||||
/// </summary>
|
||||
public IEnumerable<IAction> Actions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IAction"/> implementations.
|
||||
/// </summary>
|
||||
public IEnumerable<IAction> Actions
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Core
|
||||
/// <param name="cache"></param>
|
||||
[Obsolete("Use the other constructor specifying a ProfilingLogger instead")]
|
||||
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
|
||||
: this(dbContext, serviceContext, cache,
|
||||
: this(dbContext, serviceContext, cache,
|
||||
new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler))
|
||||
{
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Core
|
||||
/// <param name="replaceContext">If set to true and the singleton is already set, it will be replaced</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
|
||||
{
|
||||
@@ -107,14 +107,14 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
/// <param name="replaceContext">
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// startup if for some reason the boot manager is not the umbraco boot manager.
|
||||
/// </param>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
[Obsolete("Use the other method specifying an ProfilingLogger instead")]
|
||||
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
|
||||
@@ -135,14 +135,14 @@ namespace Umbraco.Core
|
||||
/// <param name="cache"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="replaceContext">
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// startup if for some reason the boot manager is not the umbraco boot manager.
|
||||
/// </param>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger, bool replaceContext)
|
||||
{
|
||||
@@ -175,7 +175,7 @@ namespace Umbraco.Core
|
||||
public CacheHelper ApplicationCache { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exposes the global ProfilingLogger - this should generally not be accessed via the UmbracoContext and should normally just be exposed
|
||||
/// Exposes the global ProfilingLogger - this should generally not be accessed via the UmbracoContext and should normally just be exposed
|
||||
/// on most base classes or injected with IoC
|
||||
/// </summary>
|
||||
public ProfilingLogger ProfilingLogger { get; private set; }
|
||||
@@ -213,7 +213,7 @@ namespace Umbraco.Core
|
||||
// GlobalSettings.CurrentVersion returns the hard-coded "current version"
|
||||
// the system is configured if they match
|
||||
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
|
||||
|
||||
|
||||
public bool IsConfigured
|
||||
{
|
||||
get { return _configured.Value; }
|
||||
@@ -226,8 +226,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsConfigured == false
|
||||
&& DatabaseContext != null
|
||||
if (IsConfigured == false
|
||||
&& DatabaseContext != null
|
||||
&& DatabaseContext.IsDatabaseConfigured)
|
||||
{
|
||||
var schemaresult = DatabaseContext.ValidateDatabaseSchema();
|
||||
@@ -274,12 +274,12 @@ namespace Umbraco.Core
|
||||
|
||||
private Lazy<bool> _configured;
|
||||
internal MainDom MainDom { get; private set; }
|
||||
|
||||
|
||||
private void Init()
|
||||
{
|
||||
MainDom = new MainDom(ProfilingLogger.Logger);
|
||||
MainDom.Acquire();
|
||||
|
||||
|
||||
//Create the lazy value to resolve whether or not the application is 'configured'
|
||||
_configured = new Lazy<bool>(() =>
|
||||
{
|
||||
@@ -289,7 +289,7 @@ namespace Umbraco.Core
|
||||
var currentVersion = UmbracoVersion.GetSemanticVersion();
|
||||
|
||||
var ok =
|
||||
//we are not configured if this is null
|
||||
//we are not configured if this is null
|
||||
string.IsNullOrWhiteSpace(configStatus) == false
|
||||
//they must match
|
||||
&& configStatus == currentVersion;
|
||||
@@ -303,7 +303,7 @@ namespace Umbraco.Core
|
||||
var found = Services.MigrationEntryService.FindEntry(Constants.System.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion());
|
||||
if (found == null)
|
||||
{
|
||||
//we haven't executed this migration in this environment, so even though the config versions match,
|
||||
//we haven't executed this migration in this environment, so even though the config versions match,
|
||||
// this db has not been updated.
|
||||
ProfilingLogger.Logger.Debug<ApplicationContext>(string.Format("The migration for version: '{0} has not been executed, there is no record in the database", currentVersion.ToSemanticString()));
|
||||
ok = false;
|
||||
@@ -323,7 +323,7 @@ namespace Umbraco.Core
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private string ConfigurationStatus
|
||||
@@ -338,7 +338,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertIsNotReady()
|
||||
@@ -363,7 +363,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
internal set { _databaseContext = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current ServiceContext
|
||||
/// </summary>
|
||||
@@ -417,7 +417,7 @@ namespace Umbraco.Core
|
||||
ResolverCollection.ResetAll();
|
||||
//reset resolution itself (though this should be taken care of by resetting any of the resolvers above)
|
||||
Resolution.Reset();
|
||||
|
||||
|
||||
//reset the instance objects
|
||||
this.ApplicationCache = null;
|
||||
if (_databaseContext != null) //need to check the internal field here
|
||||
@@ -430,14 +430,14 @@ namespace Umbraco.Core
|
||||
/*
|
||||
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null)
|
||||
{
|
||||
DatabaseContext.Database.Dispose();
|
||||
}
|
||||
*/
|
||||
DatabaseContext.Database.Dispose();
|
||||
}
|
||||
*/
|
||||
}
|
||||
this.DatabaseContext = null;
|
||||
this.Services = null;
|
||||
this._isReady = false; //set the internal field
|
||||
|
||||
|
||||
// Indicate that the instance has been disposed.
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Core.Auditing
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used by events raised from hthe BackofficeUserManager
|
||||
/// </summary>
|
||||
public class IdentityAuditEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The action that got triggered from the audit event
|
||||
/// </summary>
|
||||
public AuditEvent Action { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current date/time in UTC format
|
||||
/// </summary>
|
||||
public DateTime DateTimeUtc { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source IP address of the user performing the action
|
||||
/// </summary>
|
||||
public string IpAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user affected by the event raised
|
||||
/// </summary>
|
||||
public int AffectedUser { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a user is perfoming an action on a different user, then this will be set. Otherwise it will be -1
|
||||
/// </summary>
|
||||
public int PerformingUser { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional comment about the action being logged
|
||||
/// </summary>
|
||||
public string Comment { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This property is always empty except in the LoginFailed event for an unknown user trying to login
|
||||
/// </summary>
|
||||
public string Username { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the properties on the event being raised, all parameters are optional except for the action being performed
|
||||
/// </summary>
|
||||
/// <param name="action">An action based on the AuditEvent enum</param>
|
||||
/// <param name="ipAddress">The client's IP address. This is usually automatically set but could be overridden if necessary</param>
|
||||
/// <param name="performingUser">The Id of the user performing the action (if different from the user affected by the action)</param>
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, int performingUser = -1)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
Action = action;
|
||||
|
||||
IpAddress = ipAddress;
|
||||
|
||||
PerformingUser = performingUser == -1
|
||||
? GetCurrentRequestBackofficeUserId()
|
||||
: performingUser;
|
||||
}
|
||||
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
Action = action;
|
||||
|
||||
IpAddress = ipAddress;
|
||||
Username = username;
|
||||
Comment = comment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current logged in backoffice user's Id logging if there is one
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected int GetCurrentRequestBackofficeUserId()
|
||||
{
|
||||
var userId = -1;
|
||||
var backOfficeIdentity = Thread.CurrentPrincipal.GetUmbracoIdentity();
|
||||
if (backOfficeIdentity != null)
|
||||
int.TryParse(backOfficeIdentity.Id.ToString(), out userId);
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AuditEvent
|
||||
{
|
||||
AccountLocked,
|
||||
AccountUnlocked,
|
||||
ForgotPasswordRequested,
|
||||
ForgotPasswordChangedSuccess,
|
||||
LoginFailed,
|
||||
LoginRequiresVerification,
|
||||
LoginSucces,
|
||||
LogoutSuccess,
|
||||
PasswordChanged,
|
||||
PasswordReset,
|
||||
ResetAccessFailedCount
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Core.Cache
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
|
||||
|
||||
public const string IdToKeyCacheKey = "UI2K";
|
||||
public const string KeyToIdCacheKey = "UK2I";
|
||||
public const string IdToKeyCacheKey = "UI2K__";
|
||||
public const string KeyToIdCacheKey = "UK2I__";
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Xml.Linq;
|
||||
using ClientDependency.Core.CompositeFiles.Providers;
|
||||
using ClientDependency.Core.Config;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal class ClientDependencyConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// A utility class for working with CDF config and cache files - use sparingly!
|
||||
/// </summary>
|
||||
public class ClientDependencyConfiguration
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _fileName;
|
||||
@@ -21,7 +28,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// <summary>
|
||||
/// Changes the version number in ClientDependency.config to a random value to avoid stale caches
|
||||
/// </summary>
|
||||
internal bool IncreaseVersionNumber()
|
||||
public bool IncreaseVersionNumber()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -49,5 +56,55 @@ namespace Umbraco.Core.Configuration
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the temporary files stored for the ClientDependency folder
|
||||
/// </summary>
|
||||
/// <param name="currentHttpContext"></param>
|
||||
public bool ClearTempFiles(HttpContextBase currentHttpContext)
|
||||
{
|
||||
var cdfTempDirectories = new HashSet<string>();
|
||||
foreach (BaseCompositeFileProcessingProvider provider in ClientDependencySettings.Instance
|
||||
.CompositeFileProcessingProviderCollection)
|
||||
{
|
||||
var path = provider.CompositeFilePath.FullName;
|
||||
cdfTempDirectories.Add(path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var fullPath = currentHttpContext.Server.MapPath(XmlFileMapper.FileMapVirtualFolder);
|
||||
if (fullPath != null)
|
||||
{
|
||||
cdfTempDirectories.Add(fullPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//invalid path format or something... try/catch to be safe
|
||||
LogHelper.Error<ClientDependencyConfiguration>("Could not get path from ClientDependency.config", ex);
|
||||
}
|
||||
|
||||
var success = true;
|
||||
foreach (var directory in cdfTempDirectories)
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(directory);
|
||||
if (directoryInfo.Exists == false)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
directoryInfo.Delete(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Something could be locking the directory or the was another error, making sure we don't break the upgrade installer
|
||||
LogHelper.Error<ClientDependencyConfiguration>("Could not clear temp files", ex);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,24 +518,41 @@ namespace Umbraco.Core.Configuration
|
||||
|
||||
internal static bool ContentCacheXmlStoredInCodeGen
|
||||
{
|
||||
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
|
||||
get { return LocalTempStorageLocation == LocalTempStorage.AspNetTemp; }
|
||||
}
|
||||
|
||||
internal static ContentXmlStorage ContentCacheXmlStorageLocation
|
||||
|
||||
/// <summary>
|
||||
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently used for the xml cache file and the plugin cache files
|
||||
/// </remarks>
|
||||
internal static LocalTempStorage LocalTempStorageLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
{
|
||||
//there's a bunch of backwards compat config checks here....
|
||||
|
||||
//This is the current one
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage"))
|
||||
{
|
||||
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoLocalTempStorage"]);
|
||||
}
|
||||
|
||||
//This one is old
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
|
||||
{
|
||||
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
|
||||
}
|
||||
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
|
||||
}
|
||||
|
||||
//This one is older
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
|
||||
{
|
||||
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
|
||||
? ContentXmlStorage.AspNetTemp
|
||||
: ContentXmlStorage.Default;
|
||||
? LocalTempStorage.AspNetTemp
|
||||
: LocalTempStorage.Default;
|
||||
}
|
||||
return ContentXmlStorage.Default;
|
||||
return LocalTempStorage.Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal enum ContentXmlStorage
|
||||
internal enum LocalTempStorage
|
||||
{
|
||||
Default,
|
||||
AspNetTemp,
|
||||
@@ -12,6 +12,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
bool ConvertUrlsToAscii { get; }
|
||||
|
||||
bool TryConvertUrlsToAscii { get; }
|
||||
|
||||
IEnumerable<IChar> CharCollection { get; }
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
bool IRequestHandlerSection.ConvertUrlsToAscii
|
||||
{
|
||||
get { return UrlReplacing.ConvertUrlsToAscii; }
|
||||
get { return UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); }
|
||||
}
|
||||
|
||||
bool IRequestHandlerSection.TryConvertUrlsToAscii
|
||||
{
|
||||
get { return UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); }
|
||||
}
|
||||
|
||||
IEnumerable<IChar> IRequestHandlerSection.CharCollection
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return (bool) base["removeDoubleDashes"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("toAscii", DefaultValue = false)]
|
||||
internal bool ConvertUrlsToAscii
|
||||
[ConfigurationProperty("toAscii", DefaultValue = "false")]
|
||||
internal string ConvertUrlsToAscii
|
||||
{
|
||||
get { return (bool)base["toAscii"]; }
|
||||
get { return (string) base["toAscii"]; }
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(CharCollection), AddItemName = "char")]
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.6.3");
|
||||
private static readonly Version Version = new Version("7.6.12");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -33,9 +33,9 @@ namespace Umbraco.Core.Configuration
|
||||
public static SemVersion GetSemanticVersion()
|
||||
{
|
||||
return new SemVersion(
|
||||
Current.Major,
|
||||
Current.Major,
|
||||
Current.Minor,
|
||||
Current.Build,
|
||||
Current.Build,
|
||||
CurrentComment.IsNullOrWhiteSpace() ? null : CurrentComment,
|
||||
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
|
||||
}
|
||||
|
||||
@@ -212,6 +212,26 @@ namespace Umbraco.Core
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsDataSourceGuid = new Guid(FormsDataSource);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Language.
|
||||
/// </summary>
|
||||
public const string Language = "6B05D05B-EC78-49BE-A4E4-79E274F07A77";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public static readonly Guid LanguageGuid = new Guid(Language);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for an Identifier Reservation.
|
||||
/// </summary>
|
||||
public const string IdReservation = "92849B1E-3904-4713-9356-F646F87C25F4";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for an Identifier Reservation.
|
||||
/// </summary>
|
||||
public static readonly Guid IdReservationGuid = new Guid(IdReservation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace Umbraco.Core
|
||||
public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken";
|
||||
public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
|
||||
|
||||
internal const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
|
||||
|
||||
/// <summary>
|
||||
/// The prefix used for external identity providers for their authentication type
|
||||
/// </summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using AutoMapper;
|
||||
@@ -399,6 +400,28 @@ namespace Umbraco.Core
|
||||
if (ApplicationContext.IsConfigured == false) return;
|
||||
if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
|
||||
|
||||
// deal with localdb
|
||||
var databaseContext = ApplicationContext.DatabaseContext;
|
||||
var localdbex = new Regex(@"\(localdb\)\\([a-zA-Z0-9-_]+)(;|$)");
|
||||
var m = localdbex.Match(databaseContext.ConnectionString);
|
||||
if (m.Success)
|
||||
{
|
||||
var instanceName = m.Groups[1].Value;
|
||||
ProfilingLogger.Logger.Info<CoreBootManager>(string.Format("LocalDb instance \"{0}\"", instanceName));
|
||||
|
||||
var localDb = new LocalDb();
|
||||
if (localDb.IsAvailable == false)
|
||||
throw new UmbracoStartupFailedException("Umbraco cannot start. LocalDb is not available.");
|
||||
|
||||
if (localDb.InstanceExists(m.Groups[1].Value) == false)
|
||||
{
|
||||
if (localDb.CreateInstance(instanceName) == false)
|
||||
throw new UmbracoStartupFailedException(string.Format("Umbraco cannot start. LocalDb cannot create instance \"{0}\".", instanceName));
|
||||
if (localDb.StartInstance(instanceName) == false)
|
||||
throw new UmbracoStartupFailedException(string.Format("Umbraco cannot start. LocalDb cannot start instance \"{0}\".", instanceName));
|
||||
}
|
||||
}
|
||||
|
||||
//try now
|
||||
if (ApplicationContext.DatabaseContext.CanConnect)
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
@@ -38,5 +39,10 @@ namespace Umbraco.Core.Deploy
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <returns>The item with the specified key and type, if any, else null.</returns>
|
||||
T Item<T>(string key) where T : class;
|
||||
|
||||
///// <summary>
|
||||
///// Gets the global deployment cancellation token.
|
||||
///// </summary>
|
||||
//CancellationToken CancellationToken { get; }
|
||||
}
|
||||
}
|
||||
@@ -295,5 +295,16 @@ namespace Umbraco.Core
|
||||
return list1Groups.Count == list2Groups.Count
|
||||
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
|
||||
}
|
||||
|
||||
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
|
||||
{
|
||||
using (var e = source.GetEnumerator())
|
||||
{
|
||||
if (e.MoveNext() == false) yield break;
|
||||
|
||||
for (var value = e.Current; e.MoveNext(); value = e.Current)
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Security.Permissions;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
@@ -13,6 +12,9 @@ namespace Umbraco.Core.Events
|
||||
public class CancellableEventArgs : EventArgs, IEquatable<CancellableEventArgs>
|
||||
{
|
||||
private bool _cancel;
|
||||
private Dictionary<string, object> _eventState;
|
||||
|
||||
private static readonly ReadOnlyDictionary<string, object> EmptyAdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
|
||||
|
||||
public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
{
|
||||
@@ -26,7 +28,7 @@ namespace Umbraco.Core.Events
|
||||
if (eventMessages == null) throw new ArgumentNullException("eventMessages");
|
||||
CanCancel = canCancel;
|
||||
Messages = eventMessages;
|
||||
AdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
|
||||
AdditionalData = EmptyAdditionalData;
|
||||
}
|
||||
|
||||
public CancellableEventArgs(bool canCancel)
|
||||
@@ -34,18 +36,16 @@ namespace Umbraco.Core.Events
|
||||
CanCancel = canCancel;
|
||||
//create a standalone messages
|
||||
Messages = new EventMessages();
|
||||
AdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
|
||||
AdditionalData = EmptyAdditionalData;
|
||||
}
|
||||
|
||||
public CancellableEventArgs(EventMessages eventMessages)
|
||||
: this(true, eventMessages)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public CancellableEventArgs()
|
||||
: this(true)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Flag to determine if this instance will support being cancellable
|
||||
@@ -95,10 +95,19 @@ namespace Umbraco.Core.Events
|
||||
/// In some cases raised evens might need to contain additional arbitrary readonly data which can be read by event subscribers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
|
||||
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
|
||||
/// so we cannot change the strongly typed nature for some events.
|
||||
/// </remarks>
|
||||
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This can be used by event subscribers to store state in the event args so they easily deal with custom state data between a starting ("ing")
|
||||
/// event and an ending ("ed") event
|
||||
/// </summary>
|
||||
public IDictionary<string, object> EventState
|
||||
{
|
||||
get { return _eventState ?? (_eventState = new Dictionary<string, object>()); }
|
||||
}
|
||||
|
||||
public bool Equals(CancellableEventArgs other)
|
||||
{
|
||||
@@ -111,13 +120,13 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((CancellableEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (AdditionalData != null ? AdditionalData.GetHashCode() : 0);
|
||||
return AdditionalData != null ? AdditionalData.GetHashCode() : 0;
|
||||
}
|
||||
|
||||
public static bool operator ==(CancellableEventArgs left, CancellableEventArgs right)
|
||||
@@ -127,7 +136,7 @@ namespace Umbraco.Core.Events
|
||||
|
||||
public static bool operator !=(CancellableEventArgs left, CancellableEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
return Equals(left, right) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,8 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
public IEnumerable<TEntity> DeletedEntities
|
||||
{
|
||||
get { return EventObject; }
|
||||
get { return EventObject; }
|
||||
internal set { EventObject = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||
public class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Events
|
||||
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
//TODO: MetaData for package metadata has no equality operators :/
|
||||
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<MoveEventArgs<TEntity>>
|
||||
{
|
||||
private IEnumerable<MoveEventInfo<TEntity>> _moveInfoCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
/// </summary>
|
||||
@@ -107,7 +109,24 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Gets all MoveEventInfo objects used to create the object
|
||||
/// </summary>
|
||||
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection { get; private set; }
|
||||
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection
|
||||
{
|
||||
get { return _moveInfoCollection; }
|
||||
set
|
||||
{
|
||||
var first = value.FirstOrDefault();
|
||||
if (first == null)
|
||||
{
|
||||
throw new InvalidOperationException("MoveInfoCollection must have at least one item");
|
||||
}
|
||||
|
||||
_moveInfoCollection = value;
|
||||
|
||||
//assign the legacy props
|
||||
EventObject = first.Entity;
|
||||
ParentId = first.NewParentId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity being moved
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the Recycle Bin was emptied successfully
|
||||
/// </summary>
|
||||
public bool RecycleBinEmptiedSuccessfully { get; private set; }
|
||||
public bool RecycleBinEmptiedSuccessfully { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether this event was fired for the Content's Recycle Bin.
|
||||
|
||||
@@ -3,7 +3,7 @@ using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
|
||||
@@ -32,7 +32,11 @@ namespace Umbraco.Core
|
||||
public GuidUdi(Uri uriValue)
|
||||
: base(uriValue)
|
||||
{
|
||||
Guid = Guid.Parse(uriValue.AbsolutePath.TrimStart('/'));
|
||||
Guid guid;
|
||||
if (Guid.TryParse(uriValue.AbsolutePath.TrimStart('/'), out guid) == false)
|
||||
throw new FormatException("Url \"" + uriValue + "\" is not a guid entity id.");
|
||||
|
||||
Guid = guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,16 +47,17 @@ namespace Umbraco.Core
|
||||
public new static GuidUdi Parse(string s)
|
||||
{
|
||||
var udi = Udi.Parse(s);
|
||||
if (!(udi is GuidUdi))
|
||||
if (udi is GuidUdi == false)
|
||||
throw new FormatException("String \"" + s + "\" is not a guid entity id.");
|
||||
return (GuidUdi)udi;
|
||||
|
||||
return (GuidUdi) udi;
|
||||
}
|
||||
|
||||
public static bool TryParse(string s, out GuidUdi udi)
|
||||
{
|
||||
Udi tmp;
|
||||
udi = null;
|
||||
if (!TryParse(s, out tmp)) return false;
|
||||
if (TryParse(s, out tmp) == false) return false;
|
||||
udi = tmp as GuidUdi;
|
||||
return udi != null;
|
||||
}
|
||||
@@ -75,10 +80,9 @@ namespace Umbraco.Core
|
||||
get { return Guid == Guid.Empty; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public GuidUdi EnsureClosed()
|
||||
{
|
||||
base.EnsureNotRoot();
|
||||
EnsureNotRoot();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to create a hash code from multiple objects.
|
||||
/// Used to create a .NET HashCode from multiple objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things
|
||||
/// which we've not included here as we just need a quick easy class for this in order to create a unique
|
||||
/// hash of directories/files to see if they have changed.
|
||||
///
|
||||
/// NOTE: It's probably best to not relying on the hashing result across AppDomains! If you need a constant/reliable hash value
|
||||
/// between AppDomains use SHA1. This is perfect for hashing things in a very fast way for a single AppDomain.
|
||||
/// </remarks>
|
||||
internal class HashCodeCombiner
|
||||
{
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to generate a string hash using crypto libraries over multiple objects
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be used to generate a reliable hash that survives AppDomain restarts.
|
||||
/// This will use the crypto libs to generate the hash and will try to ensure that
|
||||
/// strings, etc... are not re-allocated so it's not consuming much memory.
|
||||
/// </remarks>
|
||||
internal class HashGenerator : DisposableObject
|
||||
{
|
||||
public HashGenerator()
|
||||
{
|
||||
_writer = new StreamWriter(_ms, Encoding.Unicode, 1024, leaveOpen: true);
|
||||
}
|
||||
|
||||
private readonly MemoryStream _ms = new MemoryStream();
|
||||
private StreamWriter _writer;
|
||||
|
||||
internal void AddInt(int i)
|
||||
{
|
||||
_writer.Write(i);
|
||||
}
|
||||
|
||||
internal void AddLong(long i)
|
||||
{
|
||||
_writer.Write(i);
|
||||
}
|
||||
|
||||
internal void AddObject(object o)
|
||||
{
|
||||
_writer.Write(o);
|
||||
}
|
||||
|
||||
internal void AddDateTime(DateTime d)
|
||||
{
|
||||
_writer.Write(d.Ticks);;
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
_writer.Write(s);
|
||||
}
|
||||
|
||||
internal void AddCaseInsensitiveString(string s)
|
||||
{
|
||||
//I've tried to no allocate a new string with this which can be done if we use the CompareInfo.GetSortKey method which will create a new
|
||||
//byte array that we can use to write to the output, however this also allocates new objects so i really don't think the performance
|
||||
//would be much different. In any case, i'll leave this here for reference. We could write the bytes out based on the sort key,
|
||||
//this is how we could deal with case insensitivity without allocating another string
|
||||
//for reference see: https://stackoverflow.com/a/10452967/694494
|
||||
//we could go a step further and s.Normalize() but we're not really dealing with crazy unicode with this class so far.
|
||||
|
||||
if (s != null)
|
||||
_writer.Write(s.ToUpperInvariant());
|
||||
}
|
||||
|
||||
internal void AddFileSystemItem(FileSystemInfo f)
|
||||
{
|
||||
//if it doesn't exist, don't proceed.
|
||||
if (f.Exists == false)
|
||||
return;
|
||||
|
||||
AddCaseInsensitiveString(f.FullName);
|
||||
AddDateTime(f.CreationTimeUtc);
|
||||
AddDateTime(f.LastWriteTimeUtc);
|
||||
|
||||
//check if it is a file or folder
|
||||
var fileInfo = f as FileInfo;
|
||||
if (fileInfo != null)
|
||||
{
|
||||
AddLong(fileInfo.Length);
|
||||
}
|
||||
|
||||
var dirInfo = f as DirectoryInfo;
|
||||
if (dirInfo != null)
|
||||
{
|
||||
foreach (var d in dirInfo.GetFiles())
|
||||
{
|
||||
AddFile(d);
|
||||
}
|
||||
foreach (var s in dirInfo.GetDirectories())
|
||||
{
|
||||
AddFolder(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddFile(FileInfo f)
|
||||
{
|
||||
AddFileSystemItem(f);
|
||||
}
|
||||
|
||||
internal void AddFolder(DirectoryInfo d)
|
||||
{
|
||||
AddFileSystemItem(d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the generated hash output of all added objects
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal string GenerateHash()
|
||||
{
|
||||
//flush,close,dispose the writer,then create a new one since it's possible to keep adding after GenerateHash is called.
|
||||
|
||||
_writer.Flush();
|
||||
_writer.Close();
|
||||
_writer.Dispose();
|
||||
_writer = new StreamWriter(_ms, Encoding.UTF8, 1024, leaveOpen: true);
|
||||
|
||||
var hashType = CryptoConfig.AllowOnlyFipsAlgorithms ? "SHA1" : "MD5";
|
||||
|
||||
//create an instance of the correct hashing provider based on the type passed in
|
||||
var hasher = HashAlgorithm.Create(hashType);
|
||||
if (hasher == null) throw new InvalidOperationException("No hashing type found by name " + hashType);
|
||||
using (hasher)
|
||||
{
|
||||
var buffer = _ms.GetBuffer();
|
||||
//get the hashed values created by our selected provider
|
||||
var hashedByteArray = hasher.ComputeHash(buffer);
|
||||
|
||||
//create a StringBuilder object
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
//loop to each each byte
|
||||
foreach (var b in hashedByteArray)
|
||||
{
|
||||
//append it to our StringBuilder
|
||||
stringBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
//return the hashed value
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_writer.Close();
|
||||
_writer.Dispose();
|
||||
_ms.Close();
|
||||
_ms.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
private ShadowWrapper _macroPartialFileSystem;
|
||||
private ShadowWrapper _partialViewsFileSystem;
|
||||
private ShadowWrapper _macroScriptsFileSystem;
|
||||
private ShadowWrapper _userControlsFileSystem;
|
||||
private ShadowWrapper _stylesheetsFileSystem;
|
||||
private ShadowWrapper _scriptsFileSystem;
|
||||
private ShadowWrapper _xsltFileSystem;
|
||||
@@ -61,6 +63,8 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
var macroPartialFileSystem = new PhysicalFileSystem(SystemDirectories.MacroPartials);
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews);
|
||||
var macroScriptsFileSystem = new PhysicalFileSystem(SystemDirectories.MacroScripts);
|
||||
var userControlsFileSystem = new PhysicalFileSystem(SystemDirectories.UserControls);
|
||||
var stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css);
|
||||
var scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts);
|
||||
var xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt);
|
||||
@@ -69,6 +73,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", ScopeProvider);
|
||||
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", ScopeProvider);
|
||||
_macroScriptsFileSystem = new ShadowWrapper(macroScriptsFileSystem, "macroScripts", ScopeProvider);
|
||||
_userControlsFileSystem = new ShadowWrapper(userControlsFileSystem, "usercontrols", ScopeProvider);
|
||||
_stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", ScopeProvider);
|
||||
_scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", ScopeProvider);
|
||||
_xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", ScopeProvider);
|
||||
@@ -85,6 +91,10 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public IFileSystem2 MacroPartialsFileSystem { get { return _macroPartialFileSystem; } }
|
||||
public IFileSystem2 PartialViewsFileSystem { get { return _partialViewsFileSystem; } }
|
||||
// Legacy /macroScripts folder
|
||||
public IFileSystem2 MacroScriptsFileSystem { get { return _macroScriptsFileSystem; } }
|
||||
// Legacy /usercontrols folder
|
||||
public IFileSystem2 UserControlsFileSystem { get { return _userControlsFileSystem; } }
|
||||
public IFileSystem2 StylesheetsFileSystem { get { return _stylesheetsFileSystem; } }
|
||||
public IFileSystem2 ScriptsFileSystem { get { return _scriptsFileSystem; } }
|
||||
public IFileSystem2 XsltFileSystem { get { return _xsltFileSystem; } }
|
||||
@@ -252,13 +262,15 @@ namespace Umbraco.Core.IO
|
||||
internal ICompletable Shadow(Guid id)
|
||||
{
|
||||
var typed = _wrappers.ToArray();
|
||||
var wrappers = new ShadowWrapper[typed.Length + 7];
|
||||
var wrappers = new ShadowWrapper[typed.Length + 9];
|
||||
var i = 0;
|
||||
while (i < typed.Length) wrappers[i] = typed[i++];
|
||||
wrappers[i++] = _macroPartialFileSystem;
|
||||
wrappers[i++] = _macroScriptsFileSystem;
|
||||
wrappers[i++] = _partialViewsFileSystem;
|
||||
wrappers[i++] = _stylesheetsFileSystem;
|
||||
wrappers[i++] = _scriptsFileSystem;
|
||||
wrappers[i++] = _userControlsFileSystem;
|
||||
wrappers[i++] = _xsltFileSystem;
|
||||
wrappers[i++] = _masterPagesFileSystem;
|
||||
wrappers[i] = _mvcViewsFileSystem;
|
||||
|
||||
@@ -97,6 +97,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static string MapPath(string path, bool useHttpContext)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException("path");
|
||||
|
||||
// Check if the path is already mapped
|
||||
if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar)
|
||||
|| path.StartsWith(@"\\")) //UNC Paths start with "\\". If the site is running off a network drive mapped paths will look like "\\Whatever\Boo\Bar"
|
||||
|
||||
@@ -73,11 +73,11 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (GlobalSettings.ContentCacheXmlStorageLocation)
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case ContentXmlStorage.AspNetTemp:
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
|
||||
case ContentXmlStorage.EnvironmentTemp:
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
@@ -85,7 +85,7 @@ namespace Umbraco.Core.IO
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
return Path.Combine(cachePath, "umbraco.config");
|
||||
case ContentXmlStorage.Default:
|
||||
case LocalTempStorage.Default:
|
||||
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -264,7 +263,7 @@ namespace Umbraco.Core.Models
|
||||
/// <returns><see cref="Property"/> Value as an <see cref="object"/></returns>
|
||||
public virtual object GetValue(string propertyTypeAlias)
|
||||
{
|
||||
return Properties[propertyTypeAlias].Value;
|
||||
return Properties.Contains(propertyTypeAlias) ? Properties[propertyTypeAlias].Value : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -275,6 +274,11 @@ namespace Umbraco.Core.Models
|
||||
/// <returns><see cref="Property"/> Value as a <see cref="TPassType"/></returns>
|
||||
public virtual TPassType GetValue<TPassType>(string propertyTypeAlias)
|
||||
{
|
||||
if (Properties.Contains(propertyTypeAlias) == false)
|
||||
{
|
||||
return default(TPassType);
|
||||
}
|
||||
|
||||
var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo<TPassType>();
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
@@ -102,7 +99,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
|
||||
internal virtual void ResetIdentity()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IUserControl : IFile
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
|
||||
private string GetPasswordHash(string storedPass)
|
||||
{
|
||||
return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass;
|
||||
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,30 @@ namespace Umbraco.Core.Models
|
||||
IsApproved = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Member object
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="rawPasswordValue">
|
||||
/// The password value passed in to this parameter should be the encoded/encrypted/hashed format of the member's password
|
||||
/// </param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="isApproved"></param>
|
||||
public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType, bool isApproved)
|
||||
: base(name, -1, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentTypeAlias = contentType.Alias;
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_rawPasswordValue = rawPasswordValue;
|
||||
IsApproved = isApproved;
|
||||
}
|
||||
|
||||
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
|
||||
|
||||
private class PropertySelectors
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
Unknown = 0, // default
|
||||
PartialView = 1,
|
||||
PartialViewMacro = 2
|
||||
PartialViewMacro = 2,
|
||||
MacroScript = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ namespace Umbraco.Core.Models
|
||||
//NOTE: Consider checking type before value is set: item.PropertyType.DataTypeId == property.PropertyType.DataTypeId
|
||||
//Transfer the existing value to the new property
|
||||
var property = this[key];
|
||||
if (item.Id == 0 && property.Id != 0)
|
||||
{
|
||||
item.Id = property.Id;
|
||||
}
|
||||
if (item.Value == null && property.Value != null)
|
||||
{
|
||||
item.Value = property.Value;
|
||||
|
||||
@@ -178,6 +178,20 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsDataSource)]
|
||||
[FriendlyName("DataSource")]
|
||||
FormsDataSource
|
||||
FormsDataSource,
|
||||
|
||||
/// <summary>
|
||||
/// Language
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Language)]
|
||||
[FriendlyName("Language")]
|
||||
Language,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved Identifier
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.IdReservation)]
|
||||
[FriendlyName("Identifier Reservation")]
|
||||
IdReservation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a UserControl file
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class UserControl : File, IUserControl
|
||||
{
|
||||
public UserControl(string path)
|
||||
: this(path, (Func<File, string>)null)
|
||||
{ }
|
||||
|
||||
internal UserControl(string path, Func<File, string> getFileContent)
|
||||
: base(path, getFileContent)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity has an identity, which in this case is a path/name.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Overrides the default Entity identity check.
|
||||
/// </remarks>
|
||||
public override bool HasIdentity
|
||||
{
|
||||
get { return string.IsNullOrEmpty(Path) == false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="textService"></param>
|
||||
/// <returns></returns>
|
||||
/// <returns></returns>
|
||||
public static CultureInfo GetUserCulture(this IUser user, ILocalizedTextService textService)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
@@ -34,7 +35,7 @@ namespace Umbraco.Core.Models
|
||||
catch (CultureNotFoundException)
|
||||
{
|
||||
//return the default one
|
||||
return CultureInfo.GetCultureInfo("en");
|
||||
return CultureInfo.GetCultureInfo(GlobalSettings.DefaultUILanguage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallationSummary
|
||||
public class InstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<IDataTypeDefinition> DataTypesInstalled { get; set; }
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class MetaData
|
||||
public class MetaData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Version { get; set; }
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
internal enum ActionRunAt
|
||||
public enum ActionRunAt
|
||||
{
|
||||
Undefined = 0,
|
||||
Install,
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Packaging.Models
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PackageAction
|
||||
public class PackageAction
|
||||
{
|
||||
private ActionRunAt _runAt;
|
||||
private bool? _undo;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UninstallationSummary
|
||||
public class UninstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<IDataTypeDefinition> DataTypesUninstalled { get; set; }
|
||||
|
||||
@@ -0,0 +1,972 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages LocalDB databases.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Latest version is SQL Server 2016 Express LocalDB,
|
||||
/// see https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-2016-express-localdb
|
||||
/// which can be installed by downloading the Express installer from https://www.microsoft.com/en-us/sql-server/sql-server-downloads
|
||||
/// (about 5MB) then select 'download media' to download SqlLocalDB.msi (about 44MB), which you can execute. This installs
|
||||
/// LocalDB only. Though you probably want to install the full Express. You may also want to install SQL Server Management
|
||||
/// Studio which can be used to connect to LocalDB databases.</para>
|
||||
/// <para>See also https://github.com/ritterim/automation-sql which is a somewhat simpler version of this.</para>
|
||||
/// </remarks>
|
||||
internal class LocalDb
|
||||
{
|
||||
private int _version;
|
||||
private bool _hasVersion;
|
||||
private string _exe;
|
||||
|
||||
#region Availability & Version
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LocalDb installed version.
|
||||
/// </summary>
|
||||
/// <remarks>If more than one version is installed, returns the highest available. Returns
|
||||
/// the major version as an integer e.g. 11, 12...</remarks>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
public int Version
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureVersion();
|
||||
if (_version <= 0)
|
||||
throw new InvalidOperationException("LocalDb is not available.");
|
||||
return _version;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the LocalDb version is detected.
|
||||
/// </summary>
|
||||
private void EnsureVersion()
|
||||
{
|
||||
if (_hasVersion) return;
|
||||
DetectVersion();
|
||||
_hasVersion = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether LocalDb is available.
|
||||
/// </summary>
|
||||
public bool IsAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureVersion();
|
||||
return _version > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that LocalDb is available.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
private void EnsureAvailable()
|
||||
{
|
||||
if (IsAvailable == false)
|
||||
throw new InvalidOperationException("LocalDb is not available.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects LocalDb installed version.
|
||||
/// </summary>
|
||||
/// <remarks>If more than one version is installed, the highest available is detected.</remarks>
|
||||
private void DetectVersion()
|
||||
{
|
||||
_hasVersion = true;
|
||||
_version = -1;
|
||||
_exe = null;
|
||||
|
||||
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
|
||||
|
||||
// MS SQL Server installs in e.g. "C:\Program Files\Microsoft SQL Server", so
|
||||
// we want to detect it in "%ProgramFiles%\Microsoft SQL Server" - however, if
|
||||
// Umbraco runs as a 32bits process (e.g. IISExpress configured as 32bits)
|
||||
// on a 64bits system, %ProgramFiles% will point to "C:\Program Files (x86)"
|
||||
// and SQL Server cannot be found. But then, %ProgramW6432% will point to
|
||||
// the original "C:\Program Files". Using it to fix the path.
|
||||
// see also: MSDN doc for WOW64 implementation
|
||||
//
|
||||
var programW6432 = Environment.GetEnvironmentVariable("ProgramW6432");
|
||||
if (string.IsNullOrWhiteSpace(programW6432) == false && programW6432 != programFiles)
|
||||
programFiles = programW6432;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(programFiles)) return;
|
||||
|
||||
// detect 14, 13, 12, 11
|
||||
for (var i = 14; i > 10; i--)
|
||||
{
|
||||
var exe = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
|
||||
if (File.Exists(exe) == false) continue;
|
||||
_version = i;
|
||||
_exe = exe;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instances
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of existing LocalDb instances.
|
||||
/// </summary>
|
||||
/// <returns>The name of existing LocalDb instances.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
public string[] GetInstances()
|
||||
{
|
||||
EnsureAvailable();
|
||||
string output, error;
|
||||
var rc = ExecuteSqlLocalDb("i", out output, out error); // info
|
||||
if (rc != 0 || error != string.Empty) return null;
|
||||
return output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a LocalDb instance exists.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>A value indicating whether a LocalDb instance with the specified name exists.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
public bool InstanceExists(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
var instances = GetInstances();
|
||||
return instances != null && instances.Contains(instanceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>A value indicating whether the instance was created without errors.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
public bool CreateInstance(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
string output, error;
|
||||
return ExecuteSqlLocalDb(string.Format("c \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>A value indicating whether the instance was dropped without errors.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
/// <remarks>
|
||||
/// When an instance is dropped all the attached database files are deleted.
|
||||
/// Successful if the instance does not exist.
|
||||
/// </remarks>
|
||||
public bool DropInstance(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
var instance = GetInstance(instanceName);
|
||||
if (instance == null) return true;
|
||||
instance.DropDatabases(); // else the files remain
|
||||
|
||||
// -i force NOWAIT, -k kills
|
||||
string output, error;
|
||||
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty
|
||||
&& ExecuteSqlLocalDb(string.Format("d \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>A value indicating whether the instance was stopped without errors.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
/// <remarks>
|
||||
/// Successful if the instance does not exist.
|
||||
/// </remarks>
|
||||
public bool StopInstance(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
if (InstanceExists(instanceName) == false) return true;
|
||||
|
||||
// -i force NOWAIT, -k kills
|
||||
string output, error;
|
||||
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>A value indicating whether the instance was started without errors.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
/// <remarks>
|
||||
/// Failed if the instance does not exist.
|
||||
/// </remarks>
|
||||
public bool StartInstance(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
if (InstanceExists(instanceName) == false) return false;
|
||||
string output, error;
|
||||
return ExecuteSqlLocalDb(string.Format("s \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The name of the instance.</param>
|
||||
/// <returns>The instance with the specified name if it exists, otherwise null.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
|
||||
public Instance GetInstance(string instanceName)
|
||||
{
|
||||
EnsureAvailable();
|
||||
return InstanceExists(instanceName) ? new Instance(instanceName) : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Databases
|
||||
|
||||
/// <summary>
|
||||
/// Represents a LocalDb instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// LocalDb is assumed to be available, and the instance is assumed to exist.
|
||||
/// </remarks>
|
||||
public class Instance
|
||||
{
|
||||
private readonly string _masterCstr;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the instance.
|
||||
/// </summary>
|
||||
public string InstanceName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Instance"/> class.
|
||||
/// </summary>
|
||||
/// <param name="instanceName"></param>
|
||||
public Instance(string instanceName)
|
||||
{
|
||||
InstanceName = instanceName;
|
||||
_masterCstr = string.Format(@"Server=(localdb)\{0};Integrated Security=True;", instanceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a LocalDb connection string.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <returns>The connection string for the specified database.</returns>
|
||||
/// <remarks>
|
||||
/// The database should exist in the LocalDb instance.
|
||||
/// </remarks>
|
||||
public string GetConnectionString(string databaseName)
|
||||
{
|
||||
return _masterCstr + string.Format(@"Database={0};", databaseName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a LocalDb connection string for an attached database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="filesPath">The directory containing database files.</param>
|
||||
/// <returns>The connection string for the specified database.</returns>
|
||||
/// <remarks>
|
||||
/// The database should not exist in the LocalDb instance.
|
||||
/// It will be attached with its name being its MDF filename (full path), uppercased, when
|
||||
/// the first connection is opened, and remain attached until explicitely detached.
|
||||
/// </remarks>
|
||||
public string GetAttachedConnectionString(string databaseName, string filesPath)
|
||||
{
|
||||
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
|
||||
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
|
||||
|
||||
return _masterCstr + string.Format(@"AttachDbFileName='{0}';", mdfFilename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of existing databases.
|
||||
/// </summary>
|
||||
/// <returns>The name of existing databases.</returns>
|
||||
public string[] GetDatabases()
|
||||
{
|
||||
var userDatabases = new List<string>();
|
||||
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var databases = new Dictionary<string, string>();
|
||||
|
||||
SetCommand(cmd, @"
|
||||
SELECT name, filename FROM sys.sysdatabases");
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
databases[reader.GetString(0)] = reader.GetString(1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var database in databases)
|
||||
{
|
||||
var dbname = database.Key;
|
||||
|
||||
if (dbname == "master" || dbname == "tempdb" || dbname == "model" || dbname == "msdb")
|
||||
continue;
|
||||
|
||||
// fixme - shall we deal with stale databases?
|
||||
// fixme - is it always ok to assume file names?
|
||||
//var mdf = database.Value;
|
||||
//var ldf = mdf.Replace(".mdf", "_log.ldf");
|
||||
//if (staleOnly && File.Exists(mdf) && File.Exists(ldf))
|
||||
// continue;
|
||||
|
||||
//ExecuteDropDatabase(cmd, dbname, mdf, ldf);
|
||||
//count++;
|
||||
|
||||
userDatabases.Add(dbname);
|
||||
}
|
||||
}
|
||||
|
||||
return userDatabases.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a database exists.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <returns>A value indicating whether a database with the specified name exists.</returns>
|
||||
/// <remarks>
|
||||
/// A database exists if it is registered in the instance, and its files exist. If the database
|
||||
/// is registered but some of its files are missing, the database is dropped.
|
||||
/// </remarks>
|
||||
public bool DatabaseExists(string databaseName)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var mdf = GetDatabase(cmd, databaseName);
|
||||
if (mdf == null) return false;
|
||||
|
||||
// it can exist, even though its files have been deleted
|
||||
// if files exist assume all is ok (should we try to connect?)
|
||||
var ldf = GetLogFilename(mdf);
|
||||
if (File.Exists(mdf) && File.Exists(ldf))
|
||||
return true;
|
||||
|
||||
ExecuteDropDatabase(cmd, databaseName, mdf, ldf);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="filesPath">The directory containing database files.</param>
|
||||
/// <returns>A value indicating whether the database was created without errors.</returns>
|
||||
/// <remarks>
|
||||
/// Failed if a database with the specified name already exists in the instance,
|
||||
/// or if the database files already exist in the specified directory.
|
||||
/// </remarks>
|
||||
public bool CreateDatabase(string databaseName, string filesPath)
|
||||
{
|
||||
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
|
||||
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
|
||||
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var mdf = GetDatabase(cmd, databaseName);
|
||||
if (mdf != null) return false;
|
||||
|
||||
// cannot use parameters on CREATE DATABASE
|
||||
// ie "CREATE DATABASE @0 ..." does not work
|
||||
SetCommand(cmd, string.Format(@"
|
||||
CREATE DATABASE {0}
|
||||
ON (NAME=N{1}, FILENAME={2})
|
||||
LOG ON (NAME=N{3}, FILENAME={4})",
|
||||
QuotedName(databaseName),
|
||||
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
|
||||
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
|
||||
|
||||
var unused = cmd.ExecuteNonQuery();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops a database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <returns>A value indicating whether the database was dropped without errors.</returns>
|
||||
/// <remarks>
|
||||
/// Successful if the database does not exist.
|
||||
/// Deletes the database files.
|
||||
/// </remarks>
|
||||
public bool DropDatabase(string databaseName)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
SetCommand(cmd, @"
|
||||
SELECT name, filename FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = @0 OR name = @0)",
|
||||
databaseName);
|
||||
|
||||
var mdf = GetDatabase(cmd, databaseName);
|
||||
if (mdf == null) return true;
|
||||
|
||||
ExecuteDropDatabase(cmd, databaseName, mdf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops stale databases.
|
||||
/// </summary>
|
||||
/// <returns>The number of databases that were dropped.</returns>
|
||||
/// <remarks>
|
||||
/// A database is considered stale when its files cannot be found.
|
||||
/// </remarks>
|
||||
public int DropStaleDatabases()
|
||||
{
|
||||
return DropDatabases(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops databases.
|
||||
/// </summary>
|
||||
/// <param name="staleOnly">A value indicating whether to delete only stale database.</param>
|
||||
/// <returns>The number of databases that were dropped.</returns>
|
||||
/// <remarks>
|
||||
/// A database is considered stale when its files cannot be found.
|
||||
/// </remarks>
|
||||
public int DropDatabases(bool staleOnly = false)
|
||||
{
|
||||
var count = 0;
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var databases = new Dictionary<string, string>();
|
||||
|
||||
SetCommand(cmd, @"
|
||||
SELECT name, filename FROM sys.sysdatabases");
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
databases[reader.GetString(0)] = reader.GetString(1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var database in databases)
|
||||
{
|
||||
var dbname = database.Key;
|
||||
|
||||
if (dbname == "master" || dbname == "tempdb" || dbname == "model" || dbname == "msdb")
|
||||
continue;
|
||||
|
||||
var mdf = database.Value;
|
||||
var ldf = mdf.Replace(".mdf", "_log.ldf");
|
||||
if (staleOnly && File.Exists(mdf) && File.Exists(ldf))
|
||||
continue;
|
||||
|
||||
ExecuteDropDatabase(cmd, dbname, mdf, ldf);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches a database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <returns>The directory containing the database files.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when a database with the specified name does not exist.</exception>
|
||||
public string DetachDatabase(string databaseName)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var mdf = GetDatabase(cmd, databaseName);
|
||||
if (mdf == null)
|
||||
throw new InvalidOperationException("Database does not exist.");
|
||||
|
||||
DetachDatabase(cmd, databaseName);
|
||||
|
||||
return Path.GetDirectoryName(mdf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="filesPath">The directory containing database files.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when a database with the specified name already exists.</exception>
|
||||
public void AttachDatabase(string databaseName, string filesPath)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
var mdf = GetDatabase(cmd, databaseName);
|
||||
if (mdf != null)
|
||||
throw new InvalidOperationException("Database already exists.");
|
||||
|
||||
AttachDatabase(cmd, databaseName, filesPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file names of a database.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="mdfName">The MDF logical name.</param>
|
||||
/// <param name="ldfName">The LDF logical name.</param>
|
||||
/// <param name="mdfFilename">The MDF filename.</param>
|
||||
/// <param name="ldfFilename">The LDF filename.</param>
|
||||
public void GetFilenames(string databaseName,
|
||||
out string mdfName, out string ldfName,
|
||||
out string mdfFilename, out string ldfFilename)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
GetFilenames(cmd, databaseName, out mdfName, out ldfName, out mdfFilename, out ldfFilename);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills all existing connections.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
public void KillConnections(string databaseName)
|
||||
{
|
||||
using (var conn = new SqlConnection(_masterCstr))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
SetCommand(cmd, @"
|
||||
DECLARE @sql VARCHAR(MAX);
|
||||
SELECT @sql = COALESCE(@sql,'') + 'kill ' + CONVERT(VARCHAR, SPId) + ';'
|
||||
FROM master.sys.sysprocesses
|
||||
WHERE DBId = DB_ID(@0) AND SPId <> @@SPId;
|
||||
EXEC(@sql);",
|
||||
databaseName);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a database.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The Sql Command.</param>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <returns>The full filename of the MDF file, if the database exists, otherwise null.</returns>
|
||||
private static string GetDatabase(SqlCommand cmd, string databaseName)
|
||||
{
|
||||
SetCommand(cmd, @"
|
||||
SELECT name, filename FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = @0 OR name = @0)",
|
||||
databaseName);
|
||||
|
||||
string mdf = null;
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
mdf = reader.GetString(1) ?? string.Empty;
|
||||
while (reader.Read())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return mdf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops a database and its files.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The Sql command.</param>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="mdf">The name of the database (MDF) file.</param>
|
||||
/// <param name="ldf">The name of the log (LDF) file.</param>
|
||||
private static void ExecuteDropDatabase(SqlCommand cmd, string databaseName, string mdf, string ldf = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// cannot use parameters on ALTER DATABASE
|
||||
// ie "ALTER DATABASE @0 ..." does not work
|
||||
SetCommand(cmd, string.Format(@"
|
||||
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
|
||||
QuotedName(databaseName)));
|
||||
|
||||
var unused1 = cmd.ExecuteNonQuery();
|
||||
}
|
||||
catch (SqlException e)
|
||||
{
|
||||
if (e.Message.Contains("Unable to open the physical file") && e.Message.Contains("Operating system error 2:"))
|
||||
{
|
||||
// quite probably, the files were missing
|
||||
// yet, it should be possible to drop the database anyways
|
||||
// but we'll have to deal with the files
|
||||
}
|
||||
else
|
||||
{
|
||||
// no idea, throw
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// cannot use parameters on DROP DATABASE
|
||||
// ie "DROP DATABASE @0 ..." does not work
|
||||
SetCommand(cmd, string.Format(@"
|
||||
DROP DATABASE {0}",
|
||||
QuotedName(databaseName)));
|
||||
|
||||
var unused2 = cmd.ExecuteNonQuery();
|
||||
|
||||
// be absolutely sure
|
||||
if (File.Exists(mdf)) File.Delete(mdf);
|
||||
ldf = ldf ?? GetLogFilename(mdf);
|
||||
if (File.Exists(ldf)) File.Delete(ldf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log (LDF) filename corresponding to a database (MDF) filename.
|
||||
/// </summary>
|
||||
/// <param name="mdfFilename">The MDF filename.</param>
|
||||
/// <returns></returns>
|
||||
private static string GetLogFilename(string mdfFilename)
|
||||
{
|
||||
if (mdfFilename.EndsWith(".mdf") == false)
|
||||
throw new ArgumentException("Not a valid MDF filename (no .mdf extension).", "mdfFilename");
|
||||
return mdfFilename.Substring(0, mdfFilename.Length - ".mdf".Length) + "_log.ldf";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches a database.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The Sql command.</param>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
private static void DetachDatabase(SqlCommand cmd, string databaseName)
|
||||
{
|
||||
// cannot use parameters on ALTER DATABASE
|
||||
// ie "ALTER DATABASE @0 ..." does not work
|
||||
SetCommand(cmd, string.Format(@"
|
||||
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
|
||||
QuotedName(databaseName)));
|
||||
|
||||
var unused1 = cmd.ExecuteNonQuery();
|
||||
|
||||
SetCommand(cmd, @"
|
||||
EXEC sp_detach_db @dbname=@0",
|
||||
databaseName);
|
||||
|
||||
var unused2 = cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a database.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The Sql command.</param>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="filesPath">The directory containing database files.</param>
|
||||
private static void AttachDatabase(SqlCommand cmd, string databaseName, string filesPath)
|
||||
{
|
||||
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
|
||||
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
|
||||
|
||||
// cannot use parameters on CREATE DATABASE
|
||||
// ie "CREATE DATABASE @0 ..." does not work
|
||||
SetCommand(cmd, string.Format(@"
|
||||
CREATE DATABASE {0}
|
||||
ON (NAME=N{1}, FILENAME={2})
|
||||
LOG ON (NAME=N{3}, FILENAME={4})
|
||||
FOR ATTACH",
|
||||
QuotedName(databaseName),
|
||||
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
|
||||
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
|
||||
|
||||
var unused = cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a database command.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The command.</param>
|
||||
/// <param name="sql">The command text.</param>
|
||||
/// <param name="args">The command arguments.</param>
|
||||
/// <remarks>
|
||||
/// The command text must refer to arguments as @0, @1... each referring
|
||||
/// to the corresponding position in <paramref name="args"/>.
|
||||
/// </remarks>
|
||||
private static void SetCommand(SqlCommand cmd, string sql, params object[] args)
|
||||
{
|
||||
cmd.CommandType = CommandType.Text;
|
||||
cmd.CommandText = sql;
|
||||
cmd.Parameters.Clear();
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
cmd.Parameters.AddWithValue("@" + i, args[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file names of a database.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The Sql command.</param>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="mdfName">The MDF logical name.</param>
|
||||
/// <param name="ldfName">The LDF logical name.</param>
|
||||
/// <param name="mdfFilename">The MDF filename.</param>
|
||||
/// <param name="ldfFilename">The LDF filename.</param>
|
||||
private void GetFilenames(SqlCommand cmd, string databaseName,
|
||||
out string mdfName, out string ldfName,
|
||||
out string mdfFilename, out string ldfFilename)
|
||||
{
|
||||
mdfName = ldfName = mdfFilename = ldfFilename = null;
|
||||
|
||||
SetCommand(cmd, @"
|
||||
SELECT DB_NAME(database_id), type_desc, name, physical_name
|
||||
FROM master.sys.master_files
|
||||
WHERE database_id=DB_ID(@0)",
|
||||
databaseName);
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var type = reader.GetString(1);
|
||||
if (type == "ROWS")
|
||||
{
|
||||
mdfName = reader.GetString(2);
|
||||
ldfName = reader.GetString(3);
|
||||
}
|
||||
else if (type == "LOG")
|
||||
{
|
||||
ldfName = reader.GetString(2);
|
||||
ldfFilename = reader.GetString(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy database files.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the source database.</param>
|
||||
/// <param name="filesPath">The directory containing source database files.</param>
|
||||
/// <param name="targetDatabaseName">The name of the target database.</param>
|
||||
/// <param name="targetFilesPath">The directory containing target database files.</param>
|
||||
/// <param name="sourceExtension">The source database files extension.</param>
|
||||
/// <param name="targetExtension">The target database files extension.</param>
|
||||
/// <param name="overwrite">A value indicating whether to overwrite the target files.</param>
|
||||
/// <param name="delete">A value indicating whether to delete the source files.</param>
|
||||
/// <remarks>
|
||||
/// The <paramref name="targetDatabaseName"/>, <paramref name="targetFilesPath"/>, <paramref name="sourceExtension"/>
|
||||
/// and <paramref name="targetExtension"/> parameters are optional. If they result in target being identical
|
||||
/// to source, no copy is performed. If <paramref name="delete"/> is false, nothing happens, otherwise the source
|
||||
/// files are deleted.
|
||||
/// If target is not identical to source, files are copied or moved, depending on the value of <paramref name="delete"/>.
|
||||
/// Extensions are used eg to copy MyDatabase.mdf to MyDatabase.mdf.temp.
|
||||
/// </remarks>
|
||||
public void CopyDatabaseFiles(string databaseName, string filesPath,
|
||||
string targetDatabaseName = null, string targetFilesPath = null,
|
||||
string sourceExtension = null, string targetExtension = null,
|
||||
bool overwrite = false, bool delete = false)
|
||||
{
|
||||
var nop = (targetFilesPath == null || targetFilesPath == filesPath)
|
||||
&& (targetDatabaseName == null || targetDatabaseName == databaseName)
|
||||
&& (sourceExtension == null && targetExtension == null || sourceExtension == targetExtension);
|
||||
if (nop && delete == false) return;
|
||||
|
||||
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
|
||||
GetDatabaseFiles(databaseName, filesPath,
|
||||
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
|
||||
|
||||
if (sourceExtension != null)
|
||||
{
|
||||
mdfFilename += "." + sourceExtension;
|
||||
ldfFilename += "." + sourceExtension;
|
||||
}
|
||||
|
||||
if (nop)
|
||||
{
|
||||
// delete
|
||||
if (File.Exists(mdfFilename)) File.Delete(mdfFilename);
|
||||
if (File.Exists(ldfFilename)) File.Delete(ldfFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy or copy+delete ie move
|
||||
string targetLogName, targetBaseFilename, targetLogFilename, targetMdfFilename, targetLdfFilename;
|
||||
GetDatabaseFiles(targetDatabaseName ?? databaseName, targetFilesPath ?? filesPath,
|
||||
out targetLogName, out targetBaseFilename, out targetLogFilename, out targetMdfFilename, out targetLdfFilename);
|
||||
|
||||
if (targetExtension != null)
|
||||
{
|
||||
targetMdfFilename += "." + targetExtension;
|
||||
targetLdfFilename += "." + targetExtension;
|
||||
}
|
||||
|
||||
if (delete)
|
||||
{
|
||||
if (overwrite && File.Exists(targetMdfFilename)) File.Delete(targetMdfFilename);
|
||||
if (overwrite && File.Exists(targetLdfFilename)) File.Delete(targetLdfFilename);
|
||||
File.Move(mdfFilename, targetMdfFilename);
|
||||
File.Move(ldfFilename, targetLdfFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Copy(mdfFilename, targetMdfFilename, overwrite);
|
||||
File.Copy(ldfFilename, targetLdfFilename, overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether database files exist.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the source database.</param>
|
||||
/// <param name="filesPath">The directory containing source database files.</param>
|
||||
/// <param name="extension">The database files extension.</param>
|
||||
/// <returns>A value indicating whether the database files exist.</returns>
|
||||
/// <remarks>
|
||||
/// Extensions are used eg to copy MyDatabase.mdf to MyDatabase.mdf.temp.
|
||||
/// </remarks>
|
||||
public bool DatabaseFilesExist(string databaseName, string filesPath, string extension = null)
|
||||
{
|
||||
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
|
||||
GetDatabaseFiles(databaseName, filesPath,
|
||||
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
|
||||
|
||||
if (extension != null)
|
||||
{
|
||||
mdfFilename += "." + extension;
|
||||
ldfFilename += "." + extension;
|
||||
}
|
||||
|
||||
return File.Exists(mdfFilename) && File.Exists(ldfFilename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the database files.
|
||||
/// </summary>
|
||||
/// <param name="databaseName">The name of the database.</param>
|
||||
/// <param name="filesPath">The directory containing database files.</param>
|
||||
/// <param name="logName">The name of the log.</param>
|
||||
/// <param name="baseFilename">The base filename (the MDF filename without the .mdf extension).</param>
|
||||
/// <param name="baseLogFilename">The base log filename (the LDF filename without the .ldf extension).</param>
|
||||
/// <param name="mdfFilename">The MDF filename.</param>
|
||||
/// <param name="ldfFilename">The LDF filename.</param>
|
||||
private static void GetDatabaseFiles(string databaseName, string filesPath,
|
||||
out string logName,
|
||||
out string baseFilename, out string baseLogFilename,
|
||||
out string mdfFilename, out string ldfFilename)
|
||||
{
|
||||
logName = databaseName + "_log";
|
||||
baseFilename = Path.Combine(filesPath, databaseName);
|
||||
baseLogFilename = Path.Combine(filesPath, logName);
|
||||
mdfFilename = baseFilename + ".mdf";
|
||||
ldfFilename = baseFilename + "_log.ldf";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SqlLocalDB
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SqlLocalDB command.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="output">The command standard output.</param>
|
||||
/// <param name="error">The command error output.</param>
|
||||
/// <returns>The process exit code.</returns>
|
||||
/// <remarks>
|
||||
/// Execution is successful if the exit code is zero, and error is empty.
|
||||
/// </remarks>
|
||||
private int ExecuteSqlLocalDb(string args, out string output, out string error)
|
||||
{
|
||||
if (_exe == null) // should never happen - we should not execute if not available
|
||||
{
|
||||
output = string.Empty;
|
||||
error = "SqlLocalDB.exe not found";
|
||||
return -1;
|
||||
}
|
||||
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
FileName = _exe,
|
||||
Arguments = args,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
p.Start();
|
||||
output = p.StandardOutput.ReadToEnd();
|
||||
error = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
|
||||
return p.ExitCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Unicode string with the delimiters added to make the input string a valid SQL Server delimited identifier.
|
||||
/// </summary>
|
||||
/// <param name="name">The name to quote.</param>
|
||||
/// <param name="quote">A quote character.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is a C# implementation of T-SQL QUOTEDNAME.
|
||||
/// <paramref name="quote"/> is optional, it can be '[' (default), ']', '\'' or '"'.
|
||||
/// </remarks>
|
||||
private static string QuotedName(string name, char quote = '[')
|
||||
{
|
||||
switch (quote)
|
||||
{
|
||||
case '[':
|
||||
case ']':
|
||||
return "[" + name.Replace("]", "]]") + "]";
|
||||
case '\'':
|
||||
return "'" + name.Replace("'", "''") + "'";
|
||||
case '"':
|
||||
return "\"" + name.Replace("\"", "\"\"") + "\"";
|
||||
default:
|
||||
throw new NotSupportedException("Not a valid quote character.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
-21
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
@@ -11,15 +10,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
|
||||
{
|
||||
public UpdateUniqueIdToHaveCorrectIndexType(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
//see: http://issues.umbraco.org/issue/U4-6188, http://issues.umbraco.org/issue/U4-6187
|
||||
public override void Up()
|
||||
{
|
||||
|
||||
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
|
||||
var indexes = SqlSyntax.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition()
|
||||
{
|
||||
TableName = x.Item1,
|
||||
@@ -28,24 +23,19 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
|
||||
//must be non-nullable
|
||||
// drop the index if it exists
|
||||
if (indexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")))
|
||||
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
|
||||
|
||||
// set uniqueID to be non-nullable
|
||||
// the index *must* be dropped else 'one or more objects access this column' exception
|
||||
Alter.Table("umbracoNode").AlterColumn("uniqueID").AsGuid().NotNullable();
|
||||
|
||||
//make sure it already exists
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")))
|
||||
{
|
||||
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
|
||||
}
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNode_uniqueID")) == false)
|
||||
{
|
||||
//must be a uniqe index
|
||||
Create.Index("IX_umbracoNode_uniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Unique();
|
||||
}
|
||||
// create the index
|
||||
Create.Index("IX_umbracoNode_uniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Unique();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
{ }
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
|
||||
{
|
||||
[Migration("7.2.0", 3, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIndexToUmbracoNodeTable : MigrationBase
|
||||
{
|
||||
private readonly bool _skipIndexCheck;
|
||||
|
||||
internal AddIndexToUmbracoNodeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger, bool skipIndexCheck) : base(sqlSyntax, logger)
|
||||
{
|
||||
_skipIndexCheck = skipIndexCheck;
|
||||
}
|
||||
|
||||
public AddIndexToUmbracoNodeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")) == false)
|
||||
{
|
||||
Create.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Ascending().WithOptions().NonClustered();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
s.Where<DocumentDto>(x => x.Newest, SqlSyntax);
|
||||
return s;
|
||||
};
|
||||
|
||||
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<IContent>
|
||||
|
||||
@@ -133,7 +133,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId);
|
||||
//TODO: IF we want to enable querying on content type information this will need to be joined
|
||||
//.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
|
||||
@@ -142,8 +144,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
|
||||
//information with the entire data set, so basically this will get both the latest document and also it's published
|
||||
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
|
||||
//and causes huge performance overhead for the SQL server, especially when sorting the result.
|
||||
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
|
||||
//and causes huge performance overhead for the SQL server, especially when sorting the result.
|
||||
//We also don't include this outer join when querying for multiple entities since it is much faster to fetch this information
|
||||
//in a separate query. For a single entity this is ok.
|
||||
|
||||
@@ -234,7 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
};
|
||||
|
||||
var baseId = 0;
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
// get the next group of nodes
|
||||
@@ -284,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == docObjectType, SqlSyntax);
|
||||
|
||||
|
||||
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
|
||||
|
||||
var toRemove = allXmlIds.Except(allContentIds).ToArray();
|
||||
@@ -293,9 +295,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
foreach (var idGroup in toRemove.InGroupsOf(2000))
|
||||
{
|
||||
Database.Execute("DELETE FROM cmsContentXml WHERE nodeId IN (@ids)", new { ids = idGroup });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override IEnumerable<IContent> GetAllVersions(int id)
|
||||
@@ -308,7 +310,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var sqlFull = translate(GetBaseQuery(BaseQueryType.FullMultiple));
|
||||
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
|
||||
|
||||
|
||||
return ProcessQuery(sqlFull, new PagingSqlQuery(sqlIds), true, includeAllVersions:true);
|
||||
}
|
||||
|
||||
@@ -388,7 +390,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override void PersistDeletedItem(IContent entity)
|
||||
{
|
||||
//We need to clear out all access rules but we need to do this in a manual way since
|
||||
//We need to clear out all access rules but we need to do this in a manual way since
|
||||
// nothing in that table is joined to a content id
|
||||
var subQuery = new Sql()
|
||||
.Select("umbracoAccessRule.accessId")
|
||||
@@ -435,7 +437,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
nodeDto.Path = parent.Path;
|
||||
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
|
||||
nodeDto.SortOrder = sortOrder;
|
||||
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
|
||||
|
||||
// note:
|
||||
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
|
||||
// but I cannot figure out what was the point, as the node should obviously be new if
|
||||
// we reach that point - removed.
|
||||
|
||||
// see if there's a reserved identifier for this unique id
|
||||
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
|
||||
var id = Database.ExecuteScalar<int>(sql);
|
||||
if (id > 0)
|
||||
{
|
||||
nodeDto.NodeId = id;
|
||||
Database.Update(nodeDto);
|
||||
}
|
||||
else
|
||||
{
|
||||
Database.Insert(nodeDto);
|
||||
}
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
@@ -449,7 +468,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
entity.Level = level;
|
||||
|
||||
//Assign the same permissions to it as the parent node
|
||||
// http://issues.umbraco.org/issue/U4-2161
|
||||
// http://issues.umbraco.org/issue/U4-2161
|
||||
var permissionsRepo = new PermissionRepository<IContent>(UnitOfWork, _cacheHelper, SqlSyntax);
|
||||
var parentPermissions = permissionsRepo.GetPermissionsForEntity(entity.ParentId).ToArray();
|
||||
//if there are parent permissions then assign them, otherwise leave null and permissions will become the
|
||||
@@ -516,7 +535,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
VersionDate = dto.UpdateDate,
|
||||
Newest = true,
|
||||
NodeId = dto.NodeId,
|
||||
Published = true
|
||||
Published = true
|
||||
};
|
||||
((Content) entity).PublishedVersionGuid = dto.VersionId;
|
||||
((Content) entity).PublishedDate = dto.UpdateDate;
|
||||
@@ -636,9 +655,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
//In order to update the ContentVersion we need to retrieve its primary key id
|
||||
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
|
||||
contentVersionDto.Id = contentVerDto.Id;
|
||||
|
||||
Database.Update(contentVersionDto);
|
||||
if (contentVerDto != null)
|
||||
{
|
||||
contentVersionDto.Id = contentVerDto.Id;
|
||||
Database.Update(contentVersionDto);
|
||||
}
|
||||
|
||||
Database.Update(dto);
|
||||
}
|
||||
|
||||
@@ -742,7 +764,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)), true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This builds the Xml document used for the XML cache
|
||||
/// </summary>
|
||||
@@ -817,11 +839,21 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
}
|
||||
|
||||
public int CountPublished()
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
public int CountPublished(string contentTypeAlias = null)
|
||||
{
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true)
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
|
||||
@@ -841,7 +873,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="permission"></param>
|
||||
/// <param name="userIds"></param>
|
||||
/// <param name="userIds"></param>
|
||||
public void AssignEntityPermission(IContent entity, char permission, IEnumerable<int> userIds)
|
||||
{
|
||||
var repo = new PermissionRepository<IContent>(UnitOfWork, _cacheHelper, SqlSyntax);
|
||||
@@ -899,9 +931,9 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter = null)
|
||||
{
|
||||
|
||||
//NOTE: This uses the GetBaseQuery method but that does not take into account the required 'newest' field which is
|
||||
//NOTE: This uses the GetBaseQuery method but that does not take into account the required 'newest' field which is
|
||||
// what we always require for a paged result, so we'll ensure it's included in the filter
|
||||
|
||||
|
||||
var filterSql = new Sql().Append("AND (cmsDocument.newest = 1)");
|
||||
if (filter != null)
|
||||
{
|
||||
@@ -910,7 +942,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
filterSql.Append(string.Format("AND ({0})", filterClaus.Item1), filterClaus.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Func<Tuple<string, object[]>> filterCallback = () => new Tuple<string, object[]>(filterSql.SQL, filterSql.Arguments);
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
@@ -957,7 +989,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
/// </param>
|
||||
/// <param name="withCache"></param>
|
||||
/// <param name="includeAllVersions">
|
||||
/// Generally when querying for content we only want to return the most recent version of the content item, however in some cases like when
|
||||
/// Generally when querying for content we only want to return the most recent version of the content item, however in some cases like when
|
||||
/// we want to return all versions of a content item, we can't simply return the latest
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
@@ -966,7 +998,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
|
||||
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
|
||||
//Go and get all of the published version data separately for this data, this is because when we are querying
|
||||
//for multiple content items we don't include the outer join to fetch this data in the same query because
|
||||
//it is insanely slow. Instead we just fetch the published version data separately in one query.
|
||||
@@ -978,7 +1010,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
if (parsedOriginalSql.InvariantContains("ORDER BY "))
|
||||
{
|
||||
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
//order by update date DESC, if there is corrupted published flags we only want the latest!
|
||||
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.updateDate, cmsDocument.newest
|
||||
@@ -1004,12 +1036,12 @@ ORDER BY cmsContentVersion.id DESC
|
||||
var content = new List<Tuple<IContent, bool>>();
|
||||
var defs = new DocumentDefinitionCollection(includeAllVersions);
|
||||
var templateIds = new List<int>();
|
||||
|
||||
|
||||
//track the looked up content types, even though the content types are cached
|
||||
// they still need to be deep cloned out of the cache and we don't want to add
|
||||
// the overhead of deep cloning them on every item in this loop
|
||||
var contentTypes = new Dictionary<int, IContentType>();
|
||||
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
DocumentPublishedReadOnlyDto publishedDto;
|
||||
@@ -1119,33 +1151,12 @@ ORDER BY cmsContentVersion.id DESC
|
||||
if (EnsureUniqueNaming == false)
|
||||
return nodeName;
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
|
||||
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
|
||||
new { objectType = NodeObjectTypeId, parentId });
|
||||
|
||||
int uniqueNumber = 1;
|
||||
var currentName = nodeName;
|
||||
|
||||
var dtos = Database.Fetch<NodeDto>(sql);
|
||||
if (dtos.Any())
|
||||
{
|
||||
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
|
||||
foreach (var dto in results)
|
||||
{
|
||||
if (id != 0 && id == dto.NodeId) continue;
|
||||
|
||||
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
|
||||
{
|
||||
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
|
||||
uniqueNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentName;
|
||||
return SimilarNodeName.GetUniqueName(names, id, nodeName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
|
||||
@@ -450,33 +450,10 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
private string EnsureUniqueNodeName(string nodeName, int id = 0)
|
||||
{
|
||||
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType",
|
||||
new { objectType = NodeObjectTypeId });
|
||||
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>(SqlSyntax)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.Text.StartsWith(nodeName));
|
||||
|
||||
int uniqueNumber = 1;
|
||||
var currentName = nodeName;
|
||||
|
||||
var dtos = Database.Fetch<NodeDto>(sql);
|
||||
if (dtos.Any())
|
||||
{
|
||||
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
|
||||
foreach (var dto in results)
|
||||
{
|
||||
if (id != 0 && id == dto.NodeId) continue;
|
||||
|
||||
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
|
||||
{
|
||||
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
|
||||
uniqueNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentName;
|
||||
return SimilarNodeName.GetUniqueName(names, id, nodeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -260,6 +260,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return GetByQuery(query);
|
||||
}
|
||||
|
||||
public Dictionary<string, Guid> GetDictionaryItemKeyMap()
|
||||
{
|
||||
var columns = new[] { "key", "id" }.Select(x => (object) SqlSyntax.GetQuotedColumnName(x)).ToArray();
|
||||
var sql = new Sql().Select(columns).From<DictionaryDto>(SqlSyntax);
|
||||
return Database.Fetch<DictionaryItemKeyIdDto>(sql).ToDictionary(x => x.Key, x => x.Id);
|
||||
}
|
||||
|
||||
private class DictionaryItemKeyIdDto
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
public IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId)
|
||||
{
|
||||
//This methods will look up children at each level, since we do not store a path for dictionary (ATM), we need to do a recursive
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>
|
||||
/// We require this on the repo because the IQuery{IContent} cannot supply the 'newest' parameter
|
||||
/// </remarks>
|
||||
int CountPublished();
|
||||
int CountPublished(string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
|
||||
@@ -9,5 +9,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IDictionaryItem Get(Guid uniqueId);
|
||||
IDictionaryItem Get(string key);
|
||||
IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId);
|
||||
Dictionary<string, Guid> GetDictionaryItemKeyMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.IO;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IUserControlRepository : IRepository<string, UserControl>
|
||||
{
|
||||
bool ValidateUserControl(UserControl userControl);
|
||||
Stream GetFileContentStream(string filepath);
|
||||
void SetFileContent(string filepath, Stream content);
|
||||
long GetFileSize(string filepath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal class MacroScriptRepository : PartialViewRepository
|
||||
{
|
||||
public MacroScriptRepository(IUnitOfWork work, IFileSystem fileSystem)
|
||||
: base(work, fileSystem)
|
||||
{ }
|
||||
|
||||
protected override PartialViewType ViewType { get { return PartialViewType.MacroScript; } }
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
|
||||
|
||||
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
var sql = new Sql();
|
||||
@@ -153,7 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// This is the underlying method that processes most queries for this repository
|
||||
/// </summary>
|
||||
/// <param name="sqlFull">
|
||||
/// The full SQL to select all media data
|
||||
/// The full SQL to select all media data
|
||||
/// </param>
|
||||
/// <param name="pagingSqlQuery">
|
||||
/// The Id SQL to just return all media ids - used to process the properties for the media item
|
||||
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
|
||||
|
||||
|
||||
//This is a tuple list identifying if the content item came from the cache or not
|
||||
var content = new List<Tuple<IMedia, bool>>();
|
||||
var defs = new DocumentDefinitionCollection();
|
||||
@@ -180,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (withCache)
|
||||
{
|
||||
var cached = IsolatedCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
|
||||
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
|
||||
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
|
||||
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
|
||||
if (cached != null && cached.Version == dto.VersionId)
|
||||
{
|
||||
@@ -303,7 +303,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.From<ContentXmlDto>(SqlSyntax)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentXmlDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
|
||||
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
{
|
||||
xmlIdsQuery.InnerJoin<ContentDto>(SqlSyntax)
|
||||
@@ -314,7 +314,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType, SqlSyntax);
|
||||
|
||||
|
||||
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
|
||||
|
||||
var toRemove = allXmlIds.Except(allMediaIds).ToArray();
|
||||
@@ -380,7 +380,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
nodeDto.Path = parent.Path;
|
||||
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
|
||||
nodeDto.SortOrder = sortOrder;
|
||||
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
|
||||
|
||||
// note:
|
||||
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
|
||||
// but I cannot figure out what was the point, as the node should obviously be new if
|
||||
// we reach that point - removed.
|
||||
|
||||
// see if there's a reserved identifier for this unique id
|
||||
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
|
||||
var id = Database.ExecuteScalar<int>(sql);
|
||||
if (id > 0)
|
||||
{
|
||||
nodeDto.NodeId = id;
|
||||
Database.Update(nodeDto);
|
||||
}
|
||||
else
|
||||
{
|
||||
Database.Insert(nodeDto);
|
||||
}
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
@@ -564,7 +581,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private IMedia CreateMediaFromDto(ContentVersionDto dto, Sql docSql)
|
||||
{
|
||||
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
|
||||
|
||||
var media = MediaFactory.BuildEntity(dto, contentType);
|
||||
|
||||
var docDef = new DocumentDefinition(dto, contentType);
|
||||
@@ -584,31 +601,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (EnsureUniqueNaming == false)
|
||||
return nodeName;
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
|
||||
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
|
||||
new { objectType = NodeObjectTypeId, parentId });
|
||||
|
||||
int uniqueNumber = 1;
|
||||
var currentName = nodeName;
|
||||
|
||||
var dtos = Database.Fetch<NodeDto>(sql);
|
||||
if (dtos.Any())
|
||||
{
|
||||
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
|
||||
foreach (var dto in results)
|
||||
{
|
||||
if (id != 0 && id == dto.NodeId) continue;
|
||||
|
||||
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
|
||||
{
|
||||
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
|
||||
uniqueNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentName;
|
||||
return SimilarNodeName.GetUniqueName(names, id, nodeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -184,30 +184,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
|
||||
{
|
||||
//find the member by username
|
||||
var memberSql = new Sql();
|
||||
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
|
||||
var sql = new Sql()
|
||||
.Select("un.*")
|
||||
.From("umbracoNode AS un")
|
||||
.InnerJoin("cmsMember2MemberGroup")
|
||||
.On("un.id = cmsMember2MemberGroup.MemberGroup")
|
||||
.LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
|
||||
.On("member.id = cmsMember2MemberGroup.Member")
|
||||
.Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
|
||||
.Where("member.LoginName=@loginName", new {loginName = username});
|
||||
|
||||
memberSql.Select("umbracoNode.id")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<MemberDto>()
|
||||
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
|
||||
.Where<MemberDto>(x => x.LoginName == username);
|
||||
var memberIdUsername = Database.Fetch<int?>(memberSql).FirstOrDefault();
|
||||
if (memberIdUsername.HasValue == false)
|
||||
{
|
||||
return Enumerable.Empty<IMemberGroup>();
|
||||
}
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("umbracoNode.*")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<Member2MemberGroupDto>()
|
||||
.On<NodeDto, Member2MemberGroupDto>(dto => dto.NodeId, dto => dto.MemberGroup)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<Member2MemberGroupDto>(x => x.Member == memberIdUsername.Value);
|
||||
|
||||
return Database.Fetch<NodeDto>(sql)
|
||||
.DistinctBy(dto => dto.NodeId)
|
||||
.Select(x => _modelFactory.BuildEntity(x));
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -230,7 +231,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Database.Insert(dto.ContentVersionDto);
|
||||
|
||||
//Create the first entry in cmsMember
|
||||
dto.NodeId = nodeDto.NodeId;
|
||||
dto.NodeId = nodeDto.NodeId;
|
||||
|
||||
//if the password is empty, generate one with the special prefix
|
||||
//this will hash the guid with a salt so should be nicely random
|
||||
if (entity.RawPasswordValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
var aspHasher = new PasswordHasher();
|
||||
dto.Password = Constants.Security.EmptyPasswordPrefix +
|
||||
aspHasher.HashPassword(Guid.NewGuid().ToString("N"));
|
||||
//re-assign
|
||||
entity.RawPasswordValue = dto.Password;
|
||||
}
|
||||
|
||||
Database.Insert(dto);
|
||||
|
||||
//Create the PropertyData for this version - cmsPropertyData
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Construct and execute delete statements for all trashed items by 'nodeObjectType'
|
||||
var deletes = new List<string>
|
||||
{
|
||||
FormatDeleteStatement("cmsTask", "nodeId"),
|
||||
FormatDeleteStatement("umbracoUser2NodeNotify", "nodeId"),
|
||||
FormatDeleteStatement("umbracoUser2NodePermission", "nodeId"),
|
||||
@"DELETE FROM umbracoAccessRule WHERE umbracoAccessRule.accessId IN (
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal class SimilarNodeName
|
||||
{
|
||||
private int _numPos = -2;
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
// cached - reused
|
||||
public int NumPos
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_numPos != -2) return _numPos;
|
||||
|
||||
var name = Name;
|
||||
|
||||
if (name[name.Length - 1] != ')')
|
||||
return _numPos = -1;
|
||||
|
||||
var pos = name.LastIndexOf('(');
|
||||
if (pos < 2 || pos == name.Length - 2) // < 2 and not < 0, because we want at least "x ("
|
||||
return _numPos = -1;
|
||||
|
||||
return _numPos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// not cached - used only once
|
||||
public int NumVal
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NumPos < 0)
|
||||
throw new InvalidOperationException();
|
||||
int num;
|
||||
if (int.TryParse(Name.Substring(NumPos + 1, Name.Length - 2 - NumPos), out num))
|
||||
return num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// compare without allocating, nor parsing integers
|
||||
internal class Comparer : IComparer<SimilarNodeName>
|
||||
{
|
||||
public int Compare(SimilarNodeName x, SimilarNodeName y)
|
||||
{
|
||||
if (x == null) throw new ArgumentNullException("x");
|
||||
if (y == null) throw new ArgumentNullException("y");
|
||||
|
||||
var xpos = x.NumPos;
|
||||
var ypos = y.NumPos;
|
||||
|
||||
var xname = x.Name;
|
||||
var yname = y.Name;
|
||||
|
||||
if (xpos < 0 || ypos < 0 || xpos != ypos)
|
||||
return string.Compare(xname, yname, StringComparison.Ordinal);
|
||||
|
||||
// compare the part before (number)
|
||||
var n = string.Compare(xname, 0, yname, 0, xpos, StringComparison.Ordinal);
|
||||
if (n != 0)
|
||||
return n;
|
||||
|
||||
// compare (number) lengths
|
||||
var diff = xname.Length - yname.Length;
|
||||
if (diff != 0) return diff < 0 ? -1 : +1;
|
||||
|
||||
// actually compare (number)
|
||||
var i = xpos;
|
||||
while (i < xname.Length - 1)
|
||||
{
|
||||
if (xname[i] != yname[i])
|
||||
return xname[i] < yname[i] ? -1 : +1;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// gets a unique name
|
||||
public static string GetUniqueName(IEnumerable<SimilarNodeName> names, int nodeId, string nodeName)
|
||||
{
|
||||
var uniqueNumber = 1;
|
||||
var uniqueing = false;
|
||||
foreach (var name in names.OrderBy(x => x, new Comparer()))
|
||||
{
|
||||
// ignore self
|
||||
if (nodeId != 0 && name.Id == nodeId) continue;
|
||||
|
||||
if (uniqueing)
|
||||
{
|
||||
if (name.NumPos > 0 && name.Name.StartsWith(nodeName) && name.NumVal == uniqueNumber)
|
||||
uniqueNumber++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (name.Name.InvariantEquals(nodeName))
|
||||
{
|
||||
uniqueing = true;
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Comparer that takes into account the duplicate index of a node name
|
||||
/// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc.
|
||||
/// </summary>
|
||||
internal class SimilarNodeNameComparer : IComparer<string>
|
||||
{
|
||||
public int Compare(string x, string y)
|
||||
{
|
||||
if (x.LastIndexOf('(') != -1 && x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1)
|
||||
{
|
||||
if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('(')))
|
||||
{
|
||||
int xDuplicateIndex = ExtractDuplicateIndex(x);
|
||||
int yDuplicateIndex = ExtractDuplicateIndex(y);
|
||||
|
||||
if (xDuplicateIndex != 0 && yDuplicateIndex != 0)
|
||||
{
|
||||
return xDuplicateIndex.CompareTo(yDuplicateIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private int ExtractDuplicateIndex(string text)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2)
|
||||
{
|
||||
int startPos = text.LastIndexOf('(') + 1;
|
||||
int length = text.Length - 1 - startPos;
|
||||
|
||||
int.TryParse(text.Substring(startPos, length), out index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the UserControl Repository
|
||||
/// </summary>
|
||||
internal class UserControlRepository : FileRepository<string, UserControl>, IUserControlRepository
|
||||
{
|
||||
public UserControlRepository(IUnitOfWork work, IFileSystem fileSystem)
|
||||
: base(work, fileSystem)
|
||||
{
|
||||
}
|
||||
|
||||
public override UserControl Get(string id)
|
||||
{
|
||||
var path = FileSystem.GetRelativePath(id);
|
||||
|
||||
path = path.EnsureEndsWith(".ascx");
|
||||
|
||||
if (FileSystem.FileExists(path) == false)
|
||||
return null;
|
||||
|
||||
var created = FileSystem.GetCreated(path).UtcDateTime;
|
||||
var updated = FileSystem.GetLastModified(path).UtcDateTime;
|
||||
|
||||
var userControl = new UserControl(path, file => GetFileContent(file.OriginalPath))
|
||||
{
|
||||
Key = path.EncodeAsGuid(),
|
||||
CreateDate = created,
|
||||
UpdateDate = updated,
|
||||
Id = path.GetHashCode(),
|
||||
VirtualPath = FileSystem.GetUrl(path)
|
||||
};
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
userControl.ResetDirtyProperties(false);
|
||||
|
||||
return userControl;
|
||||
}
|
||||
|
||||
public override void AddOrUpdate(UserControl entity)
|
||||
{
|
||||
base.AddOrUpdate(entity);
|
||||
|
||||
// ensure that from now on, content is lazy-loaded
|
||||
if (entity.GetFileContent == null)
|
||||
entity.GetFileContent = file => GetFileContent(file.OriginalPath);
|
||||
}
|
||||
|
||||
public override IEnumerable<UserControl> GetAll(params string[] ids)
|
||||
{
|
||||
ids = ids
|
||||
.Select(x => StringExtensions.EnsureEndsWith(x, ".ascx"))
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
foreach (var id in ids)
|
||||
{
|
||||
yield return Get(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var files = FindAllFiles("", "*.ascx");
|
||||
foreach (var file in files)
|
||||
{
|
||||
yield return Get(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all <see cref="UserControl"/> that exist at the relative path specified.
|
||||
/// </summary>
|
||||
/// <param name="rootPath">
|
||||
/// If null or not specified, will return the UserControl files at the root path relative to the IFileSystem
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<UserControl> GetUserControlsAtPath(string rootPath = null)
|
||||
{
|
||||
return FileSystem.GetFiles(rootPath ?? string.Empty, "*.ascx").Select(Get);
|
||||
}
|
||||
|
||||
private static readonly List<string> ValidExtensions = new List<string> { "ascx" };
|
||||
|
||||
public bool ValidateUserControl(UserControl userControl)
|
||||
{
|
||||
// get full path
|
||||
string fullPath;
|
||||
try
|
||||
{
|
||||
// may throw for security reasons
|
||||
fullPath = FileSystem.GetFullPath(userControl.Path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate path and extension
|
||||
var validDir = SystemDirectories.UserControls;
|
||||
var isValidPath = IOHelper.VerifyEditPath(fullPath, validDir);
|
||||
var isValidExtension = IOHelper.VerifyFileExtension(userControl.Path, ValidExtensions);
|
||||
return isValidPath && isValidExtension;
|
||||
}
|
||||
|
||||
public Stream GetFileContentStream(string filepath)
|
||||
{
|
||||
if (FileSystem.FileExists(filepath) == false) return null;
|
||||
|
||||
try
|
||||
{
|
||||
return FileSystem.OpenFile(filepath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null; // deal with race conds
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFileContent(string filepath, Stream content)
|
||||
{
|
||||
FileSystem.AddFile(filepath, content, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,8 +705,8 @@ ORDER BY contentNodeId, versionId, propertytypeid
|
||||
case "NAME":
|
||||
return "umbracoNode.text";
|
||||
case "PUBLISHED":
|
||||
case "OWNER":
|
||||
return "cmsDocument.published";
|
||||
case "OWNER":
|
||||
//TODO: This isn't going to work very nicely because it's going to order by ID, not by letter
|
||||
return "umbracoNode.nodeUser";
|
||||
// Members only
|
||||
|
||||
@@ -231,6 +231,18 @@ namespace Umbraco.Core.Persistence
|
||||
return new PartialViewMacroRepository(uow, FileSystemProviderManager.Current.MacroPartialsFileSystem);
|
||||
}
|
||||
|
||||
[Obsolete("MacroScripts are obsolete - this is for backwards compatibility with upgraded sites.")]
|
||||
internal virtual IPartialViewRepository CreateMacroScriptRepository(IUnitOfWork uow)
|
||||
{
|
||||
return new MacroScriptRepository(uow, FileSystemProviderManager.Current.MacroScriptsFileSystem);
|
||||
}
|
||||
|
||||
[Obsolete("UserControls are obsolete - this is for backwards compatibility with upgraded sites.")]
|
||||
internal virtual IUserControlRepository CreateUserControlRepository(IUnitOfWork uow)
|
||||
{
|
||||
return new UserControlRepository(uow, FileSystemProviderManager.Current.UserControlsFileSystem);
|
||||
}
|
||||
|
||||
public virtual IStylesheetRepository CreateStylesheetRepository(IUnitOfWork uow)
|
||||
{
|
||||
return new StylesheetRepository(uow, FileSystemProviderManager.Current.StylesheetsFileSystem);
|
||||
|
||||
+190
-134
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Compilation;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -15,6 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -39,13 +41,14 @@ namespace Umbraco.Core
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly ProfilingLogger _logger;
|
||||
private readonly string _tempFolder;
|
||||
private readonly Lazy<string> _pluginListFilePath = new Lazy<string>(GetPluginListFilePath);
|
||||
private readonly Lazy<string> _pluginHashFilePath = new Lazy<string>(GetPluginHashFilePath);
|
||||
|
||||
private readonly object _typesLock = new object();
|
||||
private readonly Dictionary<TypeListKey, TypeList> _types = new Dictionary<TypeListKey, TypeList>();
|
||||
|
||||
private long _cachedAssembliesHash = -1;
|
||||
private long _currentAssembliesHash = -1;
|
||||
private string _cachedAssembliesHash = null;
|
||||
private string _currentAssembliesHash = null;
|
||||
private IEnumerable<Assembly> _assemblies;
|
||||
private bool _reportedChange;
|
||||
|
||||
@@ -64,27 +67,21 @@ namespace Umbraco.Core
|
||||
|
||||
_serviceProvider = serviceProvider;
|
||||
_runtimeCache = runtimeCache;
|
||||
_logger = logger;
|
||||
|
||||
// the temp folder where the cache file lives
|
||||
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
if (Directory.Exists(_tempFolder) == false)
|
||||
Directory.CreateDirectory(_tempFolder);
|
||||
|
||||
var pluginListFile = GetPluginListFilePath();
|
||||
|
||||
_logger = logger;
|
||||
|
||||
if (detectChanges)
|
||||
{
|
||||
//first check if the cached hash is 0, if it is then we ne
|
||||
//first check if the cached hash is string.Empty, if it is then we need
|
||||
//do the check if they've changed
|
||||
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
|
||||
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == string.Empty;
|
||||
//if they have changed, we need to write the new file
|
||||
if (RequiresRescanning)
|
||||
{
|
||||
// if the hash has changed, clear out the persisted list no matter what, this will force
|
||||
// rescanning of all plugin types including lazy ones.
|
||||
// http://issues.umbraco.org/issue/U4-4789
|
||||
File.Delete(pluginListFile);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
WriteCachePluginsHash();
|
||||
}
|
||||
@@ -94,7 +91,8 @@ namespace Umbraco.Core
|
||||
// if the hash has changed, clear out the persisted list no matter what, this will force
|
||||
// rescanning of all plugin types including lazy ones.
|
||||
// http://issues.umbraco.org/issue/U4-4789
|
||||
File.Delete(pluginListFile);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
// always set to true if we're not detecting (generally only for testing)
|
||||
RequiresRescanning = true;
|
||||
@@ -180,23 +178,19 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Gets the currently cached hash value of the scanned assemblies.
|
||||
/// </summary>
|
||||
/// <value>The cached hash value, or 0 if no cache is found.</value>
|
||||
internal long CachedAssembliesHash
|
||||
/// <value>The cached hash value, or string.Empty if no cache is found.</value>
|
||||
internal string CachedAssembliesHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cachedAssembliesHash != -1)
|
||||
return _cachedAssembliesHash;
|
||||
if (_cachedAssembliesHash != null)
|
||||
return _cachedAssembliesHash;
|
||||
|
||||
if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty;
|
||||
|
||||
var filePath = GetPluginHashFilePath();
|
||||
if (File.Exists(filePath) == false) return 0;
|
||||
var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8);
|
||||
|
||||
var hash = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
|
||||
long val;
|
||||
if (long.TryParse(hash, out val) == false) return 0;
|
||||
|
||||
_cachedAssembliesHash = val;
|
||||
_cachedAssembliesHash = hash;
|
||||
return _cachedAssembliesHash;
|
||||
}
|
||||
}
|
||||
@@ -205,15 +199,15 @@ namespace Umbraco.Core
|
||||
/// Gets the current assemblies hash based on creating a hash from the assemblies in various places.
|
||||
/// </summary>
|
||||
/// <value>The current hash.</value>
|
||||
internal long CurrentAssembliesHash
|
||||
internal string CurrentAssembliesHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentAssembliesHash != -1)
|
||||
if (_currentAssembliesHash != null)
|
||||
return _currentAssembliesHash;
|
||||
|
||||
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
|
||||
{
|
||||
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
|
||||
{
|
||||
// the bin folder and everything in it
|
||||
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), false),
|
||||
// the app code folder and everything in it
|
||||
@@ -221,8 +215,8 @@ namespace Umbraco.Core
|
||||
// global.asax (the app domain also monitors this, if it changes will do a full restart)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
|
||||
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
}, _logger);
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
}, _logger);
|
||||
|
||||
return _currentAssembliesHash;
|
||||
}
|
||||
@@ -232,9 +226,8 @@ namespace Umbraco.Core
|
||||
/// Writes the assembly hash file.
|
||||
/// </summary>
|
||||
private void WriteCachePluginsHash()
|
||||
{
|
||||
var filePath = GetPluginHashFilePath();
|
||||
File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8);
|
||||
{
|
||||
File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -245,41 +238,40 @@ namespace Umbraco.Core
|
||||
/// <returns>The hash.</returns>
|
||||
/// <remarks>Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the
|
||||
/// file properties (false) or the file contents (true).</remarks>
|
||||
internal static long GetFileHash(IEnumerable<Tuple<FileSystemInfo, bool>> filesAndFolders, ProfilingLogger logger)
|
||||
internal static string GetFileHash(IEnumerable<Tuple<FileSystemInfo, bool>> filesAndFolders, ProfilingLogger logger)
|
||||
{
|
||||
using (logger.TraceDuration<PluginManager>("Determining hash of code files on disk", "Hash determined"))
|
||||
{
|
||||
var hashCombiner = new HashCodeCombiner();
|
||||
|
||||
// get the distinct file infos to hash
|
||||
var uniqInfos = new HashSet<string>();
|
||||
var uniqContent = new HashSet<string>();
|
||||
|
||||
foreach (var fileOrFolder in filesAndFolders)
|
||||
using (var generator = new HashGenerator())
|
||||
{
|
||||
var info = fileOrFolder.Item1;
|
||||
if (fileOrFolder.Item2)
|
||||
foreach (var fileOrFolder in filesAndFolders)
|
||||
{
|
||||
// add each unique file's contents to the hash
|
||||
// normalize the content for cr/lf and case-sensitivity
|
||||
|
||||
if (uniqContent.Contains(info.FullName)) continue;
|
||||
uniqContent.Add(info.FullName);
|
||||
if (File.Exists(info.FullName) == false) continue;
|
||||
var content = RemoveCrLf(File.ReadAllText(info.FullName));
|
||||
hashCombiner.AddCaseInsensitiveString(content);
|
||||
var info = fileOrFolder.Item1;
|
||||
if (fileOrFolder.Item2)
|
||||
{
|
||||
// add each unique file's contents to the hash
|
||||
// normalize the content for cr/lf and case-sensitivity
|
||||
if (uniqContent.Add(info.FullName))
|
||||
{
|
||||
if (File.Exists(info.FullName) == false) continue;
|
||||
var content = RemoveCrLf(File.ReadAllText(info.FullName));
|
||||
generator.AddCaseInsensitiveString(content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add each unique folder/file to the hash
|
||||
if (uniqInfos.Add(info.FullName))
|
||||
{
|
||||
generator.AddFileSystemItem(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add each unique folder/file to the hash
|
||||
|
||||
if (uniqInfos.Contains(info.FullName)) continue;
|
||||
uniqInfos.Add(info.FullName);
|
||||
hashCombiner.AddFileSystemItem(info);
|
||||
}
|
||||
}
|
||||
|
||||
return ConvertHashToInt64(hashCombiner.GetCombinedHashCode());
|
||||
return generator.GenerateHash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,39 +295,33 @@ namespace Umbraco.Core
|
||||
/// <param name="filesAndFolders">A collection of files.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
/// <returns>The hash.</returns>
|
||||
internal static long GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, ProfilingLogger logger)
|
||||
internal static string GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, ProfilingLogger logger)
|
||||
{
|
||||
using (logger.TraceDuration<PluginManager>("Determining hash of code files on disk", "Hash determined"))
|
||||
{
|
||||
var hashCombiner = new HashCodeCombiner();
|
||||
|
||||
// get the distinct file infos to hash
|
||||
var uniqInfos = new HashSet<string>();
|
||||
|
||||
foreach (var fileOrFolder in filesAndFolders)
|
||||
using (var generator = new HashGenerator())
|
||||
{
|
||||
if (uniqInfos.Contains(fileOrFolder.FullName)) continue;
|
||||
uniqInfos.Add(fileOrFolder.FullName);
|
||||
hashCombiner.AddFileSystemItem(fileOrFolder);
|
||||
// get the distinct file infos to hash
|
||||
var uniqInfos = new HashSet<string>();
|
||||
|
||||
foreach (var fileOrFolder in filesAndFolders)
|
||||
{
|
||||
if (uniqInfos.Contains(fileOrFolder.FullName)) continue;
|
||||
uniqInfos.Add(fileOrFolder.FullName);
|
||||
generator.AddFileSystemItem(fileOrFolder);
|
||||
}
|
||||
return generator.GenerateHash();
|
||||
}
|
||||
|
||||
return ConvertHashToInt64(hashCombiner.GetCombinedHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string hash value into an Int64.
|
||||
/// </summary>
|
||||
internal static long ConvertHashToInt64(string val)
|
||||
{
|
||||
long outVal;
|
||||
return long.TryParse(val, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out outVal) ? outVal : 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cache
|
||||
|
||||
private const int ListFileOpenReadTimeout = 4000; // milliseconds
|
||||
private const int ListFileOpenWriteTimeout = 2000; // milliseconds
|
||||
|
||||
/// <summary>
|
||||
/// Attemps to retrieve the list of types from the cache.
|
||||
/// </summary>
|
||||
@@ -357,12 +343,11 @@ namespace Umbraco.Core
|
||||
{
|
||||
return ReadCache();
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = GetPluginListFilePath();
|
||||
File.Delete(filePath);
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -375,13 +360,12 @@ namespace Umbraco.Core
|
||||
|
||||
internal Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
|
||||
{
|
||||
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
|
||||
|
||||
var filePath = GetPluginListFilePath();
|
||||
if (File.Exists(filePath) == false)
|
||||
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
|
||||
|
||||
if (File.Exists(_pluginListFilePath.Value) == false)
|
||||
return cache;
|
||||
|
||||
using (var stream = File.OpenRead(filePath))
|
||||
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
while (true)
|
||||
@@ -424,34 +408,86 @@ namespace Umbraco.Core
|
||||
/// <remarks>Generally only used for resetting cache, for example during the install process.</remarks>
|
||||
public void ClearPluginCache()
|
||||
{
|
||||
var path = GetPluginListFilePath();
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
|
||||
path = GetPluginHashFilePath();
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
if (File.Exists(_pluginHashFilePath.Value))
|
||||
File.Delete(_pluginHashFilePath.Value);
|
||||
|
||||
_runtimeCache.ClearCacheItem(CacheKey);
|
||||
}
|
||||
|
||||
private string GetPluginListFilePath()
|
||||
private static string GetPluginListFilePath()
|
||||
{
|
||||
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list";
|
||||
return Path.Combine(_tempFolder, filename);
|
||||
string pluginListFilePath;
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list");
|
||||
break;
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list");
|
||||
break;
|
||||
case LocalTempStorage.Default:
|
||||
default:
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list");
|
||||
break;
|
||||
}
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(pluginListFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return pluginListFilePath;
|
||||
}
|
||||
|
||||
private string GetPluginHashFilePath()
|
||||
private static string GetPluginHashFilePath()
|
||||
{
|
||||
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash";
|
||||
return Path.Combine(_tempFolder, filename);
|
||||
string pluginHashFilePath;
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash");
|
||||
break;
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash");
|
||||
break;
|
||||
case LocalTempStorage.Default:
|
||||
default:
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash");
|
||||
break;
|
||||
}
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(pluginHashFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return pluginHashFilePath;
|
||||
}
|
||||
|
||||
internal void WriteCache()
|
||||
{
|
||||
var filePath = GetPluginListFilePath();
|
||||
|
||||
using (var stream = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite))
|
||||
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
foreach (var typeList in _types.Values)
|
||||
@@ -467,11 +503,31 @@ namespace Umbraco.Core
|
||||
|
||||
internal void UpdateCache()
|
||||
{
|
||||
// note
|
||||
// at the moment we write the cache to disk every time we update it. ideally we defer the writing
|
||||
// TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing
|
||||
// since all the updates are going to happen in a row when Umbraco starts. that being said, the
|
||||
// file is small enough, so it is not a priority.
|
||||
WriteCache();
|
||||
WriteCache();
|
||||
}
|
||||
|
||||
private static Stream GetFileStream(string path, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, int timeoutMilliseconds)
|
||||
{
|
||||
const int pauseMilliseconds = 250;
|
||||
var attempts = timeoutMilliseconds / pauseMilliseconds;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new FileStream(path, fileMode, fileAccess, fileShare);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (--attempts == 0)
|
||||
throw;
|
||||
|
||||
LogHelper.Debug<PluginManager>(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds));
|
||||
Thread.Sleep(pauseMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -545,7 +601,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -554,14 +610,14 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => discovered
|
||||
.Where(x => typeof (T).IsAssignableFrom(x)),
|
||||
.Where(x => typeof(T).IsAssignableFrom(x)),
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -584,7 +640,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -593,12 +649,12 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => discovered
|
||||
.Where(x => typeof(T).IsAssignableFrom(x))
|
||||
.Where(x => x.GetCustomAttributes<TAttribute>(false).Any()),
|
||||
@@ -620,7 +676,7 @@ namespace Umbraco.Core
|
||||
cache &= specificAssemblies == null;
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (object), typeof (TAttribute),
|
||||
typeof(object), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -637,14 +693,14 @@ namespace Umbraco.Core
|
||||
|
||||
var name = ResolvedName(baseType, attributeType);
|
||||
|
||||
lock (_typesLock)
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
lock (_typesLock)
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolvedName(Type baseType, Type attributeType)
|
||||
@@ -676,7 +732,7 @@ namespace Umbraco.Core
|
||||
// else proceed,
|
||||
typeList = new TypeList(baseType, attributeType);
|
||||
|
||||
var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false;
|
||||
var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false;
|
||||
|
||||
if (scan)
|
||||
{
|
||||
@@ -780,7 +836,7 @@ namespace Umbraco.Core
|
||||
|
||||
public TypeListKey(Type baseType, Type attributeType)
|
||||
{
|
||||
BaseType = baseType ?? typeof (object);
|
||||
BaseType = baseType ?? typeof(object);
|
||||
AttributeType = attributeType;
|
||||
}
|
||||
|
||||
@@ -798,7 +854,7 @@ namespace Umbraco.Core
|
||||
|
||||
var hash = 5381;
|
||||
hash = ((hash << 5) + hash) ^ BaseType.GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof (TypeListKey)).GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof(TypeListKey)).GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -861,7 +917,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
// look for IParameterEditor (fast, IDiscoverable) then filter
|
||||
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => propertyEditor.IsAssignableFrom(x) && x != propertyEditor);
|
||||
@@ -876,8 +932,8 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static IEnumerable<Type> ResolveParameterEditors(this PluginManager mgr)
|
||||
{
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var parameterEditor = typeof (ParameterEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
var parameterEditor = typeof(ParameterEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => x != propertyEditor && x != parameterEditor);
|
||||
@@ -959,4 +1015,4 @@ namespace Umbraco.Core
|
||||
return mgr.ResolveTypesWithAttribute<ISqlSyntaxProvider, SqlSyntaxProviderAttribute>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@ using System.Security.Permissions;
|
||||
[assembly: InternalsVisibleTo("UmbracoExamine")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Concorde.Sync")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.VisualStudio")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Courier.Core")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")]
|
||||
|
||||
@@ -52,4 +51,8 @@ using System.Security.Permissions;
|
||||
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
|
||||
|
||||
//allow this to be mocked in our unit tests
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
||||
//allow custom unit-testing code to access internals through custom adapters
|
||||
[assembly: InternalsVisibleTo("Umbraco.VisualStudio")] // backwards compat.
|
||||
[assembly: InternalsVisibleTo("Umbraco.UnitTesting.Adapter")] // new, more imperative name
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
public PropertyEditorAttribute(string alias, string name)
|
||||
{
|
||||
Mandate.ParameterNotNullOrEmpty(alias, "id");
|
||||
Mandate.ParameterNotNullOrEmpty(alias, "alias");
|
||||
Mandate.ParameterNotNullOrEmpty(name, "name");
|
||||
|
||||
Alias = alias;
|
||||
@@ -82,4 +82,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
public string Group { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
//swallow this exception, we thought it was json but it really isn't so continue returning a string
|
||||
}
|
||||
}
|
||||
return property.Value.ToString();
|
||||
return asString;
|
||||
case DataTypeDatabaseType.Integer:
|
||||
case DataTypeDatabaseType.Decimal:
|
||||
//Decimals need to be formatted with invariant culture (dots, not commas)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
if (sourceString != null)
|
||||
{
|
||||
decimal d;
|
||||
return (decimal.TryParse(sourceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out d)) ? d : 0M;
|
||||
return (decimal.TryParse(sourceString, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out d)) ? d : 0M;
|
||||
}
|
||||
|
||||
// in the database an a decimal is an a decimal
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
// Fall back on normal behaviour
|
||||
if (values.Any() == false)
|
||||
{
|
||||
return sourceString.Split(Environment.NewLine.ToCharArray());
|
||||
return sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
}
|
||||
|
||||
return values.ToArray();
|
||||
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Core.Models.Identity;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
//TODO: In v8 we need to change this to use an int? nullable TKey instead, see notes against overridden TwoFactorSignInAsync
|
||||
public class BackOfficeSignInManager : SignInManager<BackOfficeIdentityUser, int>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
@@ -49,7 +50,7 @@ namespace Umbraco.Core.Security
|
||||
public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
|
||||
{
|
||||
var result = await PasswordSignInAsyncImpl(userName, password, isPersistent, shouldLockout);
|
||||
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
@@ -104,6 +105,14 @@ namespace Umbraco.Core.Security
|
||||
var user = await UserManager.FindByNameAsync(userName);
|
||||
if (user == null)
|
||||
{
|
||||
var requestContext = _request.Context;
|
||||
if (requestContext != null)
|
||||
{
|
||||
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseInvalidLoginAttemptEvent(userName);
|
||||
}
|
||||
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
if (await UserManager.IsLockedOutAsync(user.Id))
|
||||
@@ -121,6 +130,15 @@ namespace Umbraco.Core.Security
|
||||
await UserManager.AccessFailedAsync(user.Id);
|
||||
if (await UserManager.IsLockedOutAsync(user.Id))
|
||||
{
|
||||
//at this point we've just locked the user out after too many failed login attempts
|
||||
var requestContext = _request.Context;
|
||||
if (requestContext != null)
|
||||
{
|
||||
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseAccountLockedEvent(user.Id);
|
||||
}
|
||||
|
||||
return SignInStatus.LockedOut;
|
||||
}
|
||||
}
|
||||
@@ -176,7 +194,7 @@ namespace Umbraco.Core.Security
|
||||
AllowRefresh = true,
|
||||
IssuedUtc = nowUtc,
|
||||
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -191,7 +209,9 @@ namespace Umbraco.Core.Security
|
||||
|
||||
//track the last login date
|
||||
user.LastLoginDateUtc = DateTime.UtcNow;
|
||||
user.AccessFailedCount = 0;
|
||||
if (user.AccessFailedCount > 0)
|
||||
//we have successfully logged in, reset the AccessFailedCount
|
||||
user.AccessFailedCount = 0;
|
||||
await UserManager.UpdateAsync(user);
|
||||
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
@@ -231,5 +251,67 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Two factor verification step
|
||||
/// </summary>
|
||||
/// <param name="provider"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <param name="isPersistent"></param>
|
||||
/// <param name="rememberBrowser"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
|
||||
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
|
||||
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
|
||||
/// all of this code to check for -1 instead.
|
||||
/// </remarks>
|
||||
public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)
|
||||
{
|
||||
var userId = await GetVerifiedUserIdAsync();
|
||||
if (userId == -1)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
var user = await UserManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
if (await UserManager.IsLockedOutAsync(user.Id))
|
||||
{
|
||||
return SignInStatus.LockedOut;
|
||||
}
|
||||
if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code))
|
||||
{
|
||||
// When token is verified correctly, clear the access failed count used for lockout
|
||||
await UserManager.ResetAccessFailedCountAsync(user.Id);
|
||||
await SignInAsync(user, isPersistent, rememberBrowser);
|
||||
return SignInStatus.Success;
|
||||
}
|
||||
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
||||
await UserManager.AccessFailedAsync(user.Id);
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
|
||||
/// <summary>Send a two factor code to a user</summary>
|
||||
/// <param name="provider"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
|
||||
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
|
||||
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
|
||||
/// all of this code to check for -1 instead.
|
||||
/// </remarks>
|
||||
public override async Task<bool> SendTwoFactorCodeAsync(string provider)
|
||||
{
|
||||
var userId = await GetVerifiedUserIdAsync();
|
||||
if (userId == -1)
|
||||
return false;
|
||||
|
||||
var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider);
|
||||
var identityResult = await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token);
|
||||
return identityResult.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user