Compare commits
730 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6795afd5c2 | |||
| 9411a22a0d | |||
| 461ce64feb | |||
| 53c251feb5 | |||
| c5a55b17e6 | |||
| fd577afe2c | |||
| f46ef0b006 | |||
| 04f64cd804 | |||
| dc937ec942 | |||
| cefd9e7323 | |||
| 315690e758 | |||
| fb8c519f71 | |||
| 80f172e3fb | |||
| 5eced19816 | |||
| 5df4d3509d | |||
| 4bd263e7bc | |||
| b094bfad29 | |||
| e333f6c628 | |||
| 916bc6badb | |||
| 76ee23b933 | |||
| 2a0cc71d06 | |||
| a2e6da2491 | |||
| ba7d8b0955 | |||
| 7288bcf679 | |||
| ca57b9c5e6 | |||
| 62ce7f6132 | |||
| b1f2c5d93a | |||
| 1ec07aa205 | |||
| 13055ad1f2 | |||
| d5447106a6 | |||
| c5d874464a | |||
| 1cc3b39601 | |||
| c5f4793d11 | |||
| 4175c0530f | |||
| cf97926f26 | |||
| 4b08c3a5c3 | |||
| fbda4fc8d1 | |||
| 312b994537 | |||
| dd1bfe9cbf | |||
| de17b69564 | |||
| 7f44f4a030 | |||
| 422219ab21 | |||
| 9986a3c2c2 | |||
| ce81416368 | |||
| 04e04ae47a | |||
| 0f6e7d26c9 | |||
| 51ceba6fea | |||
| 5dde2efe0d | |||
| fe2b86b681 | |||
| 368fec4ac1 | |||
| 9016147118 | |||
| bee75803f7 | |||
| 7b3f7f4ad4 | |||
| f6c25f29c0 | |||
| 6de3b920eb | |||
| 76dc311586 | |||
| 581f15b172 | |||
| daf01fb0fa | |||
| 588640037c | |||
| 7b106caf95 | |||
| 828a94bc4f | |||
| efd1ea5b86 | |||
| 376d93de61 | |||
| 33d7598d3c | |||
| 6a74967a49 | |||
| 79351efd36 | |||
| 70c9da93e9 | |||
| 521acc0618 | |||
| 95c3a7386d | |||
| 448a99d5a4 | |||
| 83f705e19f | |||
| 880ae5cb95 | |||
| 476773fce5 | |||
| 953355cddd | |||
| e0801cd05a | |||
| b363a1711a | |||
| 45fca02dd1 | |||
| b426f92e4f | |||
| 3e3372752a | |||
| 4104ffc699 | |||
| f63aa2e2ed | |||
| 57553a5956 | |||
| d2d43255aa | |||
| 9ad767a285 | |||
| b39e218d51 | |||
| 22ff362521 | |||
| 3bbeb6670b | |||
| 581b1b6fbf | |||
| 4053c52dfb | |||
| 6a80cc5708 | |||
| 01ba781e75 | |||
| b477ed726d | |||
| 147d6aa638 | |||
| efca0a904d | |||
| 7e25501ac5 | |||
| 4b581a2182 | |||
| 581eac5369 | |||
| c941ff093c | |||
| 60f9490892 | |||
| 7c864fe2e9 | |||
| 8a52865d22 | |||
| 78e5d6d3ad | |||
| 33cf983cae | |||
| 3ce6eb09aa | |||
| 963015cf4a | |||
| cc35af4eab | |||
| 7c4eb71f07 | |||
| 9282312305 | |||
| fc46569cd9 | |||
| 30f42f4ea8 | |||
| fa777c46b1 | |||
| a01f69807c | |||
| e12498ccae | |||
| 4fc51e4a8b | |||
| 4c3e66e58d | |||
| 17abd53b1b | |||
| 202a3e10b5 | |||
| 925f64c79d | |||
| c28e5f4d55 | |||
| 439c460220 | |||
| ae1702c62a | |||
| 1ed29fb8bb | |||
| c9ea1a5504 | |||
| 10da318989 | |||
| 74e3308017 | |||
| 349d89e4d7 | |||
| 201ac076c3 | |||
| bc2e9936d8 | |||
| e8e9f5ca49 | |||
| b938685db0 | |||
| 39498ad362 | |||
| 92b6e276bf | |||
| a239e92bc0 | |||
| e0025db56d | |||
| 1238c10b3d | |||
| 35177eea9f | |||
| 7cd632302b | |||
| 7142222234 | |||
| fd35d89949 | |||
| 1b3fc9fb21 | |||
| 2e98651d62 | |||
| 704e981284 | |||
| 0070d8b913 | |||
| 9c0500325d | |||
| eda7f1e527 | |||
| 018a066f86 | |||
| e33a28ac19 | |||
| 0f17a27d86 | |||
| 9c24dc71a4 | |||
| 6205d8a9f9 | |||
| 0ef57d819c | |||
| 0dfbe4d4eb | |||
| d0e99bf5d9 | |||
| c321d6dd44 | |||
| 48f2aa4f15 | |||
| 5d74e61bcf | |||
| 029ace796d | |||
| 8b39f495e5 | |||
| a5581ba13e | |||
| e8e0664129 | |||
| d468e346f1 | |||
| 2a10eed059 | |||
| 4ec04c779a | |||
| 26034fa33f | |||
| 97ddbdb1f0 | |||
| 20290582d9 | |||
| 731c51bfc7 | |||
| 78cb085e00 | |||
| f2ea4596d7 | |||
| f9d5de7af4 | |||
| 04c6fd0947 | |||
| 8bcf35734d | |||
| 4de7da2ac1 | |||
| 67d12ae2ca | |||
| e8ba069051 | |||
| bb88eb155f | |||
| a3457d2498 | |||
| 388d660e11 | |||
| b6dbc245e2 | |||
| 3efba6b576 | |||
| 3803785569 | |||
| 54407a66a4 | |||
| 3f82781c0f | |||
| bca46f11ad | |||
| a86b8f786d | |||
| ac3eaf43cb | |||
| f6d4170010 | |||
| 2b0ea45da9 | |||
| 07d05c9e0d | |||
| 26a5682a14 | |||
| 5ab364781b | |||
| c2d328685d | |||
| c013be4f36 | |||
| f3fc2bcfb8 | |||
| c8d49ed5a7 | |||
| 9c2b0a3ef6 | |||
| 703c3eefe8 | |||
| 4ab67d8cff | |||
| f8d165599d | |||
| 9355a81716 | |||
| 87575e9067 | |||
| a5b31e94b6 | |||
| 3815a6c607 | |||
| f9e094a00a | |||
| 053175a5a6 | |||
| ce7488a924 | |||
| 6cd2a15964 | |||
| f442599633 | |||
| 37b6b42570 | |||
| f1149fb826 | |||
| 3f8f96e8c3 | |||
| 8ebbfca17d | |||
| 076a8e0e23 | |||
| 44da6c9f97 | |||
| a75181bd7f | |||
| 058ad9f99a | |||
| dd440eb9d5 | |||
| 00b469b61b | |||
| 83f08538c2 | |||
| aafe0e1c6c | |||
| ef91e152b4 | |||
| add0685d3f | |||
| 27fc3a9bb3 | |||
| bbc625f65d | |||
| 8b34ab24f4 | |||
| e8faf54edf | |||
| 8548ee7761 | |||
| ac391f3bcd | |||
| 0700e37830 | |||
| 7a71f64978 | |||
| 643d0012c6 | |||
| 5f4985b4eb | |||
| 14ff7cbcc7 | |||
| 4d7f66736d | |||
| d179792cc3 | |||
| 4cb7b29f60 | |||
| fb2f9cea64 | |||
| 394f21f4be | |||
| 32cd9d44cb | |||
| 7aeed3ae2f | |||
| 8269203a47 | |||
| 1eea4867af | |||
| 237b7176bf | |||
| 1fa2c16668 | |||
| 63343d89de | |||
| 1c5e2b5f73 | |||
| 26cce02984 | |||
| 30af8cb928 | |||
| 9fd0e7771d | |||
| 68a30c3906 | |||
| 65b44137d4 | |||
| 81155c2ff8 | |||
| 59afcd8be7 | |||
| 41eeb38c5f | |||
| 4356d3f94a | |||
| ba072aec7c | |||
| 68f1120b4f | |||
| b884bed7cf | |||
| 1f083ca21c | |||
| b9ecb7e1d8 | |||
| ad8ef56920 | |||
| 1ff352ddc4 | |||
| f1d3b1a6d3 | |||
| 45f9919e57 | |||
| 3275f03aff | |||
| 857b7cc608 | |||
| c23160f915 | |||
| 3e3a180fe4 | |||
| c55cf75ada | |||
| fb60647b5a | |||
| 23950c0cac | |||
| d808438288 | |||
| 909319184c | |||
| 082e399293 | |||
| 026e46cec0 | |||
| 7e794dd021 | |||
| 0b14f255d2 | |||
| c4ab25f1ac | |||
| e6edaf1c3a | |||
| c4810ba45a | |||
| b669741adb | |||
| c232fa2add | |||
| 29f3acc23e | |||
| 3f55abaf59 | |||
| 001e15f83c | |||
| 34e3257ddb | |||
| 2b64cfb4b9 | |||
| a499744084 | |||
| c72ecda18c | |||
| 3423d8b2cc | |||
| e6bf4e6c69 | |||
| 37c4fc5f74 | |||
| ac8a0ddc58 | |||
| 406a206b03 | |||
| e7fc252f4c | |||
| 55e0af118a | |||
| c66a763406 | |||
| 718ed19fb9 | |||
| 32999b7e19 | |||
| ce63251206 | |||
| 7b641f4771 | |||
| 708d060c3b | |||
| e254dddbd4 | |||
| 87c2c5455c | |||
| 8544aa685b | |||
| 09c23e93c2 | |||
| 9946d36d00 | |||
| 7012633fdd | |||
| 6e348a3a1a | |||
| 733c920867 | |||
| 35fc997d9f | |||
| 921e8a2529 | |||
| 22bd6cd989 | |||
| 5398d24f58 | |||
| d06c92d249 | |||
| 719d520a82 | |||
| 75ace2b162 | |||
| 7ae95e31a1 | |||
| 81ba2069af | |||
| 858c21c79f | |||
| 504f32f25b | |||
| 38c961209e | |||
| c39e1175fd | |||
| 1d5348763d | |||
| 3c6ab8d34f | |||
| 0713f12ba6 | |||
| 8981fd23a8 | |||
| ab3407a1aa | |||
| dad4eafebd | |||
| 96c9ecbdd7 | |||
| e7e04de22e | |||
| 7003ef8d26 | |||
| a5c78f6325 | |||
| 6ea92daa70 | |||
| 65ece33ca9 | |||
| 85758011b5 | |||
| 49a48bbfce | |||
| 663d9fcfe8 | |||
| d6f8b878d2 | |||
| 598944d3d8 | |||
| 3caecf5161 | |||
| b02b2a61f0 | |||
| 60f7211a80 | |||
| 56dc10a4f7 | |||
| dbda6689b6 | |||
| 8d6c8ef282 | |||
| 12b4587828 | |||
| df8c75438a | |||
| 77d240c69a | |||
| 2d53da55a7 | |||
| 119dd5af29 | |||
| 18d0fec014 | |||
| e1de9452b2 | |||
| 851d587d1f | |||
| 25cb76e842 | |||
| 6a3f94d0e1 | |||
| b1358936b4 | |||
| b2798e38f7 | |||
| b9baeea1a1 | |||
| dfa1a098c8 | |||
| 3b741d3075 | |||
| 5ac0841ee4 | |||
| 63b2d10e86 | |||
| 616b9b0e92 | |||
| 606abd8ac7 | |||
| 2efe2cc88a | |||
| 8b17d69d32 | |||
| 8ad1d247fa | |||
| 6cc4ab9348 | |||
| a603fd0c46 | |||
| 4b5c49907c | |||
| 1578bc8cc7 | |||
| c860069215 | |||
| 186fd1166a | |||
| d32093136f | |||
| f3f31f2569 | |||
| e7c231c5a8 | |||
| 456b5fcee0 | |||
| 0dee1f0fac | |||
| 1f7a5ff90a | |||
| f209ad2019 | |||
| 66affa7f85 | |||
| 2590ee51ec | |||
| 4b7bd6482e | |||
| 8ff89c4f48 | |||
| a9cda9d651 | |||
| ad19eb8dff | |||
| e511554a42 | |||
| 84fd5dc2ee | |||
| 344b52d10f | |||
| 2121b7c8a0 | |||
| af9441010c | |||
| 7926a0c676 | |||
| 511e9bd3a4 | |||
| 895faf41b1 | |||
| a0306f9d55 | |||
| 9a06c3a5ff | |||
| 44102df36f | |||
| 207d6b53b5 | |||
| 0e0ab81155 | |||
| a608577a4f | |||
| 972d5f8296 | |||
| 617a91c57e | |||
| cb0e5414b9 | |||
| 22cef52261 | |||
| 41e419a49e | |||
| ccfcabc707 | |||
| efbe97a01d | |||
| 0babb7f28a | |||
| 5ba801386c | |||
| 6a95da31b0 | |||
| 66da774bf7 | |||
| 1cec38c679 | |||
| 51e9b98857 | |||
| ecc761a232 | |||
| 73937172dc | |||
| 8c1996a7a1 | |||
| 57a8df8309 | |||
| 342ce7e507 | |||
| 7d227a8cd8 | |||
| 65db5396cf | |||
| 32d5a0a57c | |||
| 742c3236c7 | |||
| e83d278b9a | |||
| ede6664e7f | |||
| 54c4e3b422 | |||
| cf951e7055 | |||
| 039ab8f1d2 | |||
| e18c91dedc | |||
| e34e536dad | |||
| 209273ba64 | |||
| 8c998fd4e6 | |||
| 88f332f5c7 | |||
| 06a87bc341 | |||
| b10e646ade | |||
| 110a3b2a12 | |||
| 1bda9f32c1 | |||
| ce8390768e | |||
| 7ccfc79080 | |||
| ed850629da | |||
| 1307cb1173 | |||
| a1b9842215 | |||
| d300bf8d6d | |||
| 70231fcee2 | |||
| 67489d496a | |||
| 78e66ce25f | |||
| dcc0374464 | |||
| 7571dab4cc | |||
| a95afa610f | |||
| 13e2112c3c | |||
| cf10f5f0ee | |||
| ca00246236 | |||
| d71b8a139f | |||
| 6bf0d6e175 | |||
| f37307d540 | |||
| 6995c184e3 | |||
| aa5d7fc9fc | |||
| cf450bebc3 | |||
| 241fbb2144 | |||
| 5cbe2b6ce0 | |||
| 513aa0b95d | |||
| 7a0c6ab975 | |||
| 2ec91a075d | |||
| 68fd74897f | |||
| 047a41c05e | |||
| 49d5005453 | |||
| f2db0fe80e | |||
| 7197c9cbb4 | |||
| b9614f9393 | |||
| 9917fb20ca | |||
| 0089f79bd7 | |||
| c468e4736e | |||
| 84f684ed31 | |||
| ea3f0d6157 | |||
| 9b327dd27e | |||
| 21ed9989f3 | |||
| 780776072d | |||
| 49bf7e43a4 | |||
| db63e70a99 | |||
| 5ed4d9ccae | |||
| e71e9d7efb | |||
| 6eb4c066c6 | |||
| ba8fe93693 | |||
| cb4de2a6a1 | |||
| e0b014afad | |||
| fcc0198910 | |||
| 3c17e30133 | |||
| ee6dbcef17 | |||
| 1b369b5ba9 | |||
| 7636f54ffd | |||
| 93cd03da28 | |||
| 1dade7bfd2 | |||
| c3b1aa36b8 | |||
| 9cc466775d | |||
| d0a91bf98b | |||
| 085c06e043 | |||
| 1e45f85f7c | |||
| 0bfa90714c | |||
| c18c936820 | |||
| c116da7545 | |||
| 00d00dac26 | |||
| 0a7bcbcc69 | |||
| 2f088d0562 | |||
| ff45f1f9cc | |||
| f50898ee6d | |||
| 6796f4d4f5 | |||
| a0785f2e0d | |||
| 1abbc31348 | |||
| f48ffd28c0 | |||
| c5e1fc5a20 | |||
| 04ac744277 | |||
| f457edfada | |||
| 8982f7b933 | |||
| 9261f46ad1 | |||
| 3106c22287 | |||
| 98bd044823 | |||
| 5afe16ae0e | |||
| 8033c6807f | |||
| 0059f703c5 | |||
| e802af109f | |||
| 8d93485a08 | |||
| 583302faa2 | |||
| 4404ce94c5 | |||
| 86bc701511 | |||
| 9fabfa92ea | |||
| 48cff73537 | |||
| 54f86c06f0 | |||
| 2d089033d0 | |||
| 83e7e198f8 | |||
| e775c19e7c | |||
| 1fcc93ff7e | |||
| a86902bd91 | |||
| 955334fa0f | |||
| 7bdf76ffa7 | |||
| 45fbb531de | |||
| 528656a337 | |||
| 5ef46ad8df | |||
| 168dc0e5da | |||
| 04d73eedfe | |||
| c5338da37d | |||
| e4296f6ea4 | |||
| 9ce87ea4e8 | |||
| bdb991f6dc | |||
| 4cebec0d39 | |||
| 1fc49ccf59 | |||
| 7184838546 | |||
| 254d4e2067 | |||
| 2817a77c5a | |||
| 7b61070f8e | |||
| a157e1d596 | |||
| 5a1fc91f3e | |||
| 5e6b5a5cc1 | |||
| 7213e3a6a0 | |||
| f2ff1636ff | |||
| 9169e64e39 | |||
| 217ca62ea0 | |||
| d216d5755c | |||
| 8db748b738 | |||
| 9b1fa2ee23 | |||
| f925309e1f | |||
| a50386aaf1 | |||
| 777d634fe2 | |||
| 049435a5e6 | |||
| 300c9aa258 | |||
| f7097d571c | |||
| 87f4cd24a0 | |||
| b151c39c3b | |||
| 9e0d0de846 | |||
| b5acd53889 | |||
| ff29493203 | |||
| 5fff884798 | |||
| 3885454b26 | |||
| fec85491b6 | |||
| 26e590a260 | |||
| bf2fdc409b | |||
| bd0cceb517 | |||
| bb01eca7b7 | |||
| 542406c402 | |||
| c44b903261 | |||
| b02b130793 | |||
| d3e73e9e30 | |||
| 1de79cf473 | |||
| 42d65a36bc | |||
| c68d7a33ca | |||
| 2773102441 | |||
| ef0d6eff2c | |||
| 1efad78214 | |||
| 46b2d8a4aa | |||
| 69c242074a | |||
| 7a5cb7058b | |||
| 13f76d2af9 | |||
| f426e3c500 | |||
| 028576d93c | |||
| c1c2339827 | |||
| 0e273d1a08 | |||
| b607c9cd87 | |||
| 5cb766d387 | |||
| 076ee7e3d8 | |||
| d149438e56 | |||
| 90213ec90d | |||
| 125c378274 | |||
| 0db4993900 | |||
| 979a0c9e25 | |||
| 3104e6d11c | |||
| 850ec2d78f | |||
| d1109fb9ad | |||
| 12eb496939 | |||
| 4578f097dc | |||
| 5f0765a39a | |||
| 823e3d1b4e | |||
| 97f2b40f4b | |||
| 9be275cf40 | |||
| 72495b3eaf | |||
| ca80a0cddc | |||
| 21707f9e08 | |||
| d8cfd8f12e | |||
| d756f884a7 | |||
| a97a5dc3fd | |||
| bdf621049f | |||
| 2c219576f6 | |||
| 8f121c4d93 | |||
| 558a284750 | |||
| 0bb27b8ebd | |||
| 3263e496aa | |||
| 2c2a9cc916 | |||
| a851cef70f | |||
| 55a4214a5c | |||
| 121b184941 | |||
| babcbd2f7a | |||
| e5e174d334 | |||
| ba82885b6d | |||
| f453ee2623 | |||
| 5b00265394 | |||
| 68ba0ca1e2 | |||
| b22e9d0911 | |||
| 6a59a90964 | |||
| 26a042f291 | |||
| 98c1f9820f | |||
| 6c91d54372 | |||
| d4a2244190 | |||
| 14de3fee46 | |||
| 17d3548bbb | |||
| 9754c4511b | |||
| 442d359ab0 | |||
| bbed2fa8f6 | |||
| 3089a0ab3a | |||
| bfb5dd91cc | |||
| b3ac3a03f3 | |||
| bd0ca43b83 | |||
| f39531216c | |||
| 84de083d52 | |||
| 6ccb822cb5 | |||
| c94338d2b6 | |||
| 3ba001164f | |||
| a8c0367492 | |||
| 91646212fd | |||
| 4c9b2f552a | |||
| f24effdf0e | |||
| e9a4321ee8 | |||
| 3395445d7c | |||
| 9ea2fa9d8c | |||
| cddc6f3116 | |||
| 769f0d97d2 | |||
| f7d11bf936 | |||
| 0fc868c9ba | |||
| a3f4275b30 | |||
| cfd3301965 | |||
| 48041c3163 | |||
| cc57cd6eb7 | |||
| 193eb259a6 | |||
| 6db18d83be | |||
| 872530296f | |||
| 71b5e5223f | |||
| f3935b5007 | |||
| e35a748c92 | |||
| a3af1d478d | |||
| 7d8fdff6c1 | |||
| a813c0bfea | |||
| 4870554f47 | |||
| 547767fc0f | |||
| 74126134b1 | |||
| 6136118699 | |||
| 114b58c5ca | |||
| 3019d67922 | |||
| 97518c837f | |||
| 727250d2ab | |||
| 66e3a00e56 | |||
| aebea2da31 | |||
| 1634789641 | |||
| 631a9f92ea | |||
| 229a15be4f | |||
| 28d7a4a6dc | |||
| 21f003e705 | |||
| 0d80bf6a84 | |||
| e88f5c8fe2 | |||
| 7cb1a497e5 | |||
| b72c8ef953 | |||
| 04a09bd4d6 | |||
| 311fcfe328 | |||
| 33d09b14be | |||
| cdab89062e | |||
| 38571f2cdd | |||
| fcd90aa702 | |||
| ae17172126 | |||
| ecca480334 | |||
| 095a7c0aa7 | |||
| 169c6b6372 | |||
| 8ab2f7c257 | |||
| 51215f11af | |||
| 3df103eae2 | |||
| be1172d604 | |||
| 7cf8cd3518 | |||
| 1757e7b4c8 | |||
| a68eb91c14 | |||
| 7c9476ca41 | |||
| 5cee5a27c9 | |||
| 1e34570a1f | |||
| 7e930330e2 | |||
| cc3355b1f9 | |||
| ee9deacf92 | |||
| 4e9a8623f0 | |||
| 904265cdf5 | |||
| 4a191f0711 | |||
| 6be9a25d99 | |||
| 89cbe53b63 | |||
| 3ce3a6f6ea | |||
| b03e9f8fd5 | |||
| 4cab1c89b4 | |||
| 88fd5a9623 | |||
| a58bf507ed |
+6
-12
@@ -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,9 +132,11 @@ apidocs/api/*
|
||||
build/docs.zip
|
||||
build/ui-docs.zip
|
||||
build/csharp-docs.zip
|
||||
build/msbuild.log
|
||||
.vs/
|
||||
src/packages/
|
||||
src/PrecompiledWeb/*
|
||||
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
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://ci.appveyor.com/project/Umbraco/umbraco-cms-hs8dx/branch/dev-v7)
|
||||
[](https://ci.appveyor.com/project/Umbraco/umbraco-cms-b2cri/branch/dev-v7)
|
||||
|
||||
Umbraco CMS
|
||||
===========
|
||||
@@ -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
|
||||
-207
@@ -1,207 +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 vswhere.exe for VS2017+
|
||||
IF NOT EXIST "%toolsFolder%vswhere.exe" (
|
||||
ECHO vswhere not found - fetching now
|
||||
"%nuGetExecutable%" install vswhere -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
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,68 +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
|
||||
|
||||
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%')"
|
||||
)
|
||||
|
||||
:: 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
|
||||
)
|
||||
|
||||
:: 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"
|
||||
|
||||
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 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,615 @@
|
||||
|
||||
# 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"
|
||||
}
|
||||
|
||||
#
|
||||
# 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
|
||||
}
|
||||
|
||||
#
|
||||
# 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"
|
||||
}
|
||||
|
||||
#
|
||||
# Copies the Azure Gallery script to output
|
||||
#
|
||||
function Prepare-AzureGallery
|
||||
{
|
||||
param (
|
||||
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
|
||||
)
|
||||
|
||||
$src = "$($uenv.SolutionRoot)\src"
|
||||
$tmp = "$($uenv.SolutionRoot)\build.tmp"
|
||||
$out = "$($uenv.SolutionRoot)\build.out"
|
||||
$psScript = "$($uenv.SolutionRoot)\build\azuregalleryrelease.ps1"
|
||||
|
||||
Write-Host ">> Copy azuregalleryrelease.ps1 to output folder"
|
||||
Copy-Item $psScript $out
|
||||
}
|
||||
|
||||
#
|
||||
# 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 "prepare-azuregallery")
|
||||
{
|
||||
Prepare-AzureGallery $uenv
|
||||
}
|
||||
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
|
||||
Prepare-AzureGallery $uenv
|
||||
}
|
||||
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,95 @@
|
||||
# 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
|
||||
}
|
||||
}
|
||||
@@ -29,12 +29,11 @@
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
|
||||
<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.83, 1.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.85, 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="semver" version="[1.1.2, 3.0.0)" />
|
||||
@@ -44,65 +43,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.0, 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" />
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
*** IMPORTANT NOTICE FOR 7.6 UPGRADES ***
|
||||
*** IMPORTANT NOTICE FOR 7.7 UPGRADES ***
|
||||
|
||||
Be sure to read the version specific upgrade information before proceeding:
|
||||
https://our.umbraco.org/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-6-0
|
||||
https://our.umbraco.org/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-7-0
|
||||
|
||||
You will most likely need to make some changes to your web.config and you will need to be
|
||||
aware of the breaking changes listed there to see if these affect your installation.
|
||||
Depending on the version you are upgrading from, you may need to make some changes to your web.config
|
||||
and you will need to be aware of the breaking changes listed there to see if these affect your installation.
|
||||
|
||||
|
||||
Don't forget to build!
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="umbracoDefaultUILanguage" value="en-US" xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
|
||||
</appSettings>
|
||||
|
||||
<umbracoConfiguration xdt:Transform="InsertIfMissing">
|
||||
|
||||
@@ -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,3 +0,0 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.7.0
|
||||
beta
|
||||
@@ -0,0 +1,59 @@
|
||||
Param(
|
||||
[string]$GitHubPersonalAccessToken,
|
||||
[string]$Directory
|
||||
)
|
||||
$workingDirectory = $Directory
|
||||
CD $workingDirectory
|
||||
|
||||
# Clone repo
|
||||
$fullGitUrl = "https://$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
|
||||
git clone $fullGitUrl 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Remove everything so that unzipping the release later will update everything
|
||||
# Don't remove the readme file nor the git directory
|
||||
Write-Host "Cleaning up git directory before adding new version"
|
||||
Remove-Item -Recurse $workingDirectory\$env:GIT_REPOSITORYNAME\* -Exclude README.md,.git
|
||||
|
||||
# Find release zip
|
||||
$zipsDir = "$workingDirectory\$env:BUILD_DEFINITIONNAME\zips"
|
||||
$pattern = "UmbracoCms.([0-9]{1,2}.[0-9]{1,3}.[0-9]{1,3}).zip"
|
||||
Write-Host "Searching for Umbraco release files in $workingDirectory\$zipsDir for a file with pattern $pattern"
|
||||
$file = (Get-ChildItem $zipsDir | Where-Object { $_.Name -match "$pattern" })
|
||||
|
||||
if($file)
|
||||
{
|
||||
# Get release name
|
||||
$version = [regex]::Match($file.Name, $pattern).captures.groups[1].value
|
||||
$releaseName = "Umbraco $version"
|
||||
Write-Host "Found $releaseName"
|
||||
|
||||
# Unzip into repository to update release
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
Write-Host "Unzipping $($file.FullName) to $workingDirectory\$env:GIT_REPOSITORYNAME"
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$workingDirectory\$env:GIT_REPOSITORYNAME")
|
||||
|
||||
# Telling git who we are
|
||||
git config --global user.email "coffee@umbraco.com" 2>&1 | % { $_.ToString() }
|
||||
git config --global user.name "Umbraco HQ" 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Commit
|
||||
CD $env:GIT_REPOSITORYNAME
|
||||
Write-Host "Committing Umbraco $version Release from Build Output"
|
||||
|
||||
git add . 2>&1 | % { $_.ToString() }
|
||||
git commit -m " Release $releaseName from Build Output" 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Tag the release
|
||||
git tag -a "v$version" -m "v$version"
|
||||
|
||||
# Push release to master
|
||||
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$GitHubPersonalAccessToken@$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
|
||||
git push $fullGitAuthUrl 2>&1 | % { $_.ToString() }
|
||||
|
||||
#Push tag to master
|
||||
git push $fullGitAuthUrl --tags 2>&1 | % { $_.ToString() }
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Error "Umbraco release file not found, searched in $workingDirectory\$zipsDir for a file with pattern $pattern - cancelling"
|
||||
}
|
||||
@@ -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.7.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.7.0-beta")]
|
||||
[assembly: AssemblyFileVersion("7.7.3")]
|
||||
[assembly: AssemblyInformationalVersion("7.7.3")]
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -34,7 +35,31 @@ namespace Umbraco.Core
|
||||
{
|
||||
return Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will return a list of IAction's based on a string (letter) list. Each character in the list may represent
|
||||
/// an IAction. This will associate any found IActions based on the Letter property of the IAction with the character being referenced.
|
||||
/// </summary>
|
||||
/// <param name="actions"></param>
|
||||
/// <returns>returns a list of actions that have an associated letter found in the action string list</returns>
|
||||
public IEnumerable<IAction> FromActionSymbols(IEnumerable<string> actions)
|
||||
{
|
||||
var allActions = Actions.ToArray();
|
||||
return actions
|
||||
.Select(c => allActions.FirstOrDefault(a => a.Letter.ToString(CultureInfo.InvariantCulture) == c))
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string (letter) representation of the actions that make up the actions collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<string> ToActionSymbols(IEnumerable<IAction> actions)
|
||||
{
|
||||
return actions.Select(x => x.Letter.ToString(CultureInfo.InvariantCulture)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an Action if it exists.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class FileSystemProviderElement : ConfigurationElement
|
||||
public class FileSystemProviderElement : ConfigurationElement, IFileSystemProviderElement
|
||||
{
|
||||
private const string ALIAS_KEY = "alias";
|
||||
private const string TYPE_KEY = "type";
|
||||
@@ -38,5 +38,30 @@ namespace Umbraco.Core.Configuration
|
||||
return ((KeyValueConfigurationCollection)(base[PARAMETERS_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
string IFileSystemProviderElement.Alias
|
||||
{
|
||||
get { return Alias; }
|
||||
}
|
||||
|
||||
string IFileSystemProviderElement.Type
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
private IDictionary<string, string> _params;
|
||||
IDictionary<string, string> IFileSystemProviderElement.Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_params != null) return _params;
|
||||
_params = new Dictionary<string, string>();
|
||||
foreach (KeyValueConfigurationElement element in Parameters)
|
||||
{
|
||||
_params.Add(element.Key, element.Value);
|
||||
}
|
||||
return _params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Text;
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
[ConfigurationCollection(typeof(FileSystemProviderElement), AddItemName = "Provider")]
|
||||
public class FileSystemProviderElementCollection : ConfigurationElementCollection
|
||||
public class FileSystemProviderElementCollection : ConfigurationElementCollection, IEnumerable<IFileSystemProviderElement>
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
@@ -19,12 +19,25 @@ namespace Umbraco.Core.Configuration
|
||||
return ((FileSystemProviderElement)(element)).Alias;
|
||||
}
|
||||
|
||||
new public FileSystemProviderElement this[string key]
|
||||
public new FileSystemProviderElement this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (FileSystemProviderElement)BaseGet(key);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator<IFileSystemProviderElement> IEnumerable<IFileSystemProviderElement>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as IFileSystemProviderElement;
|
||||
}
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class FileSystemProvidersSection : ConfigurationSection
|
||||
public class FileSystemProvidersSection : ConfigurationSection, IFileSystemProvidersSection
|
||||
{
|
||||
|
||||
[ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)]
|
||||
@@ -14,5 +14,17 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
get { return ((FileSystemProviderElementCollection)(base[""])); }
|
||||
}
|
||||
|
||||
private IDictionary<string, IFileSystemProviderElement> _providers;
|
||||
|
||||
IDictionary<string, IFileSystemProviderElement> IFileSystemProvidersSection.Providers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_providers != null) return _providers;
|
||||
_providers = Providers.ToDictionary(x => x.Alias, x => x);
|
||||
return _providers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,24 +538,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
class GridEditorsConfig : IGridEditorsConfig
|
||||
internal class GridEditorsConfig : IGridEditorsConfig
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public interface IFileSystemProviderElement
|
||||
{
|
||||
string Alias { get; }
|
||||
string Type { get; }
|
||||
IDictionary<string, string> Parameters { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public interface IFileSystemProvidersSection
|
||||
{
|
||||
IDictionary<string, IFileSystemProviderElement> Providers { get; }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal enum ContentXmlStorage
|
||||
internal enum LocalTempStorage
|
||||
{
|
||||
Default,
|
||||
AspNetTemp,
|
||||
@@ -357,6 +357,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
string IContentSection.LoginBackgroundImage
|
||||
{
|
||||
get { return LoginBackgroundImage; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
bool EnablePropertyValueConverters { get; }
|
||||
|
||||
string LoginBackgroundImage { get; }
|
||||
string LoginBackgroundImage { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,23 @@
|
||||
bool KeepUserLoggedIn { get; }
|
||||
|
||||
bool HideDisabledUsersInBackoffice { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used to enable/disable the forgot password functionality on the back office login screen
|
||||
/// </summary>
|
||||
bool AllowPasswordReset { get; }
|
||||
|
||||
string AuthCookieName { get; }
|
||||
|
||||
string AuthCookieDomain { get; }
|
||||
string AuthCookieDomain { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A boolean indicating that by default the email address will be the username
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Even if this is true and the username is different from the email in the database, the username field will still be shown.
|
||||
/// When this is false, the username and email fields will be shown in the user section.
|
||||
/// </remarks>
|
||||
bool UsernameIsEmail { get; }
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,28 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return GetOptionalTextElement("hideDisabledUsersInBackoffice", false); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to enable/disable the forgot password functionality on the back office login screen
|
||||
/// </summary>
|
||||
[ConfigurationProperty("allowPasswordReset")]
|
||||
internal InnerTextConfigurationElement<bool> AllowPasswordReset
|
||||
{
|
||||
get { return GetOptionalTextElement("allowPasswordReset", true); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A boolean indicating that by default the email address will be the username
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Even if this is true and the username is different from the email in the database, the username field will still be shown.
|
||||
/// When this is false, the username and email fields will be shown in the user section.
|
||||
/// </remarks>
|
||||
[ConfigurationProperty("usernameIsEmail")]
|
||||
internal InnerTextConfigurationElement<bool> UsernameIsEmail
|
||||
{
|
||||
get { return GetOptionalTextElement("usernameIsEmail", true); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("authCookieName")]
|
||||
internal InnerTextConfigurationElement<string> AuthCookieName
|
||||
{
|
||||
@@ -44,11 +60,26 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return HideDisabledUsersInBackoffice; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to enable/disable the forgot password functionality on the back office login screen
|
||||
/// </summary>
|
||||
bool ISecuritySection.AllowPasswordReset
|
||||
{
|
||||
get { return AllowPasswordReset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A boolean indicating that by default the email address will be the username
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Even if this is true and the username is different from the email in the database, the username field will still be shown.
|
||||
/// When this is false, the username and email fields will be shown in the user section.
|
||||
/// </remarks>
|
||||
bool ISecuritySection.UsernameIsEmail
|
||||
{
|
||||
get { return UsernameIsEmail; }
|
||||
}
|
||||
|
||||
string ISecuritySection.AuthCookieName
|
||||
{
|
||||
get { return AuthCookieName; }
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.7.0");
|
||||
private static readonly Version Version = new Version("7.7.3");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "beta"; } }
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -232,6 +232,16 @@ namespace Umbraco.Core
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
|
||||
public const string AdminGroupAlias = "admin";
|
||||
|
||||
public const string TranslatorGroupAlias = "translator";
|
||||
|
||||
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
|
||||
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
|
||||
public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN";
|
||||
@@ -18,7 +19,7 @@ namespace Umbraco.Core
|
||||
|
||||
internal const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
|
||||
internal const string ForceReAuthFlag = "umbraco-force-auth";
|
||||
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a method to retrieve an artifact's unique identifier.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Artifacts are uniquely identified by their <see cref="Udi"/>, however they represent
|
||||
/// elements in Umbraco that may be uniquely identified by another value. For example,
|
||||
/// a content type is uniquely identified by its alias. If someone creates a new content
|
||||
/// type, and tries to deploy it to a remote environment where a content type with the
|
||||
/// same alias already exists, both content types end up having different <see cref="Udi"/>
|
||||
/// but the same alias. By default, Deploy would fail and throw when trying to save the
|
||||
/// new content type (duplicate alias). However, if the connector also implements this
|
||||
/// interface, the situation can be detected beforehand and reported in a nicer way.
|
||||
/// </remarks>
|
||||
public interface IUniqueIdentifyingServiceConnector
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the specified artifact.
|
||||
/// </summary>
|
||||
/// <param name="artifact">The artifact.</param>
|
||||
/// <returns>The unique identifier.</returns>
|
||||
string GetUniqueIdentifier(IArtifact artifact);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Events;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A utility class for sending emails
|
||||
/// </summary>
|
||||
public class EmailSender : IEmailSender
|
||||
{
|
||||
//TODO: This should encapsulate a BackgroundTaskRunner with a queue to send these emails!
|
||||
|
||||
private readonly bool _enableEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public EmailSender() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
internal EmailSender(bool enableEvents)
|
||||
{
|
||||
_enableEvents = enableEvents;
|
||||
}
|
||||
|
||||
private static readonly Lazy<bool> SmtpConfigured = new Lazy<bool>(() => GlobalSettings.HasSmtpServerConfigured(HttpRuntime.AppDomainAppVirtualPath));
|
||||
|
||||
/// <summary>
|
||||
/// Sends the message non-async
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Send(MailMessage message)
|
||||
{
|
||||
if (SmtpConfigured.Value == false && _enableEvents)
|
||||
{
|
||||
OnSendEmail(new SendEmailEventArgs(message));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
client.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the message async
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendAsync(MailMessage message)
|
||||
{
|
||||
if (SmtpConfigured.Value == false && _enableEvents)
|
||||
{
|
||||
OnSendEmail(new SendEmailEventArgs(message));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
if (client.DeliveryMethod == SmtpDeliveryMethod.Network)
|
||||
{
|
||||
await client.SendMailAsync(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the application should be able to send a required application email
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We assume this is possible if either an event handler is registered or an smtp server is configured
|
||||
/// </remarks>
|
||||
internal static bool CanSendRequiredEmail
|
||||
{
|
||||
get { return EventHandlerRegistered || SmtpConfigured.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns true if an event handler has been registered
|
||||
/// </summary>
|
||||
internal static bool EventHandlerRegistered
|
||||
{
|
||||
get { return SendEmail != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that is raised when no smtp server is configured if events are enabled
|
||||
/// </summary>
|
||||
internal static event EventHandler<SendEmailEventArgs> SendEmail;
|
||||
|
||||
private static void OnSendEmail(SendEmailEventArgs e)
|
||||
{
|
||||
var handler = SendEmail;
|
||||
if (handler != null) handler(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,5 +298,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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class SendEmailEventArgs : EventArgs
|
||||
{
|
||||
public MailMessage Message { get; private set; }
|
||||
|
||||
public SendEmailEventArgs(MailMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple abstraction to send an email message
|
||||
/// </summary>
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendAsync(MailMessage message);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
public class FileSystemProviderManager
|
||||
{
|
||||
private readonly FileSystemProvidersSection _config;
|
||||
private readonly IFileSystemProvidersSection _config;
|
||||
private readonly ConcurrentSet<ShadowWrapper> _wrappers = new ConcurrentSet<ShadowWrapper>();
|
||||
|
||||
private readonly ConcurrentDictionary<string, ProviderConstructionInfo> _providerLookup = new ConcurrentDictionary<string, ProviderConstructionInfo>();
|
||||
@@ -28,16 +28,45 @@ namespace Umbraco.Core.IO
|
||||
private ShadowWrapper _mvcViewsFileSystem;
|
||||
|
||||
#region Singleton & Constructor
|
||||
|
||||
private static readonly FileSystemProviderManager Instance = new FileSystemProviderManager();
|
||||
|
||||
private static volatile FileSystemProviderManager _instance;
|
||||
private static readonly object _instanceLocker = new object();
|
||||
|
||||
public static FileSystemProviderManager Current
|
||||
{
|
||||
get { return Instance; }
|
||||
get
|
||||
{
|
||||
if (_instance != null) return _instance;
|
||||
lock (_instanceLocker)
|
||||
{
|
||||
return _instance ?? (_instance = new FileSystemProviderManager());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For tests only, allows setting the value of the singleton "Current" property
|
||||
/// </summary>
|
||||
/// <param name="instance"></param>
|
||||
public static void SetCurrent(FileSystemProviderManager instance)
|
||||
{
|
||||
lock (_instanceLocker)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ResetCurrent()
|
||||
{
|
||||
lock (_instanceLocker)
|
||||
{
|
||||
if (_instance != null)
|
||||
_instance.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// for tests only, totally unsafe
|
||||
internal void Reset()
|
||||
private void Reset()
|
||||
{
|
||||
_wrappers.Clear();
|
||||
_providerLookup.Clear();
|
||||
@@ -52,10 +81,24 @@ namespace Umbraco.Core.IO
|
||||
// beware: means that we capture the "current" scope provider - take care in tests!
|
||||
get { return ApplicationContext.Current == null ? null : ApplicationContext.Current.ScopeProvider as IScopeProviderInternal; }
|
||||
}
|
||||
|
||||
internal FileSystemProviderManager()
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that can be used for tests
|
||||
/// </summary>
|
||||
/// <param name="configSection"></param>
|
||||
public FileSystemProviderManager(IFileSystemProvidersSection configSection)
|
||||
{
|
||||
_config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
|
||||
if (configSection == null) throw new ArgumentNullException("configSection");
|
||||
_config = configSection;
|
||||
CreateWellKnownFileSystems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor that will read the config from the locally found config section
|
||||
/// </summary>
|
||||
public FileSystemProviderManager()
|
||||
{
|
||||
_config = (FileSystemProvidersSection)ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
|
||||
CreateWellKnownFileSystems();
|
||||
}
|
||||
|
||||
@@ -150,8 +193,9 @@ namespace Umbraco.Core.IO
|
||||
private ProviderConstructionInfo GetUnderlyingFileSystemCtor(string alias, Func<IFileSystem> fallback)
|
||||
{
|
||||
// get config
|
||||
var providerConfig = _config.Providers[alias];
|
||||
if (providerConfig == null)
|
||||
IFileSystemProviderElement providerConfig;
|
||||
|
||||
if (_config.Providers.TryGetValue(alias, out providerConfig) == false)
|
||||
{
|
||||
if (fallback != null) return null;
|
||||
throw new ArgumentException(string.Format("No provider found with alias {0}.", alias));
|
||||
@@ -166,17 +210,21 @@ namespace Umbraco.Core.IO
|
||||
if (providerType.IsAssignableFrom(typeof(IFileSystem)))
|
||||
throw new InvalidOperationException(string.Format("Type {0} does not implement IFileSystem.", providerType.FullName));
|
||||
|
||||
// find a ctor matching the config parameters
|
||||
// find a ctor matching the config parameters
|
||||
var paramCount = providerConfig.Parameters != null ? providerConfig.Parameters.Count : 0;
|
||||
var constructor = providerType.GetConstructors().SingleOrDefault(x
|
||||
=> x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.AllKeys.Contains(y.Name)));
|
||||
=> x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.Keys.Contains(y.Name)));
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} has no ctor matching the {1} configuration parameter(s).", providerType.FullName, paramCount));
|
||||
|
||||
var parameters = new object[paramCount];
|
||||
if (providerConfig.Parameters != null) // keeps ReSharper happy
|
||||
|
||||
if (providerConfig.Parameters != null)
|
||||
{
|
||||
var allKeys = providerConfig.Parameters.Keys.ToArray();
|
||||
for (var i = 0; i < paramCount; i++)
|
||||
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
|
||||
parameters[i] = providerConfig.Parameters[allKeys[i]];
|
||||
}
|
||||
|
||||
return new ProviderConstructionInfo
|
||||
{
|
||||
|
||||
@@ -73,19 +73,19 @@ 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
|
||||
// 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
|
||||
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);
|
||||
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,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -278,10 +277,10 @@ namespace Umbraco.Core.Models
|
||||
if (Properties.Contains(propertyTypeAlias) == false)
|
||||
{
|
||||
return default(TPassType);
|
||||
}
|
||||
}
|
||||
|
||||
var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo<TPassType>();
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -42,6 +42,14 @@ namespace Umbraco.Core.Models.Identity
|
||||
|
||||
private BackOfficeIdentityUser()
|
||||
{
|
||||
_startMediaIds = new int[] { };
|
||||
_startContentIds = new int[] { };
|
||||
_groups = new IReadOnlyUserGroup[] { };
|
||||
_allowedSections = new string[] { };
|
||||
_culture = Configuration.GlobalSettings.DefaultUILanguage;
|
||||
_groups = new IReadOnlyUserGroup[0];
|
||||
_roles = new ObservableCollection<IdentityUserRole<string>>();
|
||||
_roles.CollectionChanged += _roles_CollectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -247,6 +255,11 @@ namespace Umbraco.Core.Models.Identity
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a 1:1 mapping with IUser.IsApproved
|
||||
/// </summary>
|
||||
internal bool IsApproved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to make the retrieval lazy
|
||||
/// </summary>
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
.ForMember(user => user.EmailConfirmed, expression => expression.MapFrom(user => user.EmailConfirmedDate.HasValue))
|
||||
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
|
||||
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
|
||||
.ForMember(user => user.IsApproved, expression => expression.MapFrom(user => user.IsApproved))
|
||||
.ForMember(user => user.UserName, expression => expression.MapFrom(user => user.Username))
|
||||
.ForMember(user => user.PasswordHash, expression => expression.MapFrom(user => GetPasswordHash(user.RawPasswordValue)))
|
||||
.ForMember(user => user.Culture, expression => expression.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
|
||||
@@ -17,7 +17,30 @@ namespace Umbraco.Core.Models.Membership
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the aggregate permissions in the permission set
|
||||
/// Returns the aggregate permissions in the permission set for a single node
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This value is only calculated once per node
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetAllPermissions(int entityId)
|
||||
{
|
||||
if (_aggregateNodePermissions == null)
|
||||
_aggregateNodePermissions = new Dictionary<int, string[]>();
|
||||
|
||||
string[] entityPermissions;
|
||||
if (_aggregateNodePermissions.TryGetValue(entityId, out entityPermissions) == false)
|
||||
{
|
||||
entityPermissions = this.Where(x => x.EntityId == entityId).SelectMany(x => x.AssignedPermissions).Distinct().ToArray();
|
||||
_aggregateNodePermissions[entityId] = entityPermissions;
|
||||
}
|
||||
return entityPermissions;
|
||||
}
|
||||
|
||||
private Dictionary<int, string[]> _aggregateNodePermissions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the aggregate permissions in the permission set for all nodes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
|
||||
@@ -15,8 +15,19 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
string Name { get; set; }
|
||||
int SessionTimeout { get; set; }
|
||||
|
||||
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
int StartContentId { get; set; }
|
||||
|
||||
int[] StartContentIds { get; set; }
|
||||
int[] StartMediaIds { get; set; }
|
||||
|
||||
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
int StartMediaId { get; set; }
|
||||
|
||||
int[] StartMediaIds { get; set; }
|
||||
|
||||
string Language { get; set; }
|
||||
|
||||
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class User : Entity, IUser
|
||||
public class User : Entity, IUser, IProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for creating a new/empty user
|
||||
@@ -403,34 +403,38 @@ namespace Umbraco.Core.Models.Membership
|
||||
var groups = Groups.ToArray();
|
||||
var admin = groups.FirstOrDefault(x => x.Alias == Constants.Security.AdminGroupAlias);
|
||||
if (admin != null)
|
||||
{
|
||||
{
|
||||
//if the group isn't IUserGroup we'll need to look it up
|
||||
var realGroup = admin as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(admin.Id);
|
||||
realGroup.AddAllowedSection(sectionAlias);
|
||||
//now we need to flag this for saving (hack!)
|
||||
GroupsToSave.Add(realGroup);
|
||||
}
|
||||
|
||||
//now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade.
|
||||
//this comes in the alias form of userName + 'Group'
|
||||
var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group"));
|
||||
if (customUserGroup != null)
|
||||
{
|
||||
//if the group isn't IUserGroup we'll need to look it up
|
||||
var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id);
|
||||
realGroup.AddAllowedSection(sectionAlias);
|
||||
//now we need to flag this for saving (hack!)
|
||||
GroupsToSave.Add(realGroup);
|
||||
}
|
||||
|
||||
//ok, so the user doesn't have a 1:1 group, we'll need to flag it for creation
|
||||
var newUserGroup = new UserGroup
|
||||
{
|
||||
Alias = Username + "Group",
|
||||
Name = "Group for " + Username
|
||||
};
|
||||
newUserGroup.AddAllowedSection(sectionAlias);
|
||||
GroupsToSave.Add(newUserGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
//now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade.
|
||||
//this comes in the alias form of userName + 'Group'
|
||||
var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group"));
|
||||
if (customUserGroup != null)
|
||||
{
|
||||
//if the group isn't IUserGroup we'll need to look it up
|
||||
var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id);
|
||||
realGroup.AddAllowedSection(sectionAlias);
|
||||
//now we need to flag this for saving (hack!)
|
||||
GroupsToSave.Add(realGroup);
|
||||
}
|
||||
|
||||
//ok, so the user doesn't have a 1:1 group, we'll need to flag it for creation
|
||||
var newUserGroup = new UserGroup
|
||||
{
|
||||
Alias = Username + "Group",
|
||||
Name = "Group for " + Username
|
||||
};
|
||||
newUserGroup.AddAllowedSection(sectionAlias);
|
||||
//add this user to this new group
|
||||
AddGroup(newUserGroup);
|
||||
GroupsToSave.Add(newUserGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -475,6 +479,18 @@ namespace Umbraco.Core.Models.Membership
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _sessionTimeout, Ps.Value.SessionTimeoutSelector); }
|
||||
}
|
||||
|
||||
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
int IUser.StartContentId
|
||||
{
|
||||
get
|
||||
{
|
||||
var calculatedStartNodes = this.CalculateContentStartNodeIds(ApplicationContext.Current.Services.EntityService);
|
||||
return calculatedStartNodes.Length == 0 ? -1 : calculatedStartNodes[0];
|
||||
}
|
||||
set { StartContentIds = new[] { value }; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start content id.
|
||||
/// </summary>
|
||||
@@ -489,6 +505,18 @@ namespace Umbraco.Core.Models.Membership
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _startContentIds, Ps.Value.StartContentIdSelector, Ps.Value.IntegerEnumerableComparer); }
|
||||
}
|
||||
|
||||
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
int IUser.StartMediaId
|
||||
{
|
||||
get
|
||||
{
|
||||
var calculatedStartNodes = this.CalculateMediaStartNodeIds(ApplicationContext.Current.Services.EntityService);
|
||||
return calculatedStartNodes.Length == 0 ? -1 : calculatedStartNodes[0];
|
||||
}
|
||||
set { StartMediaIds = new[] {value}; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start media id.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup
|
||||
public class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup
|
||||
{
|
||||
private int? _startContentId;
|
||||
private int? _startMediaId;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
if (_index.HasValue) return _index.Value;
|
||||
|
||||
// slow -- and don't cache, not in a set
|
||||
if (_contentSet == null) return WrappedContentInternal.GetIndex();
|
||||
if (_contentSet == null) return Content.GetIndex();
|
||||
|
||||
// slow -- but cache for next time
|
||||
var index = _contentSet.FindIndex(x => x.Id == Id);
|
||||
@@ -147,7 +147,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
public override IEnumerable<IPublishedContent> ContentSet
|
||||
{
|
||||
get { return _contentSet ?? WrappedContentInternal.ContentSet; }
|
||||
get { return _contentSet ?? Content.ContentSet; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -161,8 +161,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
get
|
||||
{
|
||||
return _properties == null
|
||||
? WrappedContentInternal.Properties
|
||||
: WrappedContentInternal.Properties.Union(_properties).ToList();
|
||||
? Content.Properties
|
||||
: Content.Properties.Union(_properties).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,15 +175,15 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias));
|
||||
if (property != null) return property.HasValue ? property.Value : null;
|
||||
}
|
||||
return WrappedContentInternal[alias];
|
||||
return Content[alias];
|
||||
}
|
||||
}
|
||||
|
||||
public override IPublishedProperty GetProperty(string alias)
|
||||
{
|
||||
return _properties == null
|
||||
? WrappedContentInternal.GetProperty(alias)
|
||||
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? WrappedContentInternal.GetProperty(alias);
|
||||
? Content.GetProperty(alias)
|
||||
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -9,6 +9,6 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
public Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
|
||||
public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
: base (content)
|
||||
{ }
|
||||
|
||||
public Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
|
||||
public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
public virtual Guid Key { get { return ((IPublishedContentWithKey) WrappedContentInternal).Key; } }
|
||||
public virtual Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// </summary>
|
||||
public abstract class PublishedContentWrapped : IPublishedContent
|
||||
{
|
||||
protected readonly IPublishedContent WrappedContentInternal;
|
||||
protected readonly IPublishedContent Content;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="PublishedContentWrapped"/> class
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <param name="content">The content to wrap and extend.</param>
|
||||
protected PublishedContentWrapped(IPublishedContent content)
|
||||
{
|
||||
WrappedContentInternal = content;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,21 +45,21 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <returns>The wrapped content, that was passed as an argument to the constructor.</returns>
|
||||
public IPublishedContent Unwrap()
|
||||
{
|
||||
return WrappedContentInternal;
|
||||
return Content;
|
||||
}
|
||||
|
||||
#region ContentSet
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> ContentSet
|
||||
{
|
||||
get { return WrappedContentInternal.ContentSet; }
|
||||
get { return Content.ContentSet; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentType
|
||||
|
||||
public virtual PublishedContentType ContentType { get { return WrappedContentInternal.ContentType; } }
|
||||
public virtual PublishedContentType ContentType { get { return Content.ContentType; } }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -67,102 +67,102 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
public virtual int Id
|
||||
{
|
||||
get { return WrappedContentInternal.Id; }
|
||||
get { return Content.Id; }
|
||||
}
|
||||
|
||||
public virtual int TemplateId
|
||||
{
|
||||
get { return WrappedContentInternal.TemplateId; }
|
||||
get { return Content.TemplateId; }
|
||||
}
|
||||
|
||||
public virtual int SortOrder
|
||||
{
|
||||
get { return WrappedContentInternal.SortOrder; }
|
||||
get { return Content.SortOrder; }
|
||||
}
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get { return WrappedContentInternal.Name; }
|
||||
get { return Content.Name; }
|
||||
}
|
||||
|
||||
public virtual string UrlName
|
||||
{
|
||||
get { return WrappedContentInternal.UrlName; }
|
||||
get { return Content.UrlName; }
|
||||
}
|
||||
|
||||
public virtual string DocumentTypeAlias
|
||||
{
|
||||
get { return WrappedContentInternal.DocumentTypeAlias; }
|
||||
get { return Content.DocumentTypeAlias; }
|
||||
}
|
||||
|
||||
public virtual int DocumentTypeId
|
||||
{
|
||||
get { return WrappedContentInternal.DocumentTypeId; }
|
||||
get { return Content.DocumentTypeId; }
|
||||
}
|
||||
|
||||
public virtual string WriterName
|
||||
{
|
||||
get { return WrappedContentInternal.WriterName; }
|
||||
get { return Content.WriterName; }
|
||||
}
|
||||
|
||||
public virtual string CreatorName
|
||||
{
|
||||
get { return WrappedContentInternal.CreatorName; }
|
||||
get { return Content.CreatorName; }
|
||||
}
|
||||
|
||||
public virtual int WriterId
|
||||
{
|
||||
get { return WrappedContentInternal.WriterId; }
|
||||
get { return Content.WriterId; }
|
||||
}
|
||||
|
||||
public virtual int CreatorId
|
||||
{
|
||||
get { return WrappedContentInternal.CreatorId; }
|
||||
get { return Content.CreatorId; }
|
||||
}
|
||||
|
||||
public virtual string Path
|
||||
{
|
||||
get { return WrappedContentInternal.Path; }
|
||||
get { return Content.Path; }
|
||||
}
|
||||
|
||||
public virtual DateTime CreateDate
|
||||
{
|
||||
get { return WrappedContentInternal.CreateDate; }
|
||||
get { return Content.CreateDate; }
|
||||
}
|
||||
|
||||
public virtual DateTime UpdateDate
|
||||
{
|
||||
get { return WrappedContentInternal.UpdateDate; }
|
||||
get { return Content.UpdateDate; }
|
||||
}
|
||||
|
||||
public virtual Guid Version
|
||||
{
|
||||
get { return WrappedContentInternal.Version; }
|
||||
get { return Content.Version; }
|
||||
}
|
||||
|
||||
public virtual int Level
|
||||
{
|
||||
get { return WrappedContentInternal.Level; }
|
||||
get { return Content.Level; }
|
||||
}
|
||||
|
||||
public virtual string Url
|
||||
{
|
||||
get { return WrappedContentInternal.Url; }
|
||||
get { return Content.Url; }
|
||||
}
|
||||
|
||||
public virtual PublishedItemType ItemType
|
||||
{
|
||||
get { return WrappedContentInternal.ItemType; }
|
||||
get { return Content.ItemType; }
|
||||
}
|
||||
|
||||
public virtual bool IsDraft
|
||||
{
|
||||
get { return WrappedContentInternal.IsDraft; }
|
||||
get { return Content.IsDraft; }
|
||||
}
|
||||
|
||||
public virtual int GetIndex()
|
||||
{
|
||||
return WrappedContentInternal.GetIndex();
|
||||
return Content.GetIndex();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -171,12 +171,12 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
public virtual IPublishedContent Parent
|
||||
{
|
||||
get { return WrappedContentInternal.Parent; }
|
||||
get { return Content.Parent; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> Children
|
||||
{
|
||||
get { return WrappedContentInternal.Children; }
|
||||
get { return Content.Children; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -185,22 +185,22 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
public virtual ICollection<IPublishedProperty> Properties
|
||||
{
|
||||
get { return WrappedContentInternal.Properties; }
|
||||
get { return Content.Properties; }
|
||||
}
|
||||
|
||||
public virtual object this[string alias]
|
||||
{
|
||||
get { return WrappedContentInternal[alias]; }
|
||||
get { return Content[alias]; }
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias)
|
||||
{
|
||||
return WrappedContentInternal.GetProperty(alias);
|
||||
return Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
|
||||
{
|
||||
return WrappedContentInternal.GetProperty(alias, recurse);
|
||||
return Content.GetProperty(alias, recurse);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -101,6 +101,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
/// </summary>
|
||||
[Column("avatar")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(500)]
|
||||
public string Avatar { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
|
||||
@@ -51,10 +51,12 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("startContentId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[ForeignKey(typeof(NodeDto), Name = "FK_startContentId_umbracoNode_id")]
|
||||
public int? StartContentId { get; set; }
|
||||
|
||||
[Column("startMediaId")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[ForeignKey(typeof(NodeDto), Name = "FK_startMediaId_umbracoNode_id")]
|
||||
public int? StartMediaId { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace Umbraco.Core.Models
|
||||
private string _contentTypeIcon;
|
||||
private string _contentTypeThumbnail;
|
||||
|
||||
public static readonly UmbracoEntity Root = new UmbracoEntity(false) {Path = "-1", Name = "root", HasChildren = true};
|
||||
|
||||
public UmbracoEntity()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
|
||||
@@ -193,6 +193,14 @@ namespace Umbraco.Core.Models
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentBlueprint, typeof(IContent))]
|
||||
[FriendlyName("DocumentBlueprint")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)]
|
||||
DocumentBlueprint
|
||||
DocumentBlueprint,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved Identifier
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.IdReservation)]
|
||||
[FriendlyName("Identifier Reservation")]
|
||||
IdReservation
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -13,6 +15,17 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class UserExtensions
|
||||
{
|
||||
public static IEnumerable<string> GetPermissions(this IUser user, string path, IUserService userService)
|
||||
{
|
||||
return userService.GetPermissionsForPath(user, path).GetAllPermissions();
|
||||
}
|
||||
|
||||
public static bool HasSectionAccess(this IUser user, string app)
|
||||
{
|
||||
var apps = user.AllowedSections;
|
||||
return apps.Any(uApp => uApp.InvariantEquals(app));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
|
||||
/// </summary>
|
||||
@@ -109,7 +122,7 @@ namespace Umbraco.Core.Models
|
||||
catch (CultureNotFoundException)
|
||||
{
|
||||
//return the default one
|
||||
return CultureInfo.GetCultureInfo("en");
|
||||
return CultureInfo.GetCultureInfo(GlobalSettings.DefaultUILanguage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +156,19 @@ namespace Umbraco.Core.Models
|
||||
return HasPathAccess(media.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
|
||||
}
|
||||
|
||||
internal static bool HasPathAccess(this IUser user, IUmbracoEntity entity, IEntityService entityService, int recycleBinId)
|
||||
{
|
||||
switch (recycleBinId)
|
||||
{
|
||||
case Constants.System.RecycleBinMedia:
|
||||
return HasPathAccess(entity.Path, user.CalculateMediaStartNodeIds(entityService), recycleBinId);
|
||||
case Constants.System.RecycleBinContent:
|
||||
return HasPathAccess(entity.Path, user.CalculateContentStartNodeIds(entityService), recycleBinId);
|
||||
default:
|
||||
throw new NotSupportedException("Path access is only determined on content or media");
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool HasPathAccess(string path, int[] startNodeIds, int recycleBinId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", "path");
|
||||
@@ -155,16 +181,73 @@ namespace Umbraco.Core.Models
|
||||
if (startNodeIds.Contains(Constants.System.Root))
|
||||
return true;
|
||||
|
||||
var formattedPath = "," + path + ",";
|
||||
var formattedPath = string.Concat(",", path, ",");
|
||||
|
||||
// only users with root access have access to the recycle bin,
|
||||
// if the above check didn't pass then access is denied
|
||||
if (formattedPath.Contains("," + recycleBinId + ","))
|
||||
if (formattedPath.Contains(string.Concat(",", recycleBinId, ",")))
|
||||
return false;
|
||||
|
||||
// check for a start node in the path
|
||||
return startNodeIds.Any(x => formattedPath.Contains("," + x + ","));
|
||||
return startNodeIds.Any(x => formattedPath.Contains(string.Concat(",", x, ",")));
|
||||
}
|
||||
|
||||
internal static bool IsInBranchOfStartNode(this IUser user, IUmbracoEntity entity, IEntityService entityService, int recycleBinId, out bool hasPathAccess)
|
||||
{
|
||||
switch (recycleBinId)
|
||||
{
|
||||
case Constants.System.RecycleBinMedia:
|
||||
return IsInBranchOfStartNode(entity.Path, user.CalculateMediaStartNodeIds(entityService), user.GetMediaStartNodePaths(entityService), out hasPathAccess);
|
||||
case Constants.System.RecycleBinContent:
|
||||
return IsInBranchOfStartNode(entity.Path, user.CalculateContentStartNodeIds(entityService), user.GetContentStartNodePaths(entityService), out hasPathAccess);
|
||||
default:
|
||||
throw new NotSupportedException("Path access is only determined on content or media");
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsInBranchOfStartNode(string path, int[] startNodeIds, string[] startNodePaths, out bool hasPathAccess)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", "path");
|
||||
|
||||
hasPathAccess = false;
|
||||
|
||||
// check for no access
|
||||
if (startNodeIds.Length == 0)
|
||||
return false;
|
||||
|
||||
// check for root access
|
||||
if (startNodeIds.Contains(Constants.System.Root))
|
||||
{
|
||||
hasPathAccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//is it self?
|
||||
var self = startNodePaths.Any(x => x == path);
|
||||
if (self)
|
||||
{
|
||||
hasPathAccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//is it ancestor?
|
||||
var ancestor = startNodePaths.Any(x => x.StartsWith(path));
|
||||
if (ancestor)
|
||||
{
|
||||
hasPathAccess = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//is it descendant?
|
||||
var descendant = startNodePaths.Any(x => path.StartsWith(x));
|
||||
if (descendant)
|
||||
{
|
||||
hasPathAccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this user is an admin.
|
||||
@@ -184,7 +267,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
const string cacheKey = "AllContentStartNodes";
|
||||
//try to look them up from cache so we don't recalculate
|
||||
var valuesInUserCache = FromUserCache(user, cacheKey);
|
||||
var valuesInUserCache = FromUserCache<int[]>(user, cacheKey);
|
||||
if (valuesInUserCache != null) return valuesInUserCache;
|
||||
|
||||
var gsn = user.Groups.Where(x => x.StartContentId.HasValue).Select(x => x.StartContentId.Value).Distinct().ToArray();
|
||||
@@ -199,7 +282,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
const string cacheKey = "AllMediaStartNodes";
|
||||
//try to look them up from cache so we don't recalculate
|
||||
var valuesInUserCache = FromUserCache(user, cacheKey);
|
||||
var valuesInUserCache = FromUserCache<int[]>(user, cacheKey);
|
||||
if (valuesInUserCache != null) return valuesInUserCache;
|
||||
|
||||
var gsn = user.Groups.Where(x => x.StartMediaId.HasValue).Select(x => x.StartMediaId.Value).Distinct().ToArray();
|
||||
@@ -207,9 +290,36 @@ namespace Umbraco.Core.Models
|
||||
var vals = CombineStartNodes(UmbracoObjectTypes.Media, gsn, usn, entityService);
|
||||
ToUserCache(user, cacheKey, vals);
|
||||
return vals;
|
||||
}
|
||||
|
||||
public static string[] GetMediaStartNodePaths(this IUser user, IEntityService entityService)
|
||||
{
|
||||
const string cacheKey = "MediaStartNodePaths";
|
||||
//try to look them up from cache so we don't recalculate
|
||||
var valuesInUserCache = FromUserCache<string[]>(user, cacheKey);
|
||||
if (valuesInUserCache != null) return valuesInUserCache;
|
||||
|
||||
var startNodeIds = user.CalculateMediaStartNodeIds(entityService);
|
||||
var vals = entityService.GetAllPaths(UmbracoObjectTypes.Media, startNodeIds).Select(x => x.Path).ToArray();
|
||||
ToUserCache(user, cacheKey, vals);
|
||||
return vals;
|
||||
}
|
||||
|
||||
public static string[] GetContentStartNodePaths(this IUser user, IEntityService entityService)
|
||||
{
|
||||
const string cacheKey = "ContentStartNodePaths";
|
||||
//try to look them up from cache so we don't recalculate
|
||||
var valuesInUserCache = FromUserCache<string[]>(user, cacheKey);
|
||||
if (valuesInUserCache != null) return valuesInUserCache;
|
||||
|
||||
var startNodeIds = user.CalculateContentStartNodeIds(entityService);
|
||||
var vals = entityService.GetAllPaths(UmbracoObjectTypes.Document, startNodeIds).Select(x => x.Path).ToArray();
|
||||
ToUserCache(user, cacheKey, vals);
|
||||
return vals;
|
||||
}
|
||||
|
||||
private static int[] FromUserCache(IUser user, string cacheKey)
|
||||
private static T FromUserCache<T>(IUser user, string cacheKey)
|
||||
where T: class
|
||||
{
|
||||
var entityUser = user as User;
|
||||
if (entityUser == null) return null;
|
||||
@@ -218,12 +328,13 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
object allContentStartNodes;
|
||||
return entityUser.AdditionalData.TryGetValue(cacheKey, out allContentStartNodes)
|
||||
? allContentStartNodes as int[]
|
||||
? allContentStartNodes as T
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToUserCache(IUser user, string cacheKey, int[] vals)
|
||||
private static void ToUserCache<T>(IUser user, string cacheKey, T vals)
|
||||
where T: class
|
||||
{
|
||||
var entityUser = user as User;
|
||||
if (entityUser == null) return;
|
||||
@@ -282,9 +393,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
var usn = new List<int>();
|
||||
foreach (var sn in userSn)
|
||||
{
|
||||
if (groupSn.Contains(sn)) continue; // ignore, already there
|
||||
|
||||
{
|
||||
string snp;
|
||||
if (paths.TryGetValue(sn, out snp) == false) continue; // ignore rogue node (no path)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
private void CreateUmbracoUserData()
|
||||
{
|
||||
_database.Insert("umbracoUser", "id", false, new UserDto { Id = 0, Disabled = false, NoConsole = false, UserName = "Administrator", Login = "admin", Password = "default", Email = "", UserLanguage = "en", CreateDate = DateTime.Now, UpdateDate = DateTime.Now });
|
||||
_database.Insert("umbracoUser", "id", false, new UserDto { Id = 0, Disabled = false, NoConsole = false, UserName = "Administrator", Login = "admin", Password = "default", Email = "", UserLanguage = "en-US", CreateDate = DateTime.Now, UpdateDate = DateTime.Now });
|
||||
}
|
||||
|
||||
private void CreateUmbracoUserGroupData()
|
||||
@@ -177,7 +177,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 1, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.AdminGroupAlias, Name = "Administrators", DefaultPermissions = "CADMOSKTPIURZ:5F7ï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-medal" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = "translator", Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoUser2UserGroupData()
|
||||
@@ -188,14 +188,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
private void CreateUmbracoUserGroup2AppData()
|
||||
{
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Content });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Media });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Settings });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Users });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Members });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 2, AppAlias = Constants.Applications.Content });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Settings });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Users });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Forms });
|
||||
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 2, AppAlias = Constants.Applications.Content });
|
||||
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Content });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Media });
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Forms });
|
||||
|
||||
_database.Insert(new UserGroup2AppDto { UserGroupId = 4, AppAlias = Constants.Applications.Translation });
|
||||
}
|
||||
|
||||
|
||||
+200
-20
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -12,14 +13,29 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
[Migration("7.7.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddUserGroupTables : MigrationBase
|
||||
{
|
||||
private readonly string _collateSyntax;
|
||||
|
||||
public AddUserGroupTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
{
|
||||
//For some of the migration data inserts we require to use a special MSSQL collate expression since
|
||||
//some databases may have a custom collation specified and if that is the case, when we compare strings
|
||||
//in dynamic SQL it will try to compare strings in different collations and this will yield errors.
|
||||
_collateSyntax = (sqlSyntax is MySqlSyntaxProvider || sqlSyntax is SqlCeSyntaxProvider)
|
||||
? string.Empty
|
||||
: "COLLATE DATABASE_DEFAULT";
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToList();
|
||||
var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
|
||||
|
||||
//In some very rare cases, there might alraedy be user group tables that we'll need to remove first
|
||||
//but of course we don't want to remove the tables we will be creating below if they already exist so
|
||||
//need to do some checks first since these old rare tables have a different schema
|
||||
RemoveOldTablesIfExist(tables, columns);
|
||||
|
||||
if (AddNewTables(tables))
|
||||
{
|
||||
@@ -28,6 +44,83 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
DeleteOldTables(tables, constraints);
|
||||
SetDefaultIcons();
|
||||
}
|
||||
else
|
||||
{
|
||||
//if we aren't adding the tables, make sure that the umbracoUserGroup table has the correct FKs - these
|
||||
//were added after the beta release so we need to do some cleanup
|
||||
//if the FK doesn't exist
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUserGroup")
|
||||
&& x.Item2.InvariantEquals("startContentId")
|
||||
&& x.Item3.InvariantEquals("FK_startContentId_umbracoNode_id")) == false)
|
||||
{
|
||||
//before we add any foreign key we need to make sure there's no stale data in there which would have happened in the beta
|
||||
//release if a start node was assigned and then that start node was deleted.
|
||||
Execute.Sql(@"UPDATE umbracoUserGroup SET startContentId = NULL WHERE startContentId NOT IN (SELECT id FROM umbracoNode)");
|
||||
|
||||
Create.ForeignKey("FK_startContentId_umbracoNode_id")
|
||||
.FromTable("umbracoUserGroup")
|
||||
.ForeignColumn("startContentId")
|
||||
.ToTable("umbracoNode")
|
||||
.PrimaryColumn("id")
|
||||
.OnDelete(Rule.None)
|
||||
.OnUpdate(Rule.None);
|
||||
}
|
||||
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUserGroup")
|
||||
&& x.Item2.InvariantEquals("startMediaId")
|
||||
&& x.Item3.InvariantEquals("FK_startMediaId_umbracoNode_id")) == false)
|
||||
{
|
||||
//before we add any foreign key we need to make sure there's no stale data in there which would have happened in the beta
|
||||
//release if a start node was assigned and then that start node was deleted.
|
||||
Execute.Sql(@"UPDATE umbracoUserGroup SET startMediaId = NULL WHERE startMediaId NOT IN (SELECT id FROM umbracoNode)");
|
||||
|
||||
Create.ForeignKey("FK_startMediaId_umbracoNode_id")
|
||||
.FromTable("umbracoUserGroup")
|
||||
.ForeignColumn("startMediaId")
|
||||
.ToTable("umbracoNode")
|
||||
.PrimaryColumn("id")
|
||||
.OnDelete(Rule.None)
|
||||
.OnUpdate(Rule.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In some very rare cases, there might alraedy be user group tables that we'll need to remove first
|
||||
/// but of course we don't want to remove the tables we will be creating below if they already exist so
|
||||
/// need to do some checks first since these old rare tables have a different schema
|
||||
/// </summary>
|
||||
/// <param name="tables"></param>
|
||||
/// <param name="columns"></param>
|
||||
private void RemoveOldTablesIfExist(List<string> tables, ColumnInfo[] columns)
|
||||
{
|
||||
if (tables.Contains("umbracoUser2userGroup", StringComparer.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//this column doesn't exist in the 7.7 schema, so if it's there, then this is a super old table
|
||||
var foundOldColumn = columns
|
||||
.FirstOrDefault(x =>
|
||||
x.ColumnName.Equals("user", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& x.TableName.Equals("umbracoUser2userGroup", StringComparison.InvariantCultureIgnoreCase));
|
||||
if (foundOldColumn != null)
|
||||
{
|
||||
Delete.Table("umbracoUser2userGroup");
|
||||
//remove from the tables list since this will be re-checked in further logic
|
||||
tables.Remove("umbracoUser2userGroup");
|
||||
}
|
||||
}
|
||||
|
||||
if (tables.Contains("umbracoUserGroup", StringComparer.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//The new schema has several columns, the super old one for this table only had 2 so if it's 2 get rid of it
|
||||
var countOfCols = columns
|
||||
.Count(x => x.TableName.Equals("umbracoUserGroup", StringComparison.InvariantCultureIgnoreCase));
|
||||
if (countOfCols == 2)
|
||||
{
|
||||
Delete.Table("umbracoUserGroup");
|
||||
//remove from the tables list since this will be re-checked in further logic
|
||||
tables.Remove("umbracoUserGroup");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDefaultIcons()
|
||||
@@ -38,7 +131,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
Execute.Sql(string.Format("UPDATE umbracoUserGroup SET icon = '{0}' WHERE userGroupAlias = '{1}'", "icon-globe", "translator"));
|
||||
}
|
||||
|
||||
private bool AddNewTables(string[] tables)
|
||||
private bool AddNewTables(List<string> tables)
|
||||
{
|
||||
var updated = false;
|
||||
if (tables.InvariantContains("umbracoUserGroup") == false)
|
||||
@@ -78,13 +171,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
FROM umbracoUserType");
|
||||
|
||||
// Add each user to the group created from their type
|
||||
Execute.Sql(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
|
||||
SELECT u.id, ug.id
|
||||
FROM umbracoUser u
|
||||
INNER JOIN umbracoUserType ut ON ut.id = u.userType
|
||||
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias = ut.userTypeAlias");
|
||||
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias {0} = ut.userTypeAlias {0}", _collateSyntax));
|
||||
|
||||
// Add the built-in administrator account to all apps
|
||||
// this will lookup all of the apps that the admin currently has access to in order to assign the sections
|
||||
// instead of use statically assigning since there could be extra sections we don't know about.
|
||||
Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app)
|
||||
SELECT ug.id, app
|
||||
FROM umbracoUserGroup ug
|
||||
@@ -93,6 +188,89 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
INNER JOIN umbracoUser2app u2a ON u2a." + SqlSyntax.GetQuotedColumnName("user") + @" = u.id
|
||||
WHERE u.id = 0");
|
||||
|
||||
// Add the default section access to the other built-in accounts
|
||||
// writer:
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId, app)
|
||||
SELECT ug.id, 'content' as app
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = 'writer' {0}", _collateSyntax));
|
||||
// editor
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId, app)
|
||||
SELECT ug.id, 'content' as app
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = 'editor' {0}", _collateSyntax));
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId, app)
|
||||
SELECT ug.id, 'media' as app
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = 'editor' {0}", _collateSyntax));
|
||||
// translator
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId, app)
|
||||
SELECT ug.id, 'translation' as app
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = 'translator' {0}", _collateSyntax));
|
||||
|
||||
//We need to lookup all distinct combinations of section access and create a group for each distinct collection
|
||||
//and assign groups accordingly. We'll perform the lookup 'now' to then create the queued SQL migrations.
|
||||
var userAppsData = Context.Database.Query<dynamic>(@"SELECT u.id, u2a.app FROM umbracoUser u
|
||||
INNER JOIN umbracoUser2app u2a ON u2a." + SqlSyntax.GetQuotedColumnName("user") + @" = u.id
|
||||
ORDER BY u.id, u2a.app");
|
||||
var usersWithApps = new Dictionary<int, List<string>>();
|
||||
foreach (var userApps in userAppsData)
|
||||
{
|
||||
List<string> apps;
|
||||
if (usersWithApps.TryGetValue(userApps.id, out apps) == false)
|
||||
{
|
||||
apps = new List<string> {userApps.app};
|
||||
usersWithApps.Add(userApps.id, apps);
|
||||
}
|
||||
else
|
||||
{
|
||||
apps.Add(userApps.app);
|
||||
}
|
||||
}
|
||||
//At this stage we have a dictionary of users with a collection of their apps which are sorted
|
||||
//and we need to determine the unique/distinct app collections for each user to create groups with.
|
||||
//We can do this by creating a hash value of all of the app values and since they are already sorted we can get a distinct
|
||||
//collection by this hash.
|
||||
var distinctApps = usersWithApps
|
||||
.Select(x => new {appCollection = x.Value, appsHash = string.Join("", x.Value).GenerateHash()})
|
||||
.DistinctBy(x => x.appsHash)
|
||||
.ToArray();
|
||||
//Now we need to create user groups for each of these distinct app collections, and then assign the corresponding users to those groups
|
||||
for (var i = 0; i < distinctApps.Length; i++)
|
||||
{
|
||||
//create the group
|
||||
var alias = "MigratedSectionAccessGroup_" + (i + 1);
|
||||
Insert.IntoTable("umbracoUserGroup").Row(new
|
||||
{
|
||||
userGroupAlias = "MigratedSectionAccessGroup_" + (i + 1),
|
||||
userGroupName = "Migrated Section Access Group " + (i + 1)
|
||||
});
|
||||
//now assign the apps
|
||||
var distinctApp = distinctApps[i];
|
||||
foreach (var app in distinctApp.appCollection)
|
||||
{
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId, app)
|
||||
SELECT ug.id, '" + app + @"' as app
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = '" + alias + "' {0}", _collateSyntax));
|
||||
}
|
||||
//now assign the corresponding users to this group
|
||||
foreach (var userWithApps in usersWithApps)
|
||||
{
|
||||
//check if this user's groups hash matches the current groups hash
|
||||
var hash = string.Join("", userWithApps.Value).GenerateHash();
|
||||
if (hash == distinctApp.appsHash)
|
||||
{
|
||||
//it matches so assign the user to this group
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
|
||||
SELECT " + userWithApps.Key + @", ug.id
|
||||
FROM umbracoUserGroup ug
|
||||
WHERE ug.userGroupAlias {0} = '" + alias + "' {0}", _collateSyntax));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rename some groups for consistency (plural form)
|
||||
Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Writers' WHERE userGroupAlias = 'writer'");
|
||||
Execute.Sql("UPDATE umbracoUserGroup SET userGroupName = 'Translators' WHERE userGroupAlias = 'translator'");
|
||||
@@ -112,49 +290,46 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
{
|
||||
// Create user group records for all non-admin users that have specific permissions set
|
||||
Execute.Sql(@"INSERT INTO umbracoUserGroup(userGroupAlias, userGroupName)
|
||||
SELECT userName + 'Group', 'Group for ' + userName
|
||||
SELECT 'permissionGroupFor' + userLogin, 'Migrated Permission Group for ' + userLogin
|
||||
FROM umbracoUser
|
||||
WHERE (id IN (
|
||||
SELECT " + SqlSyntax.GetQuotedColumnName("user") + @"
|
||||
FROM umbracoUser2app
|
||||
) OR id IN (
|
||||
SELECT userid
|
||||
FROM umbracoUser2NodePermission
|
||||
))
|
||||
AND id > 0");
|
||||
|
||||
// Associate those groups with the users
|
||||
Execute.Sql(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUser2UserGroup (userId, userGroupId)
|
||||
SELECT u.id, ug.id
|
||||
FROM umbracoUser u
|
||||
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias = userName + 'Group'");
|
||||
INNER JOIN umbracoUserGroup ug ON ug.userGroupAlias {0} = 'permissionGroupFor' + userLogin {0}", _collateSyntax));
|
||||
|
||||
// Create node permissions on the groups
|
||||
Execute.Sql(@"INSERT INTO umbracoUserGroup2NodePermission (userGroupId,nodeId,permission)
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2NodePermission (userGroupId,nodeId,permission)
|
||||
SELECT ug.id, nodeId, permission
|
||||
FROM umbracoUserGroup ug
|
||||
INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id
|
||||
INNER JOIN umbracoUser u ON u.id = u2ug.userId
|
||||
INNER JOIN umbracoUser2NodePermission u2np ON u2np.userId = u.id
|
||||
WHERE ug.userGroupAlias NOT IN (
|
||||
SELECT userTypeAlias
|
||||
WHERE ug.userGroupAlias {0} NOT IN (
|
||||
SELECT userTypeAlias {0}
|
||||
FROM umbracoUserType
|
||||
)");
|
||||
)", _collateSyntax));
|
||||
|
||||
// Create app permissions on the groups
|
||||
Execute.Sql(@"INSERT INTO umbracoUserGroup2app (userGroupId,app)
|
||||
Execute.Sql(string.Format(@"INSERT INTO umbracoUserGroup2app (userGroupId,app)
|
||||
SELECT ug.id, app
|
||||
FROM umbracoUserGroup ug
|
||||
INNER JOIN umbracoUser2UserGroup u2ug ON u2ug.userGroupId = ug.id
|
||||
INNER JOIN umbracoUser u ON u.id = u2ug.userId
|
||||
INNER JOIN umbracoUser2app u2a ON u2a." + SqlSyntax.GetQuotedColumnName("user") + @" = u.id
|
||||
WHERE ug.userGroupAlias NOT IN (
|
||||
SELECT userTypeAlias
|
||||
WHERE ug.userGroupAlias {0} NOT IN (
|
||||
SELECT userTypeAlias {0}
|
||||
FROM umbracoUserType
|
||||
)");
|
||||
)", _collateSyntax));
|
||||
}
|
||||
|
||||
private void DeleteOldTables(string[] tables, Tuple<string, string, string>[] constraints)
|
||||
private void DeleteOldTables(List<string> tables, Tuple<string, string, string>[] constraints)
|
||||
{
|
||||
if (tables.InvariantContains("umbracoUser2App"))
|
||||
{
|
||||
@@ -172,6 +347,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser");
|
||||
}
|
||||
//This is the super old constraint name of the FK for user type so check this one too
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_user_userType")))
|
||||
{
|
||||
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser");
|
||||
}
|
||||
|
||||
Delete.Column("userType").FromTable("umbracoUser");
|
||||
Delete.Table("umbracoUserType");
|
||||
|
||||
+3
-1
@@ -32,6 +32,9 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("invitedDate")) == false)
|
||||
Create.Column("invitedDate").OnTable("umbracoUser").AsDateTime().Nullable();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("avatar")) == false)
|
||||
Create.Column("avatar").OnTable("umbracoUser").AsString(500).Nullable();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("passwordConfig")) == false)
|
||||
{
|
||||
Create.Column("passwordConfig").OnTable("umbracoUser").AsString(500).Nullable();
|
||||
@@ -43,7 +46,6 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
Execute.Sql("UPDATE umbracoUser SET passwordConfig = '" + json + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
|
||||
+6
-3
@@ -28,10 +28,13 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
//if it exists we need to drop it first
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")))
|
||||
//if an index exists on this table we need to drop it. Normally we'd check via index name but in some odd cases (i.e. Our)
|
||||
//the index name is something odd (starts with "mi_"). In any case, the index cannot exist if we want to alter the column
|
||||
//so we'll drop whatever index is there and add one with the correct name after.
|
||||
var loginNameIndex = dbIndexes.FirstOrDefault(x => x.TableName.InvariantEquals("cmsMember") && x.ColumnName.InvariantEquals("LoginName"));
|
||||
if (loginNameIndex != null)
|
||||
{
|
||||
localContext.Delete.Index("IX_cmsMember_LoginName").OnTable("cmsMember");
|
||||
localContext.Delete.Index(loginNameIndex.IndexName).OnTable("cmsMember");
|
||||
}
|
||||
|
||||
//we can apply the col length change
|
||||
|
||||
-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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private readonly ContentPreviewRepository<IContent> _contentPreviewRepository;
|
||||
private readonly ContentXmlRepository<IContent> _contentXmlRepository;
|
||||
private readonly PermissionRepository<IContent> _permissionRepository;
|
||||
private readonly ContentByGuidReadRepository _contentByGuidReadRepository;
|
||||
|
||||
public ContentRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection)
|
||||
: base(work, cacheHelper, logger, syntaxProvider, contentSection)
|
||||
@@ -44,7 +45,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IContent>(work, CacheHelper.NoCache, logger, syntaxProvider);
|
||||
_contentXmlRepository = new ContentXmlRepository<IContent>(work, CacheHelper.NoCache, logger, syntaxProvider);
|
||||
_permissionRepository = new PermissionRepository<IContent>(UnitOfWork, cacheHelper, Logger, SqlSyntax);
|
||||
|
||||
_contentByGuidReadRepository = new ContentByGuidReadRepository(this, work, cacheHelper, logger, syntaxProvider);
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
@@ -183,6 +184,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsTask WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoUserStartNode WHERE startNode = @Id",
|
||||
"UPDATE umbracoUserGroup SET startContentId = NULL WHERE startContentId = @Id",
|
||||
"DELETE FROM umbracoRelation WHERE parentId = @Id",
|
||||
"DELETE FROM umbracoRelation WHERE childId = @Id",
|
||||
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
|
||||
@@ -194,7 +197,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoAccess WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
}
|
||||
@@ -435,7 +438,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);
|
||||
@@ -938,6 +958,113 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read Repository implementation for GUID keys
|
||||
public IContent Get(Guid id)
|
||||
{
|
||||
return _contentByGuidReadRepository.Get(id);
|
||||
}
|
||||
|
||||
IEnumerable<IContent> IReadRepository<Guid, IContent>.GetAll(params Guid[] ids)
|
||||
{
|
||||
return _contentByGuidReadRepository.GetAll(ids);
|
||||
}
|
||||
|
||||
public bool Exists(Guid id)
|
||||
{
|
||||
return _contentByGuidReadRepository.Exists(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A reading repository purely for looking up by GUID
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: This is ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things!
|
||||
/// Then we can do the same thing with repository instances and we wouldn't need to leave all these methods as not implemented because we wouldn't need to implement them
|
||||
/// </remarks>
|
||||
private class ContentByGuidReadRepository : PetaPocoRepositoryBase<Guid, IContent>
|
||||
{
|
||||
private readonly ContentRepository _outerRepo;
|
||||
|
||||
public ContentByGuidReadRepository(ContentRepository outerRepo,
|
||||
IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_outerRepo = outerRepo;
|
||||
}
|
||||
|
||||
protected override IContent PerformGet(Guid id)
|
||||
{
|
||||
var sql = _outerRepo.GetBaseQuery(BaseQueryType.FullSingle)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
var content = _outerRepo.CreateContentFromDto(dto, sql);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContent> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
Func<Sql, Sql> translate = s =>
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
s.Where("umbracoNode.uniqueID in (@ids)", new { ids });
|
||||
}
|
||||
//we only want the newest ones with this method
|
||||
s.Where<DocumentDto>(x => x.Newest, SqlSyntax);
|
||||
return s;
|
||||
};
|
||||
|
||||
var sqlBaseFull = _outerRepo.GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
var sqlBaseIds = _outerRepo.GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
return _outerRepo.ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return _outerRepo.GetBaseQuery(isCount);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return "umbracoNode.uniqueID = @Id";
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { return _outerRepo.NodeObjectTypeId; }
|
||||
}
|
||||
|
||||
#region Not needed to implement
|
||||
|
||||
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override void PersistNewItem(IContent entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override void PersistUpdatedItem(IContent entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override string GetDatabaseFieldNameForOrderBy(string orderBy)
|
||||
{
|
||||
//Some custom ones
|
||||
@@ -1126,31 +1253,10 @@ 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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.Querying;
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
|
||||
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>, IDeleteMediaFilesRepository
|
||||
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>, IReadRepository<Guid, IContent>, IDeleteMediaFilesRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// This builds the Xml document used for the XML cache
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,8 @@ using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>, IRecycleBinRepository<IMedia>, IDeleteMediaFilesRepository
|
||||
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>, IRecycleBinRepository<IMedia>, IReadRepository<Guid, IMedia>, IDeleteMediaFilesRepository
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update published xml for the media item
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user