Compare commits
868 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e2598a3a3 | |||
| 19241995e8 | |||
| 3097a46ab3 | |||
| c63156fcd8 | |||
| 84a90884e3 | |||
| d29b7311fa | |||
| 17b59cc20f | |||
| 4423571eaa | |||
| 28eb4ff03a | |||
| 79517f171c | |||
| e76c42f874 | |||
| 151c35bc47 | |||
| 84265a9716 | |||
| 6618388973 | |||
| b280bf73aa | |||
| a6d57a5f6d | |||
| 6b7a83c6c9 | |||
| eb5f2e29a3 | |||
| cf86409e3f | |||
| 134f5e2601 | |||
| 37ac524e8e | |||
| 43af2f8e55 | |||
| a20584e3de | |||
| 19ec26fef9 | |||
| 527f98189a | |||
| 78c289a559 | |||
| 646abb3aea | |||
| 2af59471a2 | |||
| b76b09026b | |||
| 090f89e5b6 | |||
| 4870afd2d3 | |||
| 80257b6684 | |||
| 8f3a0b6919 | |||
| 9fd2f34343 | |||
| e774717346 | |||
| 5f5759c8a3 | |||
| 0b90097aad | |||
| 7cd540c37e | |||
| 83595ecaf5 | |||
| e07c5e6ff8 | |||
| 67ff827d67 | |||
| 3f7159797f | |||
| 6f81b7fe24 | |||
| f2819f61e5 | |||
| 2107e9d9e3 | |||
| c623b448ba | |||
| 8bb49f57e8 | |||
| da0b1c2f23 | |||
| a288fc62eb | |||
| 82e50ccd3a | |||
| 15b0dc9f29 | |||
| 8057d09fc2 | |||
| 8877100bf0 | |||
| 41c96e6e26 | |||
| abdd117264 | |||
| 6272dac698 | |||
| 7a4b5fa796 | |||
| c962132ee0 | |||
| 0780979469 | |||
| a42e568fdf | |||
| e62661cc6a | |||
| 6841b2ac61 | |||
| f7f4276485 | |||
| bb9e23ca3e | |||
| 10db9363f4 | |||
| 7d5d69e2e3 | |||
| 06897aa8c6 | |||
| ee75158dd0 | |||
| c022397d86 | |||
| e25f6e82ed | |||
| d104525a50 | |||
| b0a64aa4b5 | |||
| ca86385269 | |||
| fb8368267e | |||
| ef4322a9b3 | |||
| 7032e137b8 | |||
| e99643ecfd | |||
| a325529eb8 | |||
| c68f832be6 | |||
| 4ab1c51327 | |||
| 6f39bac32a | |||
| e007a37c36 | |||
| ab9e029525 | |||
| d298388a7b | |||
| 4463abe9bb | |||
| d1db38182f | |||
| e808360653 | |||
| c5df712471 | |||
| 2f67008987 | |||
| 1672e284c5 | |||
| 60ce36a7f2 | |||
| 05c12d1f27 | |||
| 72e2cacc87 | |||
| 4285102422 | |||
| 5ffdf4dd60 | |||
| abbcf0e26b | |||
| 347b25472f | |||
| b745948dc6 | |||
| 8007c8869f | |||
| b51a328ead | |||
| f61437cc7c | |||
| f3b8f2eb5f | |||
| de4628f450 | |||
| b0a64655b4 | |||
| 48d88ec8fa | |||
| 7ca151a3fa | |||
| 363c8fab2c | |||
| 8d4fd9d1cf | |||
| 24071eb3b1 | |||
| 62ffbe7cf0 | |||
| f2d6b47e47 | |||
| f3541908c7 | |||
| 4e57e1cc18 | |||
| 410afffaa9 | |||
| 936e94789d | |||
| 468a56e5de | |||
| 468ec8824a | |||
| cad913b7a1 | |||
| 382103d99d | |||
| cbb6b9bca0 | |||
| a1b90a50cd | |||
| 9ea5855073 | |||
| 4a37433042 | |||
| 91469812d3 | |||
| 68a8eb7f3f | |||
| 2e7baabb94 | |||
| 57745fe71d | |||
| de6f9236e7 | |||
| a00c59e8b5 | |||
| 05c2da807f | |||
| 0fd8ca43d6 | |||
| 87f09b2ee6 | |||
| e466878925 | |||
| 7adf917570 | |||
| 7c41db4bcc | |||
| 5caaef7795 | |||
| 3b72945b1f | |||
| 71eb22d0e8 | |||
| 9d780a2e8f | |||
| 55b00069cb | |||
| b304728f4e | |||
| 319acc199e | |||
| 2a982f21a7 | |||
| 3ff455f11d | |||
| 41ad1bf27b | |||
| f49d939a2d | |||
| c7e80fc532 | |||
| b47d691841 | |||
| 7e1e83b493 | |||
| f6971185f8 | |||
| 244429b972 | |||
| 80ceba36dc | |||
| d33b617851 | |||
| 2b5f370c92 | |||
| 4a0cf81d7d | |||
| fef743517f | |||
| b3cbcebf5b | |||
| 3d90c2b83f | |||
| 3b8b5b6dfa | |||
| 85f95cf067 | |||
| 23be0134e7 | |||
| 355fb64d8b | |||
| 42959a1db5 | |||
| 5b754b3958 | |||
| cba31986fe | |||
| 07b0267668 | |||
| a83291cc6e | |||
| a1cd22208b | |||
| 59d75277c2 | |||
| 69e193e887 | |||
| c3116d5df7 | |||
| b0b874212e | |||
| 19086ab3a1 | |||
| 069594c3ac | |||
| 0bc4c7e2aa | |||
| 7f12cff3bd | |||
| 56adab43d6 | |||
| 83294b6594 | |||
| e0c89eb1ea | |||
| 9ad8e6a876 | |||
| 14b58386e5 | |||
| e26cbccfe0 | |||
| 7dc06c1bea | |||
| effa984bdc | |||
| fa058f348d | |||
| 62d9a1a132 | |||
| 9284269428 | |||
| f8c4481eb1 | |||
| d7f2041ee9 | |||
| 9f6e6e4701 | |||
| 34ba3f3351 | |||
| dcfb8b94d8 | |||
| 0876004d9a | |||
| d3ad61835a | |||
| ad882ab5be | |||
| 774f333c70 | |||
| 700b71f9fb | |||
| bba58ea0c0 | |||
| d0d4885812 | |||
| ce11a377e6 | |||
| c6443c477f | |||
| eb94572361 | |||
| fd8317493e | |||
| 3642ff72a4 | |||
| 6e830c1171 | |||
| 22ff1bcaa0 | |||
| ab523baf4f | |||
| 2dec03f441 | |||
| 9ada0d9c97 | |||
| c3be9004f6 | |||
| 11fa015b64 | |||
| df553140e8 | |||
| 0b898f2e32 | |||
| 4c9be2cc49 | |||
| 1aad4e50d5 | |||
| f5f3676a56 | |||
| f69f68e2b5 | |||
| 3858656015 | |||
| a5ec5f46ac | |||
| ea70e18a91 | |||
| 0cfd5773c3 | |||
| b15def99c7 | |||
| 8c8b6984c2 | |||
| bc828fc6f3 | |||
| dc7b83ca6c | |||
| 8188e4ff55 | |||
| fcf9dcd803 | |||
| 070fa11f8c | |||
| f52a84937b | |||
| 7e9f55f517 | |||
| 3556043b55 | |||
| 3e8fb11abd | |||
| 5181988a41 | |||
| 5169310258 | |||
| f987640462 | |||
| 55e97dad74 | |||
| 8c70578764 | |||
| 5cb2dd0036 | |||
| a9713dc6fc | |||
| b32e7d4ccc | |||
| 8d4958e395 | |||
| d2f3e162f1 | |||
| 9883757704 | |||
| 7404e20a19 | |||
| 0310782d7d | |||
| 744ff0cf33 | |||
| afd0018dd6 | |||
| c36aec624b | |||
| c81e14758d | |||
| 1ba86f361a | |||
| 9b33c94437 | |||
| 2a30fd238e | |||
| 3cb4128c0f | |||
| 79958e0fe0 | |||
| f238f9fcea | |||
| 7c545410cc | |||
| e5a21625ea | |||
| dcf5fff3f0 | |||
| 1dc88658e8 | |||
| efd3d6937a | |||
| e17f75a18a | |||
| 6d46c2204d | |||
| 27b9385df5 | |||
| 88c20c7c5f | |||
| bd9adffaa5 | |||
| ea6d81f7ac | |||
| 0ce059d5bd | |||
| 773ef1d168 | |||
| 1f7de81cd2 | |||
| fef9e3fde0 | |||
| e849d2d0d0 | |||
| 9a6123579f | |||
| f56bd731b7 | |||
| bee0642417 | |||
| 43abb08e59 | |||
| 17db5709f1 | |||
| 2fc6d9b7df | |||
| 591a881283 | |||
| e4561851a4 | |||
| c6daf2e111 | |||
| 1554d52ea9 | |||
| 883366f741 | |||
| cac331adbc | |||
| 18318ecabf | |||
| 6b9ef6df7a | |||
| fcaf78ef29 | |||
| 833b4c552c | |||
| c678b7f6d9 | |||
| e320840a29 | |||
| b664fc5e94 | |||
| b87346bee5 | |||
| 6029306fe9 | |||
| f979792290 | |||
| 5f82fc6e20 | |||
| b1a0ca98ea | |||
| f4f99c2922 | |||
| 6f55b84c65 | |||
| 43f369f96f | |||
| 60924d93fb | |||
| c1b3da869d | |||
| e39158d5e5 | |||
| aed476de0a | |||
| ddd9c8ef7d | |||
| 9e2c7d16a6 | |||
| 44db4e7a48 | |||
| 680d97c630 | |||
| 89347b359b | |||
| 8053cea92d | |||
| f44b158c15 | |||
| 1ff66d0c3a | |||
| ae07f34215 | |||
| aba2c2a79e | |||
| 9f62dfc087 | |||
| a21912dc41 | |||
| 733b4a770f | |||
| 2e097b7e46 | |||
| e1b6997ef7 | |||
| b6b7b9f433 | |||
| 8181f88cb0 | |||
| 3a4865ef7d | |||
| 37f6dbfa6b | |||
| 131fea3ba4 | |||
| b0661218a0 | |||
| 50335723b0 | |||
| 6f1bd3fd71 | |||
| fb99b101d8 | |||
| 91e658c41c | |||
| 0ce7efc978 | |||
| 3b8a2d7d65 | |||
| 61b9ab5c7b | |||
| cfda04b6ed | |||
| 11bdfbba19 | |||
| 9694831638 | |||
| a20844b55b | |||
| 158e40d7f6 | |||
| 6939cc757e | |||
| 2baec8a129 | |||
| 93a1fc1eba | |||
| fe67a09ccb | |||
| 1c4e1db054 | |||
| 5d694a73a3 | |||
| af369fa6c7 | |||
| 901036535d | |||
| 5d6cbee196 | |||
| 9e9ae09ec2 | |||
| 58697eaa04 | |||
| ce58ad91b5 | |||
| e49d14d687 | |||
| 3c98d9d2e1 | |||
| e010ff2a8c | |||
| 8d14a3ef2c | |||
| 2f3d9c9509 | |||
| 6e8f3b6e13 | |||
| b9cbdbd9a7 | |||
| ebf35356a8 | |||
| 00cec3267c | |||
| 4bfb2d219b | |||
| f3ba1fa7ed | |||
| 7eca99e249 | |||
| 80c61110df | |||
| 837bb83d48 | |||
| 054ac78970 | |||
| 8e47f91ba5 | |||
| 31f6bd4c02 | |||
| 3e5744dbb7 | |||
| 5982a868ea | |||
| 69a42e26a9 | |||
| d9ed420ed4 | |||
| 2b3397120f | |||
| cb0d8320e6 | |||
| 53d9b65e64 | |||
| df0fe62fca | |||
| 1f41aa7d81 | |||
| dad77fc506 | |||
| 350f5c88c7 | |||
| 928c0dc535 | |||
| 483a2c7c02 | |||
| cc7a28db65 | |||
| 065e764957 | |||
| 000ba73887 | |||
| 084fde885a | |||
| 08b81d991b | |||
| 4937f87c0d | |||
| 50a5679b5c | |||
| 2c248791e1 | |||
| 6b6a7fe231 | |||
| 6fd197d1bf | |||
| 00aa59c9c6 | |||
| 06254ba462 | |||
| 684cb19a7c | |||
| 9c5da12653 | |||
| 83f229abc6 | |||
| 3405a1f600 | |||
| 8be02ff7b0 | |||
| c371f00315 | |||
| 0ccf798f14 | |||
| 6ca94074f3 | |||
| 8236eee7a6 | |||
| 8bdeca6374 | |||
| e37bbdfcf4 | |||
| dac882ce9f | |||
| 2dc11f6fa1 | |||
| fe2e689865 | |||
| ece5fecf12 | |||
| b21dea84d9 | |||
| 492b0aadc8 | |||
| b399630202 | |||
| b9b1fc3a70 | |||
| 1d52f59657 | |||
| 78f6b8d8bc | |||
| 0afee71e9e | |||
| dc67d82671 | |||
| c7d9683fc4 | |||
| ed7675bdcd | |||
| 3c7c6b4ad7 | |||
| 838d7975c9 | |||
| 5af9712d51 | |||
| ffa7b922e8 | |||
| e2c2b8534f | |||
| cfd3cc5187 | |||
| e6de9e0004 | |||
| 1f29418ea5 | |||
| 977b30c51a | |||
| 66a849dd12 | |||
| 637abc384b | |||
| 8959b81f6a | |||
| 32bb8ac5a4 | |||
| 016caba35f | |||
| 9290f61f94 | |||
| d522dd7a31 | |||
| 95f632e1ea | |||
| e72ec171f7 | |||
| 2538e2e9c9 | |||
| 08aff2654a | |||
| cd0fdae47b | |||
| 0f3a76809b | |||
| 4f88da2c26 | |||
| e14aba3875 | |||
| b1ceeb23b3 | |||
| 46d0807fa4 | |||
| 579c572dd8 | |||
| bbf4c18825 | |||
| 9a94ac5317 | |||
| 0b904e82b5 | |||
| c816558936 | |||
| 4ce0553630 | |||
| b1b285b673 | |||
| c773714e2d | |||
| 51fec0c7d3 | |||
| 2a90478ba7 | |||
| 87ace12a4f | |||
| 77162e4b51 | |||
| 93756a62f4 | |||
| 2343004be1 | |||
| d6fe350a4a | |||
| 23321f8ac9 | |||
| 71902f3b9b | |||
| 13b544521d | |||
| 2cf8c1207d | |||
| 632f84904f | |||
| a1fd73cdb0 | |||
| a53866953e | |||
| e72758e04a | |||
| 9d373c194d | |||
| 856e123ec2 | |||
| 0b2af6b48f | |||
| ff996b05f0 | |||
| 403502ef1b | |||
| ee4ad36e70 | |||
| 4f2650277a | |||
| 0231a6037e | |||
| db51a5bb44 | |||
| d1b7063e44 | |||
| 364d4b11e8 | |||
| c1769aa299 | |||
| a571eee945 | |||
| ab8653d3de | |||
| 143d127fb9 | |||
| 748a3c167e | |||
| 01cf2e240a | |||
| 49689d5057 | |||
| f722d7731d | |||
| 5b93b35882 | |||
| b29ee4df60 | |||
| d8fe3c6faf | |||
| 74ba3261ff | |||
| ac42f335c2 | |||
| f0eefbd5af | |||
| ed48f29e3a | |||
| 03c5d5197b | |||
| 7f0921bd96 | |||
| c38b49b1cf | |||
| c4dfbd4a1c | |||
| 9b1e5d7649 | |||
| 9260f943ff | |||
| dcdaa8d62d | |||
| 360665c082 | |||
| c07b96ade7 | |||
| 576a790e2f | |||
| 9b7a9fc988 | |||
| 62485157ad | |||
| 4ce7b089ca | |||
| 9e91098ebd | |||
| 49356b31a1 | |||
| 29f93160b7 | |||
| 507c2353e9 | |||
| 827e79c705 | |||
| d02139bbcd | |||
| 8a19b97900 | |||
| 981fad1ec8 | |||
| 019147c1a1 | |||
| 782d6100f0 | |||
| ff98b5f321 | |||
| f6ea317dd1 | |||
| 68e8ca8d8e | |||
| bcae131545 | |||
| c6d686eabd | |||
| a8b660bfbf | |||
| 1f60486df8 | |||
| c066280990 | |||
| ba747ea579 | |||
| 0df7767f4a | |||
| 6f857b16ee | |||
| 27115e5794 | |||
| 92378095a2 | |||
| 13e6a81bbe | |||
| bd2a794b59 | |||
| 576ffce9aa | |||
| 3bfdaa7e10 | |||
| dc7b5bae9b | |||
| 422be31c4e | |||
| 8d8fb76694 | |||
| 80d605beac | |||
| bb9a0258df | |||
| daa0741802 | |||
| 69258fee7b | |||
| 1fd7569c6e | |||
| 3a1f19ee0a | |||
| 90bc7f5829 | |||
| 30b391f3fb | |||
| 089f488c15 | |||
| 99b99ce3cd | |||
| 10ca1239b6 | |||
| f4fc3c4db4 | |||
| e17690fbed | |||
| eaa8ca5fdc | |||
| 234c0680d8 | |||
| ea8c6af028 | |||
| d7d1b3f2a0 | |||
| 5f31c70cbe | |||
| 579a04d179 | |||
| ff3b7963b1 | |||
| e85d212891 | |||
| 5b19d5e0d8 | |||
| 7c21889239 | |||
| 52d35c06eb | |||
| 52c3ea24d5 | |||
| 03756ab894 | |||
| ef40ecfefd | |||
| 8e35d904cb | |||
| 73b2d31298 | |||
| 9808492509 | |||
| 81788338e3 | |||
| 3ffe1517e0 | |||
| 641e74b747 | |||
| 49c316e13d | |||
| 5235053523 | |||
| 8676a99b3a | |||
| af6d2df823 | |||
| bbe80d882a | |||
| 408ec17201 | |||
| a4ef487fab | |||
| 8208f8de51 | |||
| e8ea7f9d29 | |||
| e431a41fc1 | |||
| 84b380028c | |||
| 0b2cfe956d | |||
| 1f5c9d2e1b | |||
| 372f981d33 | |||
| 6f03e221c6 | |||
| 90a5adec8f | |||
| 62939385a0 | |||
| d4729e1483 | |||
| 9de35f0202 | |||
| 773d249d36 | |||
| d12a492cb6 | |||
| ef0b521083 | |||
| 6261447923 | |||
| d18881c8ad | |||
| 89596dbe0b | |||
| 358536ffaa | |||
| 4592f8e8d8 | |||
| 0038d173a2 | |||
| 68e6ad76fb | |||
| 6462329494 | |||
| 2fdd1c0bd2 | |||
| 006deb22db | |||
| 40624255ca | |||
| 891bea81bf | |||
| f71577f86e | |||
| e3e88b3679 | |||
| 5dc993890d | |||
| a2025c14b1 | |||
| 24900fb9f8 | |||
| 594c473134 | |||
| 34f811c128 | |||
| 7e4c284abb | |||
| 664d0c5160 | |||
| eb26fb901f | |||
| 3141b04376 | |||
| 201fb5dc01 | |||
| 15e668c056 | |||
| a42f425971 | |||
| 3f4774855e | |||
| 28f995cea6 | |||
| cfb14d7089 | |||
| ef4d41c80a | |||
| 584e47f51f | |||
| 66e40fcbb0 | |||
| 49d54f8ca5 | |||
| 4ed236249a | |||
| 00a4287945 | |||
| 92d52bf99b | |||
| 2e611c20d4 | |||
| ae10f3d21f | |||
| 7a9b392f69 | |||
| 493d71dba9 | |||
| 55d4e4072a | |||
| f687b34c14 | |||
| 507517c1ad | |||
| ae8cfb2b23 | |||
| 731e380445 | |||
| 9048252826 | |||
| e76819a570 | |||
| 87c311d6e7 | |||
| 86d8eae984 | |||
| 6aac216763 | |||
| b7c3fb7f30 | |||
| a0a3b7e9c9 | |||
| d98db5e6bc | |||
| 48adc4c98d | |||
| bd0edb322d | |||
| 79d4562cb1 | |||
| f48263e5a2 | |||
| 63196d7e53 | |||
| e0e9823a03 | |||
| 72a0d614af | |||
| f766057e50 | |||
| f0b5c831f1 | |||
| d3232a09fb | |||
| 586c59949a | |||
| 7cbaecc53a | |||
| 4f8bb7fb51 | |||
| 1c883be16b | |||
| 73d681d674 | |||
| e84a963d66 | |||
| 8daa558997 | |||
| b78fd0f6ac | |||
| 58c180d6cc | |||
| 831eb3ef5e | |||
| fa68bee762 | |||
| 796700b4fe | |||
| ab09042fdc | |||
| 041d4a4a8b | |||
| ee79d7ecdc | |||
| 18e377f159 | |||
| e33456d168 | |||
| df2c104002 | |||
| f265fa683c | |||
| 7e7f9902f6 | |||
| a36184a261 | |||
| dcd2630d6b | |||
| b70f543790 | |||
| 24c1c15b66 | |||
| 4a0cd7228b | |||
| 12093ae7ea | |||
| 6a980e64eb | |||
| edfe212227 | |||
| 37071ada78 | |||
| 3c7e820773 | |||
| 203837aa84 | |||
| e3bc98a155 | |||
| f85532f15e | |||
| 5fe63c2c1a | |||
| 2c1d85989f | |||
| d518ab908e | |||
| bc32ff31d4 | |||
| 7804677f2b | |||
| b04d71ca5f | |||
| 27c884f4eb | |||
| ab4de382f3 | |||
| a293c633fc | |||
| 507e3ece98 | |||
| b77826fd3d | |||
| 4c7358d63b | |||
| 4814612102 | |||
| 6dae4396bb | |||
| a966f63bbd | |||
| 76f9e6baee | |||
| dd5560f00b | |||
| b56575fc50 | |||
| b06d30c0f9 | |||
| 49f17be9df | |||
| 9c50e1c48d | |||
| 0688835ac0 | |||
| 353b8f21cd | |||
| 30a0d553d5 | |||
| d0fbc362b2 | |||
| 39c534ef54 | |||
| 7f86166abf | |||
| 838e86469a | |||
| 24b147e2a6 | |||
| 11342ff1df | |||
| bf20af9345 | |||
| c69774e3f2 | |||
| 7de8f5a59a | |||
| 8a114a105e | |||
| 909e3788ea | |||
| 09a92ef6ce | |||
| 7d9c2bcce9 | |||
| 529ae961ff | |||
| 5f9865cc73 | |||
| b891b18b20 | |||
| bc461b1646 | |||
| b0eeaecf1c | |||
| 2708f11fcd | |||
| fd8daa398f | |||
| c59ac4ee0e | |||
| a03aa9cbab | |||
| 89b714c960 | |||
| d9b0b0112e | |||
| 6640039cdf | |||
| 70deffd418 | |||
| f31d5b12da | |||
| 7fd6155807 | |||
| 043c677ddc | |||
| f634d9f8b1 | |||
| db637d9c31 | |||
| 24b521881c | |||
| 0b3a38ec8f | |||
| 330673bef9 | |||
| 661ff0e726 | |||
| e1c45fda9a | |||
| b45a4a18f2 | |||
| 1456e7028a | |||
| b38ee33e5a | |||
| 903a9f93c3 | |||
| b08376651a | |||
| 75514c7c42 | |||
| ea6f561c96 | |||
| 053544a578 | |||
| 4e6d60b755 | |||
| 4308718c88 | |||
| 1b59ca0412 | |||
| 282cc0c722 | |||
| 4584abaa72 | |||
| c1ca0c9c2f | |||
| 27c260cd03 | |||
| 73a5c55547 | |||
| b8831c253e | |||
| b2d3e75566 | |||
| cb526170e2 | |||
| 93237bfdb8 | |||
| 1c39c741b6 | |||
| ec2589f517 | |||
| 2ca6e8be9a | |||
| b9d1a776cb | |||
| 5eb609d8a0 | |||
| 4d5069726b | |||
| 3a98d63bf1 | |||
| 0da8644e77 | |||
| ac65af772a | |||
| e643189b10 | |||
| 97716d5570 | |||
| 5517252200 | |||
| 92a5c31ce6 | |||
| 345c038e8b | |||
| 59defa6b11 | |||
| 7e0d7ebbd4 | |||
| c13d4ed235 | |||
| a47b5035fe | |||
| f6be389f4f | |||
| 252f0fe88e | |||
| 387faca71e | |||
| 6c172702be | |||
| 62cda02ded | |||
| 3e2a6424bd | |||
| c0f695bcf9 | |||
| 2e1e2e4325 | |||
| 1316bb5d5b | |||
| fe97860694 | |||
| 98b4aa6bae | |||
| eb7f4e74f2 | |||
| 4a9e326267 | |||
| 67edc6cf9a | |||
| e740838ef7 | |||
| dec44b1288 | |||
| 98a0729549 | |||
| 97868593f8 | |||
| dcdfb074af | |||
| a36b1097c5 | |||
| e268a05f6e | |||
| fb7d22dca8 | |||
| bac53bc241 | |||
| ce690acda0 | |||
| cb8157dfca | |||
| 4c7c6e37d1 | |||
| 116e53e07d | |||
| a74c4433f7 | |||
| 2ccf74a921 | |||
| f3a81d82be | |||
| 5451d28fe4 | |||
| db57268440 | |||
| ac265067e8 | |||
| 5330830362 | |||
| 4b6b24b2f5 | |||
| 7d5d89e9cb | |||
| ab85cbf599 | |||
| c6937618c2 | |||
| ef84d63982 | |||
| 280a7445f4 | |||
| 5ae8a47f4b | |||
| bc148d08fa | |||
| 57a1e73383 | |||
| c7b9a57795 | |||
| eb1edf94d3 | |||
| c0b230a95e | |||
| 607c2cc8e7 | |||
| 48d3a3defd | |||
| 8509285aae | |||
| 2413e9951f | |||
| 1a444fd703 | |||
| bd68a140ba | |||
| 7b34211bb1 | |||
| fa78223229 | |||
| 39c19dc1d4 | |||
| 7b4b6357cb | |||
| 4454290820 | |||
| d94cd9dc8d | |||
| 8c07af9505 | |||
| d32fb44187 | |||
| dd38480ce2 | |||
| 08e8e3918e | |||
| 7a3591d1bd | |||
| 59ab7f5c8d | |||
| c2b2182182 | |||
| 66b656f058 | |||
| c8f7ecabf4 | |||
| 9137249510 | |||
| 7367c8ef67 | |||
| ed480e1e6e | |||
| 841074fc27 | |||
| 64f477f90e | |||
| 813741bc3d | |||
| 7566546fd7 | |||
| a4f83e0125 | |||
| 7b08c0dde6 | |||
| 59b8b397b7 | |||
| 361ef2a475 | |||
| d382fcc919 | |||
| aac8451627 | |||
| c96c09f42f | |||
| 50f8f18503 | |||
| 7c72e70967 | |||
| 75b74a6687 | |||
| 9001cd8a6f | |||
| 7dcb4435d8 | |||
| 1279fd234f |
+11
-5
@@ -1,10 +1,16 @@
|
||||
root=true
|
||||
# editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Default settings:
|
||||
# A newline ending every file
|
||||
# Use 4 spaces as indentation
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{cs,cshtml,csx,vb,vbx,vbhtml,fs,fsx,txt,ps1,sql}]
|
||||
indent_size = 4
|
||||
# Trim trailing whitespace, limited support.
|
||||
# https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,49 @@
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.gif binary
|
||||
|
||||
*.cs text=auto diff=csharp
|
||||
*.vb text=auto
|
||||
*.c text=auto
|
||||
*.cpp text=auto
|
||||
*.cxx text=auto
|
||||
*.h text=auto
|
||||
*.hxx text=auto
|
||||
*.py text=auto
|
||||
*.rb text=auto
|
||||
*.java text=auto
|
||||
*.html text=auto
|
||||
*.htm text=auto
|
||||
*.css text=auto
|
||||
*.scss text=auto
|
||||
*.sass text=auto
|
||||
*.less text=auto
|
||||
*.js text=auto
|
||||
*.lisp text=auto
|
||||
*.clj text=auto
|
||||
*.sql text=auto
|
||||
*.php text=auto
|
||||
*.lua text=auto
|
||||
*.m text=auto
|
||||
*.asm text=auto
|
||||
*.erl text=auto
|
||||
*.fs text=auto
|
||||
*.fsx text=auto
|
||||
*.hs text=auto
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
+2
-1
@@ -139,4 +139,5 @@ src/PrecompiledWeb/*
|
||||
|
||||
build.out/
|
||||
build.tmp/
|
||||
build/Modules/*/temp/
|
||||
build/Modules/*/temp/
|
||||
/src/.idea/*
|
||||
|
||||
@@ -14,6 +14,43 @@ By default, this builds the current version. It is possible to specify a differe
|
||||
|
||||
Valid version strings are defined in the `Set-UmbracoVersion` documentation below.
|
||||
|
||||
## PowerShell Quirks
|
||||
|
||||
There is a good chance that running `build.ps1` ends up in error, with messages such as
|
||||
|
||||
>The file ...\build\build.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies.
|
||||
|
||||
PowerShell has *Execution Policies* that may prevent the script from running. You can check the current policies with:
|
||||
|
||||
PS> Get-ExecutionPolicy -List
|
||||
|
||||
Scope ExecutionPolicy
|
||||
----- ---------------
|
||||
MachinePolicy Undefined
|
||||
UserPolicy Undefined
|
||||
Process Undefined
|
||||
CurrentUser Undefined
|
||||
LocalMachine RemoteSigned
|
||||
|
||||
Policies can be `Restricted`, `AllSigned`, `RemoteSigned`, `Unrestricted` and `Bypass`. Scopes can be `MachinePolicy`, `UserPolicy`, `Process`, `CurrentUser`, `LocalMachine`. You need the current policy to be `RemoteSigned`—as long as it is `Undefined`, the script cannot run. You can change the current user policy with:
|
||||
|
||||
PS> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||
|
||||
Alternatively, you can do it at machine level, from within an elevated PowerShell session:
|
||||
|
||||
PS> Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned
|
||||
|
||||
And *then* the script should run. It *might* however still complain about executing scripts, with messages such as:
|
||||
|
||||
>Security warning - Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning message. Do you want to run ...\build\build.ps1?
|
||||
[D] Do not run [R] Run once [S] Suspend [?] Help (default is "D"):
|
||||
|
||||
This is usually caused by the scripts being *blocked*. And that usually happens when the source code has been downloaded as a Zip file. When Windows downloads Zip files, they are marked as *blocked* (technically, they have a Zone.Identifier alternate data stream, with a value of "3" to indicate that they were downloaded from the Internet). And when such a Zip file is un-zipped, each and every single file is also marked as blocked.
|
||||
|
||||
The best solution is to unblock the Zip file before un-zipping: right-click the files, open *Properties*, and there should be a *Unblock* checkbox at the bottom of the dialog. If, however, the Zip file has already been un-zipped, it is possible to recursively unblock all files from PowerShell with:
|
||||
|
||||
PS> Get-ChildItem -Recurse *.* | Unblock-File
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Umbraco CMS
|
||||
===========
|
||||
The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 390,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
|
||||
The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
|
||||
|
||||
[](https://vimeo.com/172382998/)
|
||||
|
||||
@@ -12,7 +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 `gulp dev` in `src\Umbraco.Web.UI.Client`.
|
||||
The easiest way to get started is to run `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`. See [this page](BUILD.md) for more details.
|
||||
|
||||
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.
|
||||
|
||||
@@ -26,7 +26,7 @@ For the first time on the Microsoft platform, there is a free user and developer
|
||||
|
||||
Umbraco is not only loved by developers, but is a content editors dream. Enjoy intuitive editing tools, media management, responsive views and approval workflows to send your content live.
|
||||
|
||||
Used by more than 350,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scales. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 200,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
|
||||
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scales. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
|
||||
|
||||
To view more examples, please visit [https://umbraco.com/why-umbraco/#caseStudies](https://umbraco.com/why-umbraco/#caseStudies)
|
||||
|
||||
|
||||
@@ -10,6 +10,5 @@ IF ERRORLEVEL 1 (
|
||||
: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 If this is due to a SecurityError then please refer to BUILD.md for help!
|
||||
ECHO.
|
||||
ECHO powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
|
||||
@@ -44,7 +44,7 @@ function Build-UmbracoDocs
|
||||
# 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'") `
|
||||
(Get-Content $indexPath).Replace("origin + location.href.substr(origin.length).replace(rUrl, indexFile)", "'$baseUrl'") `
|
||||
| Set-Content $indexPath
|
||||
|
||||
# restore
|
||||
@@ -100,8 +100,9 @@ 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"
|
||||
Write-Host "Download DocFx from $source"
|
||||
|
||||
Invoke-WebRequest $source -OutFile "$buildTemp\docfx.zip"
|
||||
|
||||
&$uenv.Zip x "$buildTemp\docfx.zip" -o"$buildTemp\docfx" -aos > $nul
|
||||
|
||||
@@ -239,18 +239,18 @@ function Prepare-Tests
|
||||
|
||||
# data
|
||||
Write-Host "Copy data files"
|
||||
if( -Not (Test-Path -Path "$tmp\tests\Packaging" ) )
|
||||
if (-Not (Test-Path -Path "$tmp\tests\Packaging" ) )
|
||||
{
|
||||
Write-Host "Create packaging directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\Packaging"
|
||||
mkdir "$tmp\tests\Packaging" > $null
|
||||
}
|
||||
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
|
||||
|
||||
# required for package install tests
|
||||
if( -Not (Test-Path -Path "$tmp\tests\bin" ) )
|
||||
if (-Not (Test-Path -Path "$tmp\tests\bin" ) )
|
||||
{
|
||||
Write-Host "Create bin directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\bin"
|
||||
mkdir "$tmp\tests\bin" > $null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,19 +27,18 @@
|
||||
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.6, 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.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="Examine" version="[0.1.89, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.10, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -32,6 +32,7 @@
|
||||
<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\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
|
||||
<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" />
|
||||
|
||||
@@ -98,10 +98,51 @@ if ($project) {
|
||||
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
|
||||
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
|
||||
} else {
|
||||
# This part only runs for upgrades
|
||||
|
||||
$upgradeViewSource = Join-Path $umbracoFolderSource "Views\install\*"
|
||||
$upgradeView = Join-Path $umbracoFolder "Views\install\"
|
||||
Write-Host "Copying2 ${upgradeViewSource} to ${upgradeView}"
|
||||
Copy-Item $upgradeViewSource $upgradeView -Force
|
||||
|
||||
Try
|
||||
{
|
||||
# Disable tours for upgrades, presumably Umbraco experience is already available
|
||||
$umbracoSettingsConfigPath = Join-Path $configFolder "umbracoSettings.config"
|
||||
$content = (Get-Content $umbracoSettingsConfigPath).Replace('<tours enable="true">','<tours enable="false">')
|
||||
# Saves with UTF-8 encoding without BOM which makes sure Umbraco can still read it
|
||||
# Reference: https://stackoverflow.com/a/32951824/5018
|
||||
[IO.File]::WriteAllLines($umbracoSettingsConfigPath, $content)
|
||||
}
|
||||
Catch
|
||||
{
|
||||
# Not a big problem if this fails, let it go
|
||||
}
|
||||
|
||||
Try
|
||||
{
|
||||
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
|
||||
$uiXml = New-Object System.Xml.XmlDocument
|
||||
$uiXml.PreserveWhitespace = $true
|
||||
|
||||
$uiXml.Load($uiXmlFile)
|
||||
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
|
||||
|
||||
if($createExists.Count -eq 0)
|
||||
{
|
||||
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
|
||||
|
||||
#Creating: <create assembly="umbraco" type="macroTasks" />
|
||||
$createNode = $uiXml.CreateElement("create")
|
||||
$createNode.SetAttribute("assembly", "umbraco")
|
||||
$createNode.SetAttribute("type", "macroTasks")
|
||||
$macrosTasksNode.AppendChild($createNode)
|
||||
$uiXml.Save($uiXmlFile)
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
}
|
||||
|
||||
$installFolder = Join-Path $projectPath "Install"
|
||||
|
||||
@@ -79,14 +79,14 @@
|
||||
<add sortOrder="1" alias="dataTypes" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes(sortOrder)" />
|
||||
|
||||
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2"
|
||||
|
||||
<add initialize="true" sortOrder="2" alias="macros" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MacroTreeController, umbraco"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5"
|
||||
<add initialize="true" sortOrder="5" alias="xslt" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.XsltTreeController, umbraco"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
|
||||
@@ -49,11 +49,9 @@
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.7.3")]
|
||||
[assembly: AssemblyInformationalVersion("7.7.3")]
|
||||
[assembly: AssemblyFileVersion("7.8.2")]
|
||||
[assembly: AssemblyInformationalVersion("7.8.2")]
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Threading;
|
||||
using Semver;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
@@ -260,7 +262,7 @@ namespace Umbraco.Core
|
||||
/// - http://issues.umbraco.org/issue/U4-5728
|
||||
/// - http://issues.umbraco.org/issue/U4-5391
|
||||
/// </remarks>
|
||||
internal string UmbracoApplicationUrl
|
||||
public string UmbracoApplicationUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -268,9 +270,21 @@ namespace Umbraco.Core
|
||||
return _umbracoApplicationUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the url.
|
||||
/// </summary>
|
||||
public void ResetUmbracoApplicationUrl()
|
||||
{
|
||||
_umbracoApplicationUrl = null;
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
internal string _umbracoApplicationUrl;
|
||||
internal string _umbracoApplicationUrl;
|
||||
|
||||
internal List<string> _umbracoApplicationDomains = new List<string>();
|
||||
|
||||
internal string _umbracoApplicationDeploymentId;
|
||||
|
||||
private Lazy<bool> _configured;
|
||||
internal MainDom MainDom { get; private set; }
|
||||
@@ -341,6 +355,52 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Current Version of the Umbraco Site before an upgrade
|
||||
/// by using the last/most recent Umbraco Migration that has been run
|
||||
/// </summary>
|
||||
/// <returns>A SemVersion of the latest Umbraco DB Migration run</returns>
|
||||
/// <remarks>
|
||||
/// NOTE: This existed in the InstallHelper previously but should really be here so it can be re-used if necessary
|
||||
/// </remarks>
|
||||
internal SemVersion CurrentVersion()
|
||||
{
|
||||
//Set a default version of 0.0.0
|
||||
var version = new SemVersion(0);
|
||||
|
||||
//If we have a db context available, if we don't then we are not installed anyways
|
||||
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect)
|
||||
version = DatabaseContext.ValidateDatabaseSchema().DetermineInstalledVersionByMigrations(Services.MigrationEntryService);
|
||||
|
||||
if (version != new SemVersion(0))
|
||||
return version;
|
||||
|
||||
// If we aren't able to get a result from the umbracoMigrations table then use the version in web.config, if it's available
|
||||
if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus))
|
||||
return version;
|
||||
|
||||
var configuredVersion = GlobalSettings.ConfigurationStatus;
|
||||
|
||||
string currentComment = null;
|
||||
|
||||
var current = configuredVersion.Split('-');
|
||||
if (current.Length > 1)
|
||||
currentComment = current[1];
|
||||
|
||||
Version currentVersion;
|
||||
if (Version.TryParse(current[0], out currentVersion))
|
||||
{
|
||||
version = new SemVersion(
|
||||
currentVersion.Major,
|
||||
currentVersion.Minor,
|
||||
currentVersion.Build,
|
||||
string.IsNullOrWhiteSpace(currentComment) ? null : currentComment,
|
||||
currentVersion.Revision > 0 ? currentVersion.Revision.ToString() : null);
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
private void AssertIsNotReady()
|
||||
{
|
||||
if (this.IsReady)
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Umbraco.Core.Cache
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
private class NoopLocker : DisposableObject
|
||||
private class NoopLocker : DisposableObjectSlim
|
||||
{
|
||||
protected override void DisposeResources()
|
||||
{ }
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a composite key of (Type, Type) for fast dictionaries.
|
||||
/// </summary>
|
||||
internal struct CompositeTypeTypeKey : IEquatable<CompositeTypeTypeKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompositeTypeTypeKey"/> struct.
|
||||
/// </summary>
|
||||
public CompositeTypeTypeKey(Type type1, Type type2) : this()
|
||||
{
|
||||
Type1 = type1;
|
||||
Type2 = type2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first type.
|
||||
/// </summary>
|
||||
public Type Type1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second type.
|
||||
/// </summary>
|
||||
public Type Type2 { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(CompositeTypeTypeKey other)
|
||||
{
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj is CompositeTypeTypeKey ? (CompositeTypeTypeKey)obj : default(CompositeTypeTypeKey);
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
public static bool operator ==(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
{
|
||||
return key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2;
|
||||
}
|
||||
|
||||
public static bool operator !=(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
{
|
||||
return key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Type1.GetHashCode() * 397) ^ Type2.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a list of types.
|
||||
/// </summary>
|
||||
/// <remarks>Types in the list are, or derive from, or implement, the base type.</remarks>
|
||||
/// <typeparam name="TBase">The base type.</typeparam>
|
||||
internal class TypeList<TBase>
|
||||
{
|
||||
private readonly List<Type> _list = new List<Type>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a type to the list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to add.</typeparam>
|
||||
public void Add<T>()
|
||||
where T : TBase
|
||||
{
|
||||
_list.Add(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a type is in the list.
|
||||
/// </summary>
|
||||
public bool Contains(Type type)
|
||||
{
|
||||
return _list.Contains(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class BackOfficeElement : UmbracoConfigurationElement, IBackOfficeSection
|
||||
{
|
||||
[ConfigurationProperty("tours")]
|
||||
internal TourConfigElement Tours
|
||||
{
|
||||
get { return (TourConfigElement)this["tours"]; }
|
||||
}
|
||||
|
||||
ITourSection IBackOfficeSection.Tours
|
||||
{
|
||||
get { return Tours; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public interface IBackOfficeSection
|
||||
{
|
||||
ITourSection Tours { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public interface ITourSection
|
||||
{
|
||||
bool EnableTours { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public interface IUmbracoSettingsSection : IUmbracoConfigurationSection
|
||||
{
|
||||
IBackOfficeSection BackOffice { get; }
|
||||
|
||||
IContentSection Content { get; }
|
||||
|
||||
ISecuritySection Security { get; }
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class TourConfigElement : UmbracoConfigurationElement, ITourSection
|
||||
{
|
||||
//disabled by default so that upgraders don't get it enabled by default
|
||||
//TODO: we probably just want to disable the initial one from automatically loading ?
|
||||
[ConfigurationProperty("enable", DefaultValue = false)]
|
||||
public bool EnableTours
|
||||
{
|
||||
get { return (bool)this["enable"]; }
|
||||
}
|
||||
|
||||
//TODO: We could have additional filters, etc... defined here
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
public class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection
|
||||
{
|
||||
[ConfigurationProperty("backOffice")]
|
||||
internal BackOfficeElement BackOffice
|
||||
{
|
||||
get { return (BackOfficeElement)this["backOffice"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("content")]
|
||||
internal ContentElement Content
|
||||
{
|
||||
@@ -149,6 +155,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return Templates; }
|
||||
}
|
||||
|
||||
IBackOfficeSection IUmbracoSettingsSection.BackOffice
|
||||
{
|
||||
get { return BackOffice; }
|
||||
}
|
||||
|
||||
IDeveloperSection IUmbracoSettingsSection.Developer
|
||||
{
|
||||
get { return Developer; }
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.7.3");
|
||||
private static readonly Version Version = new Version("7.8.2");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -73,6 +73,11 @@
|
||||
/// </summary>
|
||||
public const string Media = "media";
|
||||
|
||||
/// <summary>
|
||||
/// alias for the macro tree.
|
||||
/// </summary>
|
||||
public const string Macros = "macros";
|
||||
|
||||
/// <summary>
|
||||
/// alias for the datatype tree.
|
||||
/// </summary>
|
||||
|
||||
@@ -11,17 +11,53 @@
|
||||
/// The integer identifier for global system root node.
|
||||
/// </summary>
|
||||
public const int Root = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for global system root node.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RootString = "-1";
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinContent = -20;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RecycleBinContentString = "-20";
|
||||
|
||||
/// <summary>
|
||||
/// The string path prefix of the content's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Everything that is in the content recycle bin, has a path that starts with the prefix.</para>
|
||||
/// <para>Use this instead of re-creating the string everywhere.</para>
|
||||
/// </remarks>
|
||||
public const string RecycleBinContentPathPrefix = "-1,-20,";
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinMedia = -21;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RecycleBinMediaString = "-21";
|
||||
|
||||
/// <summary>
|
||||
/// The string path prefix of the media's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Everything that is in the media recycle bin, has a path that starts with the prefix.</para>
|
||||
/// <para>Use this instead of re-creating the string everywhere.</para>
|
||||
/// </remarks>
|
||||
public const string RecycleBinMediaPathPrefix = "-1,-21,";
|
||||
|
||||
public const int DefaultContentListViewDataTypeId = -95;
|
||||
public const int DefaultMediaListViewDataTypeId = -96;
|
||||
public const int DefaultMembersListViewDataTypeId = -97;
|
||||
@@ -29,6 +65,6 @@
|
||||
public const string UmbracoConnectionName = "umbracoDbDSN";
|
||||
public const string UmbracoMigrationName = "Umbraco";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ namespace Umbraco.Core
|
||||
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
|
||||
{
|
||||
if (truncateTo == DateTruncate.Year)
|
||||
return new DateTime(dt.Year, 0, 0);
|
||||
return new DateTime(dt.Year, 1, 1);
|
||||
if (truncateTo == DateTruncate.Month)
|
||||
return new DateTime(dt.Year, dt.Month, 0);
|
||||
return new DateTime(dt.Year, dt.Month, 1);
|
||||
if (truncateTo == DateTruncate.Day)
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day);
|
||||
if (truncateTo == DateTruncate.Hour)
|
||||
@@ -41,43 +41,43 @@ namespace Umbraco.Core
|
||||
Hour,
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day)
|
||||
/// </summary>
|
||||
/// <param name="fromDateTime">Date to start from</param>
|
||||
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,60 +2,60 @@ using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract implementation of IDisposable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can also be used as a pattern for when inheriting is not possible.
|
||||
///
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract implementation of IDisposable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can also be used as a pattern for when inheriting is not possible.
|
||||
///
|
||||
/// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx
|
||||
/// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
|
||||
///
|
||||
/// Note: if an object's ctor throws, it will never be disposed, and so if that ctor
|
||||
/// has allocated disposable objects, it should take care of disposing them.
|
||||
/// </remarks>
|
||||
public abstract class DisposableObject : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
/// </remarks>
|
||||
public abstract class DisposableObject : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
// for internal tests only (not thread safe)
|
||||
//TODO make this internal + rename "Disposed" when we can break compatibility
|
||||
public bool IsDisposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// finalizer
|
||||
~DisposableObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
public bool IsDisposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// finalizer
|
||||
~DisposableObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
//TODO make this private, non-virtual when we can break compatibility
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
DisposeUnmanagedResources();
|
||||
|
||||
if (disposing)
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
|
||||
protected virtual void DisposeUnmanagedResources()
|
||||
{ }
|
||||
}
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
|
||||
protected virtual void DisposeUnmanagedResources()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// This should not be use if there are ubmanaged resources to be disposed, use DisposableObject instead
|
||||
/// </summary>
|
||||
public abstract class DisposableObjectSlim : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
// for internal tests only (not thread safe)
|
||||
internal bool Disposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a <code>using</code> (C#) statement.
|
||||
/// </summary>
|
||||
public class DisposableTimer : DisposableObject
|
||||
{
|
||||
public class DisposableTimer : DisposableObjectSlim
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly LogType? _logType;
|
||||
private readonly IProfiler _profiler;
|
||||
@@ -209,13 +209,13 @@ namespace Umbraco.Core
|
||||
loggerType,
|
||||
startMessage(),
|
||||
completeMessage());
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObjectSlim"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
if (_profiler != null)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Event messages collection
|
||||
/// </summary>
|
||||
public sealed class EventMessages : DisposableObject
|
||||
public sealed class EventMessages : DisposableObjectSlim
|
||||
{
|
||||
private readonly List<EventMessage> _msgs = new List<EventMessage>();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
@@ -7,18 +8,27 @@ namespace Umbraco.Core.Events
|
||||
public class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use the overload specifying packageMetaData instead")]
|
||||
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
if (packageMetaData == null) throw new ArgumentNullException("packageMetaData");
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: this(eventObject, packageMetaData, true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
|
||||
@@ -76,260 +76,257 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
if (_events == null)
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
|
||||
|
||||
IReadOnlyList<IEventDefinition> events;
|
||||
switch (filter)
|
||||
{
|
||||
case EventDefinitionFilter.All:
|
||||
return FilterSupersededAndUpdateToLatestEntity(_events);
|
||||
events = _events;
|
||||
break;
|
||||
case EventDefinitionFilter.FirstIn:
|
||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l1.Add(e);
|
||||
}
|
||||
return FilterSupersededAndUpdateToLatestEntity(l1);
|
||||
events = l1;
|
||||
break;
|
||||
case EventDefinitionFilter.LastIn:
|
||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l2.Add(e);
|
||||
}
|
||||
return FilterSupersededAndUpdateToLatestEntity(l2);
|
||||
events = l2;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("filter", filter, null);
|
||||
}
|
||||
|
||||
return FilterSupersededAndUpdateToLatestEntity(events);
|
||||
}
|
||||
|
||||
private class EventDefinitionTypeData
|
||||
|
||||
private class EventDefinitionInfos
|
||||
{
|
||||
public IEventDefinition EventDefinition { get; set; }
|
||||
public Type EventArgType { get; set; }
|
||||
public SupersedeEventAttribute[] SupersedeAttributes { get; set; }
|
||||
public Type[] SupersedeTypes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will iterate over the events (latest first) and filter out any events or entities in event args that are included
|
||||
/// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
|
||||
/// to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
|
||||
/// </summary>
|
||||
/// <param name="events"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList<IEventDefinition> events)
|
||||
|
||||
// fixme
|
||||
// this is way too convoluted, the superceede attribute is used only on DeleteEventargs to specify
|
||||
// that it superceeds save, publish, move and copy - BUT - publish event args is also used for
|
||||
// unpublishing and should NOT be superceeded - so really it should not be managed at event args
|
||||
// level but at event level
|
||||
//
|
||||
// what we want is:
|
||||
// if an entity is deleted, then all Saved, Moved, Copied, Published events prior to this should
|
||||
// not trigger for the entity - and even though, does it make any sense? making a copy of an entity
|
||||
// should ... trigger?
|
||||
//
|
||||
// not going to refactor it all - we probably want to *always* trigger event but tell people that
|
||||
// due to scopes, they should not expected eg a saved entity to still be around - however, now,
|
||||
// going to write a ugly condition to deal with U4-10764
|
||||
|
||||
// iterates over the events (latest first) and filter out any events or entities in event args that are included
|
||||
// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
|
||||
// to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
|
||||
internal static IEnumerable<IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList<IEventDefinition> events)
|
||||
{
|
||||
//used to keep the 'latest' entity and associated event definition data
|
||||
var allEntities = new List<Tuple<IEntity, EventDefinitionTypeData>>();
|
||||
|
||||
//tracks all CancellableObjectEventArgs instances in the events which is the only type of args we can work with
|
||||
var cancelableArgs = new List<CancellableObjectEventArgs>();
|
||||
// keeps the 'latest' entity and associated event data
|
||||
var entities = new List<Tuple<IEntity, EventDefinitionInfos>>();
|
||||
|
||||
// collects the event definitions
|
||||
// collects the arguments in result, that require their entities to be updated
|
||||
var result = new List<IEventDefinition>();
|
||||
var resultArgs = new List<CancellableObjectEventArgs>();
|
||||
|
||||
//This will eagerly load all of the event arg types and their attributes so we don't have to continuously look this data up
|
||||
var allArgTypesWithAttributes = events.Select(x => x.Args.GetType())
|
||||
// eagerly fetch superceeded arg types for each arg type
|
||||
var argTypeSuperceeding = events.Select(x => x.Args.GetType())
|
||||
.Distinct()
|
||||
.ToDictionary(x => x, x => x.GetCustomAttributes<SupersedeEventAttribute>(false).ToArray());
|
||||
|
||||
//Iterate all events and collect the actual entities in them and relates them to their corresponding EventDefinitionTypeData
|
||||
//we'll process the list in reverse because events are added in the order they are raised and we want to filter out
|
||||
//any entities from event args that are not longer relevant
|
||||
//(i.e. if an item is Deleted after it's Saved, we won't include the item in the Saved args)
|
||||
.ToDictionary(x => x, x => x.GetCustomAttributes<SupersedeEventAttribute>(false).Select(y => y.SupersededEventArgsType).ToArray());
|
||||
|
||||
// iterate over all events and filter
|
||||
//
|
||||
// process the list in reverse, because events are added in the order they are raised and we want to keep
|
||||
// the latest (most recent) entities and filter out what is not relevant anymore (too old), eg if an entity
|
||||
// is Deleted after being Saved, we want to filter out the Saved event
|
||||
for (var index = events.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var eventDefinition = events[index];
|
||||
var def = events[index];
|
||||
|
||||
var argType = eventDefinition.Args.GetType();
|
||||
var attributes = allArgTypesWithAttributes[eventDefinition.Args.GetType()];
|
||||
|
||||
var meta = new EventDefinitionTypeData
|
||||
var infos = new EventDefinitionInfos
|
||||
{
|
||||
EventDefinition = eventDefinition,
|
||||
EventArgType = argType,
|
||||
SupersedeAttributes = attributes
|
||||
EventDefinition = def,
|
||||
SupersedeTypes = argTypeSuperceeding[def.Args.GetType()]
|
||||
};
|
||||
|
||||
var args = eventDefinition.Args as CancellableObjectEventArgs;
|
||||
if (args != null)
|
||||
var args = def.Args as CancellableObjectEventArgs;
|
||||
if (args == null)
|
||||
{
|
||||
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
|
||||
|
||||
if (list == null)
|
||||
// not a cancellable event arg, include event definition in result
|
||||
result.Add(def);
|
||||
}
|
||||
else
|
||||
{
|
||||
// event object can either be a single object or an enumerable of objects
|
||||
// try to get as an enumerable, get null if it's not
|
||||
var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
|
||||
if (eventObjects == null)
|
||||
{
|
||||
//extract the event object
|
||||
var obj = args.EventObject as IEntity;
|
||||
if (obj != null)
|
||||
// single object, cast as an IEntity
|
||||
// if cannot cast, cannot filter, nothing - just include event definition in result
|
||||
var eventEntity = args.EventObject as IEntity;
|
||||
if (eventEntity == null)
|
||||
{
|
||||
//Now check if this entity already exists in other event args that supersede this current event arg type
|
||||
if (IsFiltered(obj, meta, allEntities) == false)
|
||||
{
|
||||
//if it's not filtered we can adde these args to the response
|
||||
cancelableArgs.Add(args);
|
||||
result.Add(eventDefinition);
|
||||
//track the entity
|
||||
allEntities.Add(Tuple.Create(obj, meta));
|
||||
}
|
||||
result.Add(def);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
// look for this entity in superceding event args
|
||||
// found = must be removed (ie not added), else track
|
||||
if (IsSuperceeded(eventEntity, infos, entities) == false)
|
||||
{
|
||||
//Can't retrieve the entity so cant' filter or inspect, just add to the output
|
||||
result.Add(eventDefinition);
|
||||
// track
|
||||
entities.Add(Tuple.Create(eventEntity, infos));
|
||||
|
||||
// track result arguments
|
||||
// include event definition in result
|
||||
resultArgs.Add(args);
|
||||
result.Add(def);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// enumerable of objects
|
||||
var toRemove = new List<IEntity>();
|
||||
foreach (var entity in list)
|
||||
foreach (var eventObject in eventObjects)
|
||||
{
|
||||
//extract the event object
|
||||
var obj = entity as IEntity;
|
||||
if (obj != null)
|
||||
{
|
||||
//Now check if this entity already exists in other event args that supersede this current event arg type
|
||||
if (IsFiltered(obj, meta, allEntities))
|
||||
{
|
||||
//track it to be removed
|
||||
toRemove.Add(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
//track the entity, it's not filtered
|
||||
allEntities.Add(Tuple.Create(obj, meta));
|
||||
}
|
||||
}
|
||||
// extract the event object, cast as an IEntity
|
||||
// if cannot cast, cannot filter, nothing to do - just leave it in the list & continue
|
||||
var eventEntity = eventObject as IEntity;
|
||||
if (eventEntity == null)
|
||||
continue;
|
||||
|
||||
// look for this entity in superceding event args
|
||||
// found = must be removed, else track
|
||||
if (IsSuperceeded(eventEntity, infos, entities))
|
||||
toRemove.Add(eventEntity);
|
||||
else
|
||||
{
|
||||
//we don't need to do anything here, we can't cast to IEntity so we cannot filter, so it will just remain in the list
|
||||
}
|
||||
entities.Add(Tuple.Create(eventEntity, infos));
|
||||
}
|
||||
|
||||
//remove anything that has been filtered
|
||||
// remove superceded entities
|
||||
foreach (var entity in toRemove)
|
||||
{
|
||||
list.Remove(entity);
|
||||
}
|
||||
eventObjects.Remove(entity);
|
||||
|
||||
//track the event and include in the response if there's still entities remaining in the list
|
||||
if (list.Count > 0)
|
||||
// if there are still entities in the list, keep the event definition
|
||||
if (eventObjects.Count > 0)
|
||||
{
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
//re-assign if the items have changed
|
||||
args.EventObject = list;
|
||||
// re-assign if changed
|
||||
args.EventObject = eventObjects;
|
||||
}
|
||||
cancelableArgs.Add(args);
|
||||
result.Add(eventDefinition);
|
||||
|
||||
// track result arguments
|
||||
// include event definition in result
|
||||
resultArgs.Add(args);
|
||||
result.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//it's not a cancelable event arg so we just include it in the result
|
||||
result.Add(eventDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
|
||||
UpdateToLatestEntities(allEntities, cancelableArgs);
|
||||
// go over all args in result, and update them with the latest instanceof each entity
|
||||
UpdateToLatestEntities(entities, resultArgs);
|
||||
|
||||
//we need to reverse the result since we've been adding by latest added events first!
|
||||
// reverse, since we processed the list in reverse
|
||||
result.Reverse();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void UpdateToLatestEntities(IEnumerable<Tuple<IEntity, EventDefinitionTypeData>> allEntities, IEnumerable<CancellableObjectEventArgs> cancelableArgs)
|
||||
// edits event args to use the latest instance of each entity
|
||||
private static void UpdateToLatestEntities(IEnumerable<Tuple<IEntity, EventDefinitionInfos>> entities, IEnumerable<CancellableObjectEventArgs> args)
|
||||
{
|
||||
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
|
||||
|
||||
// get the latest entities
|
||||
// ordered hash set + keepOldest will keep the latest inserted entity (in case of duplicates)
|
||||
var latestEntities = new OrderedHashSet<IEntity>(keepOldest: true);
|
||||
foreach (var entity in allEntities.OrderByDescending(entity => entity.Item1.UpdateDate))
|
||||
{
|
||||
foreach (var entity in entities.OrderByDescending(entity => entity.Item1.UpdateDate))
|
||||
latestEntities.Add(entity.Item1);
|
||||
}
|
||||
|
||||
foreach (var args in cancelableArgs)
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
|
||||
if (list == null)
|
||||
// event object can either be a single object or an enumerable of objects
|
||||
// try to get as an enumerable, get null if it's not
|
||||
var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(arg.EventObject);
|
||||
if (eventObjects == null)
|
||||
{
|
||||
//try to find the args entity in the latest entity - based on the equality operators, this will
|
||||
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
|
||||
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
|
||||
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, args.EventObject));
|
||||
// single object
|
||||
// look for a more recent entity for that object, and replace if any
|
||||
// works by "equalling" entities ie the more recent one "equals" this one (though different object)
|
||||
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, arg.EventObject));
|
||||
if (foundEntity != null)
|
||||
{
|
||||
args.EventObject = foundEntity;
|
||||
}
|
||||
arg.EventObject = foundEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// enumerable of objects
|
||||
// same as above but for each object
|
||||
var updated = false;
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
for (var i = 0; i < eventObjects.Count; i++)
|
||||
{
|
||||
//try to find the args entity in the latest entity - based on the equality operators, this will
|
||||
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
|
||||
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
|
||||
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, list[i]));
|
||||
if (foundEntity != null)
|
||||
{
|
||||
list[i] = foundEntity;
|
||||
updated = true;
|
||||
}
|
||||
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, eventObjects[i]));
|
||||
if (foundEntity == null) continue;
|
||||
eventObjects[i] = foundEntity;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated)
|
||||
{
|
||||
args.EventObject = list;
|
||||
}
|
||||
arg.EventObject = eventObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will check against all of the processed entity/events (allEntities) to see if this entity already exists in
|
||||
/// event args that supersede the event args being passed in and if so returns true.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="eventDef"></param>
|
||||
/// <param name="allEntities"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsFiltered(
|
||||
IEntity entity,
|
||||
EventDefinitionTypeData eventDef,
|
||||
List<Tuple<IEntity, EventDefinitionTypeData>> allEntities)
|
||||
// determines if a given entity, appearing in a given event definition, should be filtered out,
|
||||
// considering the entities that have already been visited - an entity is filtered out if it
|
||||
// appears in another even definition, which superceedes this event definition.
|
||||
private static bool IsSuperceeded(IEntity entity, EventDefinitionInfos infos, List<Tuple<IEntity, EventDefinitionInfos>> entities)
|
||||
{
|
||||
var argType = eventDef.EventDefinition.Args.GetType();
|
||||
//var argType = meta.EventArgsType;
|
||||
var argType = infos.EventDefinition.Args.GetType();
|
||||
|
||||
//check if the entity is found in any processed event data that could possible supersede this one
|
||||
var foundByEntity = allEntities
|
||||
.Where(x => x.Item2.SupersedeAttributes.Length > 0
|
||||
//if it's the same arg type than it cannot supersede
|
||||
&& x.Item2.EventArgType != argType
|
||||
&& Equals(x.Item1, entity))
|
||||
// look for other instances of the same entity, coming from an event args that supercedes other event args,
|
||||
// ie is marked with the attribute, and is not this event args (cannot supersede itself)
|
||||
var superceeding = entities
|
||||
.Where(x => x.Item2.SupersedeTypes.Length > 0 // has the attribute
|
||||
&& x.Item2.EventDefinition.Args.GetType() != argType // is not the same
|
||||
&& Equals(x.Item1, entity)) // same entity
|
||||
.ToArray();
|
||||
|
||||
//no args have been processed with this entity so it should not be filtered
|
||||
if (foundByEntity.Length == 0)
|
||||
// first time we see this entity = not filtered
|
||||
if (superceeding.Length == 0)
|
||||
return false;
|
||||
|
||||
// fixme see notes above
|
||||
// delete event args does NOT superceedes 'unpublished' event
|
||||
if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs<>) && infos.EventDefinition.EventName == "UnPublished")
|
||||
return false;
|
||||
|
||||
// found occurences, need to determine if this event args is superceded
|
||||
if (argType.IsGenericType)
|
||||
{
|
||||
var supercededBy = foundByEntity
|
||||
.FirstOrDefault(x =>
|
||||
x.Item2.SupersedeAttributes.Any(y =>
|
||||
//if the attribute type is a generic type def then compare with the generic type def of the event arg
|
||||
(y.SupersededEventArgsType.IsGenericTypeDefinition && y.SupersededEventArgsType == argType.GetGenericTypeDefinition())
|
||||
//if the attribute type is not a generic type def then compare with the normal type of the event arg
|
||||
|| (y.SupersededEventArgsType.IsGenericTypeDefinition == false && y.SupersededEventArgsType == argType)));
|
||||
// generic, must compare type arguments
|
||||
var supercededBy = superceeding.FirstOrDefault(x =>
|
||||
x.Item2.SupersedeTypes.Any(y =>
|
||||
// superceeding a generic type which has the same generic type definition
|
||||
// fixme no matter the generic type parameters? could be different?
|
||||
y.IsGenericTypeDefinition && y == argType.GetGenericTypeDefinition()
|
||||
// or superceeding a non-generic type which is ... fixme how is this ever possible? argType *is* generic?
|
||||
|| y.IsGenericTypeDefinition == false && y == argType));
|
||||
return supercededBy != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var supercededBy = foundByEntity
|
||||
.FirstOrDefault(x =>
|
||||
x.Item2.SupersedeAttributes.Any(y =>
|
||||
//since the event arg type is not a generic type, then we just compare type 1:1
|
||||
y.SupersededEventArgsType == argType));
|
||||
// non-generic, can compare types 1:1
|
||||
var supercededBy = superceeding.FirstOrDefault(x =>
|
||||
x.Item2.SupersedeTypes.Any(y => y == argType));
|
||||
return supercededBy != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core
|
||||
/// 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
|
||||
internal class HashGenerator : DisposableObjectSlim
|
||||
{
|
||||
public HashGenerator()
|
||||
{
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace Umbraco.Core
|
||||
var ipAddress = httpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
|
||||
|
||||
if (string.IsNullOrEmpty(ipAddress))
|
||||
return httpContext.Request.ServerVariables["REMOTE_ADDR"];
|
||||
return httpContext.Request.UserHostAddress;
|
||||
|
||||
var addresses = ipAddress.Split(',');
|
||||
if (addresses.Length != 0)
|
||||
return addresses[0];
|
||||
|
||||
return httpContext.Request.ServerVariables["REMOTE_ADDR"];
|
||||
return httpContext.Request.UserHostAddress;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
|
||||
@@ -13,11 +13,28 @@ using Umbraco.Core.Configuration;
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
public static class IOHelper
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value forcing Umbraco to consider it is non-hosted.
|
||||
/// </summary>
|
||||
/// <remarks>This should always be false, unless unit testing.</remarks>
|
||||
public static bool ForceNotHosted { get; set; }
|
||||
|
||||
private static string _rootDir = "";
|
||||
|
||||
// static compiled regex for faster performance
|
||||
private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Umbraco is hosted.
|
||||
/// </summary>
|
||||
public static bool IsHosted
|
||||
{
|
||||
get
|
||||
{
|
||||
return ForceNotHosted == false && (HttpContext.Current != null || HostingEnvironment.IsHosted);
|
||||
}
|
||||
}
|
||||
|
||||
public static char DirSepChar
|
||||
{
|
||||
@@ -72,14 +89,14 @@ namespace Umbraco.Core.IO
|
||||
internal static string ResolveUrlsFromTextString(string text)
|
||||
{
|
||||
if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString)
|
||||
{
|
||||
{
|
||||
using (DisposableTimer.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete"))
|
||||
{
|
||||
// find all relative urls (ie. urls that contain ~)
|
||||
var tags = ResolveUrlPattern.Matches(text);
|
||||
|
||||
|
||||
foreach (Match tag in tags)
|
||||
{
|
||||
{
|
||||
string url = "";
|
||||
if (tag.Groups[1].Success)
|
||||
url = tag.Groups[1].Value;
|
||||
@@ -97,7 +114,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static string MapPath(string path, bool useHttpContext)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException("path");
|
||||
if (path == null) throw new ArgumentNullException("path");
|
||||
useHttpContext = useHttpContext && IsHosted;
|
||||
|
||||
// Check if the path is already mapped
|
||||
if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar)
|
||||
@@ -303,7 +321,7 @@ namespace Umbraco.Core.IO
|
||||
var debugFolder = Path.Combine(binFolder, "debug");
|
||||
if (Directory.Exists(debugFolder))
|
||||
return debugFolder;
|
||||
#endif
|
||||
#endif
|
||||
var releaseFolder = Path.Combine(binFolder, "release");
|
||||
if (Directory.Exists(releaseFolder))
|
||||
return releaseFolder;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
@@ -107,7 +108,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(fullPath, recursive);
|
||||
WithRetry(() => Directory.Delete(fullPath, recursive));
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
@@ -225,8 +226,8 @@ namespace Umbraco.Core.IO
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
{
|
||||
WithRetry(() => File.Delete(fullPath));
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
@@ -378,7 +379,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (overrideIfExists == false)
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
File.Delete(fullPath);
|
||||
WithRetry(() => File.Delete(fullPath));
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(fullPath);
|
||||
@@ -386,9 +387,9 @@ namespace Umbraco.Core.IO
|
||||
Directory.CreateDirectory(directory); // ensure it exists
|
||||
|
||||
if (copy)
|
||||
File.Copy(physicalPath, fullPath);
|
||||
WithRetry(() => File.Copy(physicalPath, fullPath));
|
||||
else
|
||||
File.Move(physicalPath, fullPath);
|
||||
WithRetry(() => File.Move(physicalPath, fullPath));
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
@@ -417,6 +418,35 @@ namespace Umbraco.Core.IO
|
||||
return path;
|
||||
}
|
||||
|
||||
protected void WithRetry(Action action)
|
||||
{
|
||||
// 10 times 100ms is 1s
|
||||
const int count = 10;
|
||||
const int pausems = 100;
|
||||
|
||||
for (var i = 0;; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
break; // done
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// if it's not *exactly* IOException then it could be
|
||||
// some inherited exception such as FileNotFoundException,
|
||||
// and then we don't want to retry
|
||||
if (e.GetType() != typeof(IOException)) throw;
|
||||
|
||||
// if we have tried enough, throw, else swallow
|
||||
// the exception and retry after a pause
|
||||
if (i == count) throw;
|
||||
}
|
||||
|
||||
Thread.Sleep(pausems);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using log4net.Appender;
|
||||
using log4net.Util;
|
||||
|
||||
namespace Umbraco.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// This class will do the exact same thing as the RollingFileAppender that comes from log4net
|
||||
/// With the extension, that it is able to do automatic cleanup of the logfiles in the directory where logging happens
|
||||
///
|
||||
/// By specifying the properties MaxLogFileDays and BaseFilePattern, the files will automaticly get deleted when
|
||||
/// the logger is configured(typically when the app starts). To utilize this appender swap out the type of the rollingFile appender
|
||||
/// that ships with Umbraco, to be Umbraco.Core.Logging.RollingFileCleanupAppender, and add the maxLogFileDays and baseFilePattern elements
|
||||
/// to the configuration i.e.:
|
||||
///
|
||||
/// <example>
|
||||
/// <appender name="rollingFile" type="Log4netAwesomeness.CustomRollingFileAppender, Log4netAwesomeness">
|
||||
/// <file type="log4net.Util.PatternString" value="App_Data\Logs\UmbracoTraceLog.%property{log4net:HostName}.txt" />
|
||||
/// <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
|
||||
/// <appendToFile value="true" />
|
||||
/// <rollingStyle value="Date" />
|
||||
/// <maximumFileSize value="5MB" />
|
||||
/// <maxLogFileDays value="5"/>
|
||||
/// <basefilePattern value="UmbracoTraceLog.*.txt.*"/>
|
||||
/// <layout type="log4net.Layout.PatternLayout">
|
||||
/// <conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
|
||||
/// </layout>
|
||||
/// <layout type="log4net.Layout.PatternLayout">
|
||||
/// <conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
|
||||
/// </layout>
|
||||
/// <encoding value="utf-8" />
|
||||
/// </appender>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public class RollingFileCleanupAppender : RollingFileAppender
|
||||
{
|
||||
public int MaxLogFileDays { get; set; }
|
||||
public string BaseFilePattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This override will delete logs older than the specified amount of days
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="append"></param>
|
||||
protected override void OpenFile(string fileName, bool append)
|
||||
{
|
||||
bool cleanup = true;
|
||||
// Validate settings and input
|
||||
if (MaxLogFileDays <= 0)
|
||||
{
|
||||
LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'MaxLogFileDays' needs to be a positive integer, aborting cleanup");
|
||||
cleanup = false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BaseFilePattern))
|
||||
{
|
||||
LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'BaseFilePattern' is empty, aborting cleanup");
|
||||
cleanup = false;
|
||||
}
|
||||
// grab the directory we are logging to, as this is were we will search for older logfiles
|
||||
var logFolder = Path.GetDirectoryName(fileName);
|
||||
if (Directory.Exists(logFolder) == false)
|
||||
{
|
||||
LogLog.Warn(typeof(RollingFileCleanupAppender), string.Format("Directory '{0}' for logfiles does not exist, aborting cleanup", logFolder));
|
||||
cleanup = false;
|
||||
}
|
||||
// If everything is validated, we can do the actual cleanup
|
||||
if (cleanup)
|
||||
{
|
||||
Cleanup(logFolder);
|
||||
}
|
||||
|
||||
base.OpenFile(fileName, append);
|
||||
}
|
||||
|
||||
private void Cleanup(string directoryPath)
|
||||
{
|
||||
// only take files that matches the pattern we are using i.e. UmbracoTraceLog.*.txt.*
|
||||
string[] logFiles = Directory.GetFiles(directoryPath, BaseFilePattern);
|
||||
LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Found {0} files that matches the baseFilePattern: '{1}'", logFiles.Length, BaseFilePattern));
|
||||
|
||||
foreach (var logFile in logFiles)
|
||||
{
|
||||
DateTime lastAccessTime = System.IO.File.GetLastWriteTimeUtc(logFile);
|
||||
// take the value from the config file
|
||||
if (lastAccessTime < DateTime.Now.AddDays(-MaxLogFileDays))
|
||||
{
|
||||
LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Deleting file {0} as its lastAccessTime is older than {1} days speficied by MaxLogFileDays", logFile, MaxLogFileDays));
|
||||
base.DeleteFile(logFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
internal class ManifestWatcher : DisposableObject
|
||||
internal class ManifestWatcher : DisposableObjectSlim
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<FileSystemWatcher> _fws = new List<FileSystemWatcher>();
|
||||
|
||||
@@ -2,18 +2,30 @@
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public sealed class AuditItem : Entity, IAggregateRoot
|
||||
public sealed class AuditItem : Entity, IAuditItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for creating an item to be created
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <param name="comment"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="userId"></param>
|
||||
public AuditItem(int objectId, string comment, AuditType type, int userId)
|
||||
{
|
||||
DisableChangeTracking();
|
||||
|
||||
Id = objectId;
|
||||
Comment = comment;
|
||||
AuditType = type;
|
||||
UserId = userId;
|
||||
|
||||
EnableChangeTracking();
|
||||
}
|
||||
|
||||
public string Comment { get; private set; }
|
||||
public AuditType AuditType { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -95,8 +95,10 @@ namespace Umbraco.Core.Models
|
||||
get { return _allowedTemplates; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector,
|
||||
Ps.Value.TemplateComparer);
|
||||
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector, Ps.Value.TemplateComparer);
|
||||
|
||||
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
|
||||
DefaultTemplateId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IAuditItem : IAggregateRoot
|
||||
{
|
||||
string Comment { get; }
|
||||
AuditType AuditType { get; }
|
||||
int UserId { get; }
|
||||
}
|
||||
}
|
||||
@@ -40,19 +40,6 @@ namespace Umbraco.Core.Models.Identity
|
||||
identityUser.EnableChangeTracking();
|
||||
});
|
||||
|
||||
config.CreateMap<BackOfficeIdentityUser, UserData>()
|
||||
.ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
|
||||
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Roles.Select(x => x.RoleId).ToArray()))
|
||||
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
|
||||
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
|
||||
.ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.CalculatedContentStartNodeIds))
|
||||
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
|
||||
.ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.CalculatedMediaStartNodeIds))
|
||||
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
|
||||
.ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
|
||||
}
|
||||
|
||||
private string GetPasswordHash(string storedPass)
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// <summary>
|
||||
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
|
||||
/// </summary>
|
||||
string Avatar { get; set; }
|
||||
string Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A Json blob stored for recording tour data for a user
|
||||
/// </summary>
|
||||
string TourData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
private string _name;
|
||||
private string _securityStamp;
|
||||
private string _avatar;
|
||||
private string _tourData;
|
||||
private int _sessionTimeout;
|
||||
private int[] _startContentIds;
|
||||
private int[] _startMediaIds;
|
||||
@@ -132,6 +133,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
public readonly PropertyInfo SecurityStampSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.SecurityStamp);
|
||||
public readonly PropertyInfo AvatarSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Avatar);
|
||||
public readonly PropertyInfo TourDataSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.TourData);
|
||||
public readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
|
||||
public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartContentIds);
|
||||
public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartMediaIds);
|
||||
@@ -464,8 +466,18 @@ namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
get { return _avatar; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _avatar, Ps.Value.AvatarSelector); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Json blob stored for recording tour data for a user
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string TourData
|
||||
{
|
||||
get { return _tourData; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _tourData, Ps.Value.TourDataSelector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session timeout.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,5 +27,10 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Length(500)]
|
||||
public string OriginIdentity { get; set; }
|
||||
|
||||
[Column("instructionCount")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Constraint(Default = 1)]
|
||||
public int InstructionCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -24,4 +24,5 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[ResultColumn]
|
||||
public NodeDto NodeDto { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,6 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("logComment")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(4000)]
|
||||
public string Comment { get; set; }
|
||||
public string Comment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[TableName("cmsMedia")]
|
||||
[PrimaryKey("versionId", autoIncrement = false)]
|
||||
[ExplicitColumns]
|
||||
internal class MediaDto
|
||||
{
|
||||
[Column("nodeId")]
|
||||
[ForeignKey(typeof(ContentDto), Column = "nodeId")]
|
||||
[ForeignKey(typeof(NodeDto))]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsMedia", ForColumns = "nodeId, versionId, mediaPath")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("versionId")]
|
||||
[PrimaryKeyColumn(AutoIncrement = false)]
|
||||
public Guid VersionId { get; set; }
|
||||
|
||||
[Column("mediaPath")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public string MediaPath { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
public ContentVersionDto ContentVersionDto { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[ForeignKey(typeof(PropertyTypeGroupDto))]
|
||||
public int? PropertyTypeGroupId { get; set; }
|
||||
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyTypeAlias")]
|
||||
[Column("Alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
UserGroupDtos = new List<UserGroupDto>();
|
||||
UserStartNodeDtos = new HashSet<UserStartNodeDto>();
|
||||
}
|
||||
}
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(Name = "PK_user")]
|
||||
@@ -40,7 +40,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("userPassword")]
|
||||
[Length(500)]
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This will represent a JSON structure of how the password has been created (i.e hash algorithm, iterations)
|
||||
/// </summary>
|
||||
@@ -95,7 +95,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Constraint(Default = SystemMethods.CurrentDateTime)]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
|
||||
/// </summary>
|
||||
@@ -103,6 +103,14 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(500)]
|
||||
public string Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A Json blob stored for recording tour data for a user
|
||||
/// </summary>
|
||||
[Column("tourData")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[SpecialDbType(SpecialDbTypes.NTEXT)]
|
||||
public string TourData { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
public List<UserGroupDto> UserGroupDtos { get; set; }
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[TableName("umbracoUserLogin")]
|
||||
[PrimaryKey("sessionId", autoIncrement = false)]
|
||||
[ExplicitColumns]
|
||||
internal class UserLoginDto
|
||||
{
|
||||
[Column("sessionId")]
|
||||
[PrimaryKeyColumn(AutoIncrement = false)]
|
||||
public Guid SessionId { get; set; }
|
||||
|
||||
[Column("userId")]
|
||||
[ForeignKey(typeof(UserDto), Name = "FK_umbracoUserLogin_umbracoUser_id")]
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tracks when the session is created
|
||||
/// </summary>
|
||||
[Column("loggedInUtc")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
public DateTime LoggedInUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updated every time a user's session is validated
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This allows us to guess if a session is timed out if a user doesn't actively log out
|
||||
/// and also allows us to trim the data in the table
|
||||
/// </remarks>
|
||||
[Column("lastValidatedUtc")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
public DateTime LastValidatedUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tracks when the session is removed when the user's account is logged out
|
||||
/// </summary>
|
||||
[Column("loggedOutUtc")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public DateTime? LoggedOutUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logs the IP address of the session if available
|
||||
/// </summary>
|
||||
[Column("ipAddress")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public string IpAddress { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[Obsolete("Table no longer exists as of 7.6 - retained only to support migrations from previous versions")]
|
||||
[Obsolete("Table no longer exists as of 7.7 - retained only to support migrations from previous versions")]
|
||||
[TableName("umbracoUserType")]
|
||||
[PrimaryKey("id")]
|
||||
[ExplicitColumns]
|
||||
|
||||
@@ -188,11 +188,11 @@ namespace Umbraco.Core.Models
|
||||
Language,
|
||||
|
||||
/// <summary>
|
||||
/// Document
|
||||
/// Document Blueprint
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentBlueprint, typeof(IContent))]
|
||||
[FriendlyName("DocumentBlueprint")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentBlueprint)]
|
||||
DocumentBlueprint,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,12 +30,11 @@ namespace Umbraco.Core.Models
|
||||
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="userService"></param>
|
||||
/// <param name="staticCache"></param>
|
||||
/// <returns>
|
||||
/// A list of 5 different sized avatar URLs
|
||||
/// </returns>
|
||||
internal static string[] GetCurrentUserAvatarUrls(this IUser user, IUserService userService, ICacheProvider staticCache)
|
||||
internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache)
|
||||
{
|
||||
//check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
|
||||
if (user.Avatar == "none")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -394,7 +394,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances
|
||||
/// created. Typically only used if a resolver is no longer used in an application and memory is to be GC'd
|
||||
/// </summary>
|
||||
internal void ResetCollections()
|
||||
internal virtual void ResetCollections()
|
||||
{
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
@@ -14,17 +14,17 @@ namespace Umbraco.Core.Packaging
|
||||
bool fileFound = false;
|
||||
string foundDir = null;
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
ReadZipfileEntries(packageFilePath, entry =>
|
||||
{
|
||||
string fileName = Path.GetFileName(entry.Name);
|
||||
|
||||
if (string.IsNullOrEmpty(fileName) == false &&
|
||||
fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (string.IsNullOrEmpty(fileName) == false && fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
|
||||
foundDir = entry.Name.Substring(0, entry.Name.Length - fileName.Length);
|
||||
fileFound = true;
|
||||
using (var reader = new StreamReader(stream))
|
||||
using (var entryStream = entry.Open())
|
||||
using (var reader = new StreamReader(entryStream))
|
||||
{
|
||||
retVal = reader.ReadToEnd();
|
||||
return false;
|
||||
@@ -77,7 +77,7 @@ namespace Umbraco.Core.Packaging
|
||||
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
ReadZipfileEntries(packageFilePath, entry =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
if (fileName == string.Empty) { return true; }
|
||||
@@ -86,8 +86,9 @@ namespace Umbraco.Core.Packaging
|
||||
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
|
||||
{
|
||||
using (var streamWriter = File.Open(destination, FileMode.Create))
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(streamWriter);
|
||||
entryStream.CopyTo(streamWriter);
|
||||
}
|
||||
|
||||
d.Remove(fileName);
|
||||
@@ -106,7 +107,7 @@ namespace Umbraco.Core.Packaging
|
||||
{
|
||||
var retVal = expectedFiles.ToList();
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (zipEntry, stream) =>
|
||||
ReadZipfileEntries(packageFilePath, zipEntry =>
|
||||
{
|
||||
string fileName = Path.GetFileName(zipEntry.Name);
|
||||
|
||||
@@ -125,7 +126,7 @@ namespace Umbraco.Core.Packaging
|
||||
var dictionary = new Dictionary<string, List<string>>();
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
ReadZipfileEntries(packageFilePath, entry =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
|
||||
@@ -148,53 +149,42 @@ namespace Umbraco.Core.Packaging
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
var files = new HashSet<string>(filesToGet.Select(f => f.ToLower()));
|
||||
var files = new HashSet<string>(filesToGet.Select(f => f.ToLowerInvariant()));
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
using (var zipArchive = new ZipArchive(fs))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
foreach (var zipEntry in zipArchive.Entries)
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
if (zipEntry.IsDirectory) continue;
|
||||
if (zipEntry.Name.IsNullOrWhiteSpace() && zipEntry.FullName.EndsWith("/")) continue;
|
||||
|
||||
if (files.Contains(zipEntry.Name))
|
||||
if (files.Contains(zipEntry.Name.ToLowerInvariant()))
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
using (var entryStream = zipEntry.Open())
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
zipInputStream.CopyTo(memStream);
|
||||
yield return memStream.ToArray();
|
||||
memStream.Close();
|
||||
}
|
||||
entryStream.CopyTo(memStream);
|
||||
memStream.Close();
|
||||
yield return memStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadZipfileEntries(string packageFilePath, Func<ZipEntry, ZipInputStream, bool> entryFunc, bool skipsDirectories = true)
|
||||
private void ReadZipfileEntries(string packageFilePath, Func<ZipArchiveEntry, bool> entryFunc, bool skipsDirectories = true)
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
using (var zipArchive = new ZipArchive(fs))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
foreach (var zipEntry in zipArchive.Entries)
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (zipEntry.IsDirectory && skipsDirectories) continue;
|
||||
if (entryFunc(zipEntry, zipInputStream) == false) break;
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
if (zipEntry.Name.IsNullOrWhiteSpace() && zipEntry.FullName.EndsWith("/") && skipsDirectories)
|
||||
continue;
|
||||
if (entryFunc(zipEntry) == false) break;
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// it will create one per context, otherwise it will be a global singleton object which is NOT thread safe
|
||||
/// since we need (at least) a new instance of the database object per thread.
|
||||
/// </remarks>
|
||||
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory2
|
||||
internal class DefaultDatabaseFactory : DisposableObjectSlim, IDatabaseFactory2
|
||||
{
|
||||
private readonly string _connectionStringName;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
@@ -98,18 +98,18 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
//NOTE Currently doesn't add Alias (legacy that eventually will go away)
|
||||
var documentDto = new DocumentDto
|
||||
{
|
||||
Newest = true,
|
||||
NodeId = entity.Id,
|
||||
Published = entity.Published,
|
||||
Text = entity.Name,
|
||||
UpdateDate = entity.UpdateDate,
|
||||
WriterUserId = entity.WriterId,
|
||||
VersionId = entity.Version,
|
||||
ExpiresDate = null,
|
||||
ReleaseDate = null,
|
||||
ContentVersionDto = BuildContentVersionDto(entity)
|
||||
};
|
||||
{
|
||||
Newest = true,
|
||||
NodeId = entity.Id,
|
||||
Published = entity.Published,
|
||||
Text = entity.Name,
|
||||
UpdateDate = entity.UpdateDate,
|
||||
WriterUserId = entity.WriterId,
|
||||
VersionId = entity.Version,
|
||||
ExpiresDate = null,
|
||||
ReleaseDate = null,
|
||||
ContentVersionDto = BuildContentVersionDto(entity)
|
||||
};
|
||||
|
||||
if (entity.Template != null && entity.Template.Id > 0)
|
||||
documentDto.TemplateId = entity.Template.Id;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
|
||||
internal class MacroFactory
|
||||
{
|
||||
public IMacro BuildEntity(MacroDto dto)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
@@ -9,20 +10,17 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
private readonly IMediaType _contentType;
|
||||
private readonly Guid _nodeObjectTypeId;
|
||||
private readonly int _id;
|
||||
private int _primaryKey;
|
||||
|
||||
public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId, int id)
|
||||
public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId)
|
||||
{
|
||||
_contentType = contentType;
|
||||
_nodeObjectTypeId = nodeObjectTypeId;
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public MediaFactory(Guid nodeObjectTypeId, int id)
|
||||
public MediaFactory(Guid nodeObjectTypeId)
|
||||
{
|
||||
_nodeObjectTypeId = nodeObjectTypeId;
|
||||
_id = id;
|
||||
}
|
||||
|
||||
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
|
||||
@@ -64,15 +62,27 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
return BuildEntity(dto, _contentType);
|
||||
}
|
||||
|
||||
public ContentVersionDto BuildDto(IMedia entity)
|
||||
public MediaDto BuildDto(IMedia entity)
|
||||
{
|
||||
var dto = new ContentVersionDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
VersionDate = entity.UpdateDate,
|
||||
VersionId = entity.Version,
|
||||
ContentDto = BuildContentDto(entity)
|
||||
};
|
||||
var versionDto = new ContentVersionDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
VersionDate = entity.UpdateDate,
|
||||
VersionId = entity.Version,
|
||||
ContentDto = BuildContentDto(entity)
|
||||
};
|
||||
|
||||
//Extract the media path for storage
|
||||
string mediaPath;
|
||||
TryMatch(entity.GetValue<string>("umbracoFile"), out mediaPath);
|
||||
|
||||
var dto = new MediaDto()
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
ContentVersionDto = versionDto,
|
||||
MediaPath = mediaPath,
|
||||
VersionId = entity.Version
|
||||
};
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -119,5 +129,30 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
return nodeDto;
|
||||
}
|
||||
|
||||
private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Try getting a media path out of the string being stored for media
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="mediaPath"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool TryMatch(string text, out string mediaPath)
|
||||
{
|
||||
mediaPath = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return false;
|
||||
|
||||
var match = MediaPathPattern.Match(text);
|
||||
if (match.Success == false || match.Groups.Count != 2)
|
||||
return false;
|
||||
|
||||
|
||||
var url = match.Groups[1].Value;
|
||||
mediaPath = url;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
user.Avatar = dto.Avatar;
|
||||
user.EmailConfirmedDate = dto.EmailConfirmedDate;
|
||||
user.InvitedDate = dto.InvitedDate;
|
||||
user.TourData = dto.TourData;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
@@ -69,7 +70,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
UpdateDate = entity.UpdateDate,
|
||||
Avatar = entity.Avatar,
|
||||
EmailConfirmedDate = entity.EmailConfirmedDate,
|
||||
InvitedDate = entity.InvitedDate
|
||||
InvitedDate = entity.InvitedDate,
|
||||
TourData = entity.TourData
|
||||
};
|
||||
|
||||
foreach (var startNodeId in entity.StartContentIds)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
[MapperFor(typeof(AuditItem))]
|
||||
[MapperFor(typeof(IAuditItem))]
|
||||
public sealed class AuditMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
public AuditMapper(ISqlSyntaxProvider sqlSyntax) : base(sqlSyntax)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
public AuditMapper()
|
||||
{
|
||||
BuildMap();
|
||||
}
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
if (PropertyInfoCache.IsEmpty)
|
||||
{
|
||||
CacheMap<AuditItem, LogDto>(src => src.Id, dto => dto.NodeId);
|
||||
CacheMap<AuditItem, LogDto>(src => src.CreateDate, dto => dto.Datestamp);
|
||||
CacheMap<AuditItem, LogDto>(src => src.UserId, dto => dto.UserId);
|
||||
CacheMap<AuditItem, LogDto>(src => src.AuditType, dto => dto.Header);
|
||||
CacheMap<AuditItem, LogDto>(src => src.Comment, dto => dto.Comment);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -43,27 +43,28 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
{3, typeof (ContentDto)},
|
||||
{4, typeof (ContentVersionDto)},
|
||||
{5, typeof (DocumentDto)},
|
||||
{6, typeof (MediaDto)},
|
||||
|
||||
{6, typeof (ContentTypeTemplateDto)},
|
||||
{7, typeof (DataTypeDto)},
|
||||
{8, typeof (DataTypePreValueDto)},
|
||||
{9, typeof (DictionaryDto)},
|
||||
{10, typeof (LanguageDto)},
|
||||
{11, typeof (LanguageTextDto)},
|
||||
{12, typeof (DomainDto)},
|
||||
{13, typeof (LogDto)},
|
||||
{14, typeof (MacroDto)},
|
||||
{15, typeof (MacroPropertyDto)},
|
||||
{16, typeof (MemberTypeDto)},
|
||||
{17, typeof (MemberDto)},
|
||||
{18, typeof (Member2MemberGroupDto)},
|
||||
{19, typeof (ContentXmlDto)},
|
||||
{20, typeof (PreviewXmlDto)},
|
||||
{21, typeof (PropertyTypeGroupDto)},
|
||||
{22, typeof (PropertyTypeDto)},
|
||||
{23, typeof (PropertyDataDto)},
|
||||
{24, typeof (RelationTypeDto)},
|
||||
{25, typeof (RelationDto)},
|
||||
{7, typeof (ContentTypeTemplateDto)},
|
||||
{8, typeof (DataTypeDto)},
|
||||
{9, typeof (DataTypePreValueDto)},
|
||||
{10, typeof (DictionaryDto)},
|
||||
{11, typeof (LanguageDto)},
|
||||
{12, typeof (LanguageTextDto)},
|
||||
{13, typeof (DomainDto)},
|
||||
{14, typeof (LogDto)},
|
||||
{15, typeof (MacroDto)},
|
||||
{16, typeof (MacroPropertyDto)},
|
||||
{17, typeof (MemberTypeDto)},
|
||||
{18, typeof (MemberDto)},
|
||||
{19, typeof (Member2MemberGroupDto)},
|
||||
{20, typeof (ContentXmlDto)},
|
||||
{21, typeof (PreviewXmlDto)},
|
||||
{22, typeof (PropertyTypeGroupDto)},
|
||||
{23, typeof (PropertyTypeDto)},
|
||||
{24, typeof (PropertyDataDto)},
|
||||
{25, typeof (RelationTypeDto)},
|
||||
{26, typeof (RelationDto)},
|
||||
|
||||
{28, typeof (TagDto)},
|
||||
{29, typeof (TagRelationshipDto)},
|
||||
@@ -91,6 +92,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
{52, typeof (UserGroup2NodePermissionDto) },
|
||||
{53, typeof (UserGroup2AppDto) },
|
||||
{54, typeof (UserStartNodeDto) },
|
||||
{55, typeof (UserLoginDto)}
|
||||
};
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -148,6 +148,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return new Version(7, 6, 0);
|
||||
}
|
||||
|
||||
//if the error is for cmsMedia it must be the previous version to 7.8 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("cmsMedia"))))
|
||||
{
|
||||
return new Version(7, 7, 0);
|
||||
}
|
||||
|
||||
return UmbracoVersion.Current;
|
||||
}
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.8.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddCmsMediaTable : MigrationBase
|
||||
{
|
||||
public AddCmsMediaTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
|
||||
if (tables.InvariantContains("cmsMedia") == false)
|
||||
{
|
||||
Create.Table<MediaDto>();
|
||||
|
||||
MigrateMediaPaths();
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateMediaPaths()
|
||||
{
|
||||
Execute.Code(database =>
|
||||
{
|
||||
//Due to how much data there can be and that this query will result in executing an Index Scan on the cmsPropertyData PK
|
||||
//which will literally iterate over every row in there (assume there's 1,000,000 and you can see why this will perform
|
||||
//horribly) ultimately we need to fix this: http://issues.umbraco.org/issue/U4-10286
|
||||
//so in the meantime I've found an ugly query to work around this Index Scan issue which is to use a sub-query of Ids.
|
||||
//Doing this results in the same Index Scan taking place but it only visits the explicit rows that are provided, not every row
|
||||
//in the cmsPropertyData table. This results in a tremendously faster query when there is tons of data.
|
||||
|
||||
//The will return any media property in nvarchar or ntext that is an umbracoFile that is not null.
|
||||
//This will give us the least amount of data returned to work with.
|
||||
var sql = @"SELECT cmsPropertyData.dataNvarchar, cmsPropertyData.dataNtext, umbracoNode.id, cmsContentVersion.VersionId
|
||||
FROM cmsPropertyData
|
||||
INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid
|
||||
INNER JOIN umbracoNode ON umbracoNode.id = cmsPropertyData.contentNodeId
|
||||
INNER JOIN cmsContentVersion ON cmsContentVersion.ContentId = umbracoNode.id
|
||||
WHERE cmsPropertyType.Alias = (@alias)
|
||||
AND umbracoNode.id IN (SELECT umbracoNode.id
|
||||
FROM umbracoNode
|
||||
INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id
|
||||
INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType
|
||||
INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
|
||||
WHERE cmsPropertyType.Alias = (@alias) AND umbracoNode.nodeObjectType = (@nodeObjectType))
|
||||
AND (cmsPropertyData.dataNvarchar IS NOT NULL OR cmsPropertyData.dataNtext IS NOT NULL)";
|
||||
|
||||
var paths = new List<MediaDto>();
|
||||
|
||||
//using QUERY = a db cursor, we won't load this all into memory first, just row by row
|
||||
foreach (var row in database.Query<dynamic>(sql, new {alias = "umbracoFile", nodeObjectType = Constants.ObjectTypes.Media}))
|
||||
{
|
||||
var id = (int) row.id;
|
||||
var versionId = (Guid)row.VersionId;
|
||||
|
||||
string mediaPath = null;
|
||||
|
||||
//if there's values in dataNvarchar then ensure there's a media path match and extract it
|
||||
if (row.dataNvarchar != null && MediaFactory.TryMatch((string) row.dataNvarchar, out mediaPath))
|
||||
{
|
||||
paths.Add(new MediaDto
|
||||
{
|
||||
MediaPath = mediaPath,
|
||||
NodeId = id,
|
||||
VersionId = versionId
|
||||
});
|
||||
}
|
||||
//if there's values in dataNtext then ensure there's a media path match and extract it
|
||||
else if (row.dataNtext != null && MediaFactory.TryMatch((string) row.dataNtext, out mediaPath))
|
||||
{
|
||||
paths.Add(new MediaDto
|
||||
{
|
||||
MediaPath = mediaPath,
|
||||
NodeId = id,
|
||||
VersionId = versionId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//now we need to insert paths for each item
|
||||
database.BulkInsertRecords(paths, SqlSyntax);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.8.0", 0, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIndexToPropertyTypeAliasColumn : MigrationBase
|
||||
{
|
||||
public AddIndexToPropertyTypeAliasColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
Execute.Code(database =>
|
||||
{
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsPropertyTypeAlias")) == false)
|
||||
{
|
||||
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
|
||||
|
||||
//we can apply the index
|
||||
localContext.Create.Index("IX_cmsPropertyTypeAlias").OnTable("cmsPropertyType")
|
||||
.OnColumn("Alias")
|
||||
.Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered();
|
||||
|
||||
return localContext.GetSql();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_cmsPropertyTypeAlias").OnTable("cmsPropertyType");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.8.0", 2, Constants.System.UmbracoMigrationName)]
|
||||
public class AddInstructionCountColumn : MigrationBase
|
||||
{
|
||||
public AddInstructionCountColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
//Don't exeucte if the column is already there
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoCacheInstruction") && x.ColumnName.InvariantEquals("instructionCount")) == false)
|
||||
Create.Column("instructionCount")
|
||||
.OnTable("umbracoCacheInstruction")
|
||||
.AsInt32()
|
||||
.WithDefaultValue(1)
|
||||
.NotNullable();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.8.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddTourDataUserColumn : MigrationBase
|
||||
{
|
||||
public AddTourDataUserColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
//Don't exeucte if the column is already there
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("tourData")) == false)
|
||||
{
|
||||
var textType = SqlSyntax.GetSpecialDbType(SpecialDbTypes.NTEXT);
|
||||
Create.Column("tourData").OnTable("umbracoUser").AsCustom(textType).Nullable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.8.0", 4, Constants.System.UmbracoMigrationName)]
|
||||
public class AddUserLoginTable : MigrationBase
|
||||
{
|
||||
public AddUserLoginTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
|
||||
if (tables.InvariantContains("umbracoUserLogin") == false)
|
||||
{
|
||||
Create.Table<UserLoginDto>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,7 +402,7 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
|
||||
// Helper to handle named parameters from object properties
|
||||
static Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
|
||||
static readonly Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
|
||||
public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
|
||||
{
|
||||
return rxParams.Replace(_sql, m =>
|
||||
@@ -545,7 +545,7 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
|
||||
// Create a command
|
||||
static Regex rxParamsPrefix = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
|
||||
static readonly Regex rxParamsPrefix = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
|
||||
public IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
|
||||
{
|
||||
// Perform named argument replacements
|
||||
@@ -666,8 +666,8 @@ namespace Umbraco.Core.Persistence
|
||||
return ExecuteScalar<T>(sql.SQL, sql.Arguments);
|
||||
}
|
||||
|
||||
Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL)\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
||||
Regex rxFrom = new Regex(@"\A\s*FROM\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
||||
static readonly Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL)\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
||||
static readonly Regex rxFrom = new Regex(@"\A\s*FROM\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
||||
string AddSelectClause<T>(string sql)
|
||||
{
|
||||
if (sql.StartsWith(";"))
|
||||
@@ -701,9 +701,9 @@ namespace Umbraco.Core.Persistence
|
||||
return Fetch<T>(sql.SQL, sql.Arguments);
|
||||
}
|
||||
|
||||
static Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static readonly Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static readonly Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static readonly Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
|
||||
{
|
||||
sqlSelectRemoved = null;
|
||||
@@ -2546,5 +2546,4 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using Umbraco.Core.Persistence.FaultHandling;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MySql.Data.MySqlClient;
|
||||
using StackExchange.Profiling.Data;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
|
||||
@@ -38,8 +38,12 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
_tableDefinition = DefinitionFactory.GetTableDefinition(sqlSyntaxProvider, pd.type);
|
||||
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.type);
|
||||
|
||||
_readerColumns = pd.Columns.Select(x => x.Value).ToArray();
|
||||
|
||||
//Only return real columns, do not include columns that are result columns
|
||||
_readerColumns = pd.Columns
|
||||
.Where(x => x.Value.ResultColumn == false)
|
||||
.Select(x => x.Value)
|
||||
.ToArray();
|
||||
_sqlSyntaxProvider = sqlSyntaxProvider;
|
||||
_enumerator = dataSource.GetEnumerator();
|
||||
_columnDefinitions = _tableDefinition.Columns.ToArray();
|
||||
|
||||
@@ -1,29 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal class AuditRepository : PetaPocoRepositoryBase<int, AuditItem>, IAuditRepository
|
||||
internal class AuditRepository : PetaPocoRepositoryBase<int, IAuditItem>, IAuditRepository
|
||||
{
|
||||
public AuditRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(AuditItem entity)
|
||||
/// <summary>
|
||||
/// Return the audit items as paged result
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// The query coming from the service
|
||||
/// </param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// A user supplied custom filter
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IAuditItem> GetPagedResultsByQuery(IQuery<IAuditItem> query, long pageIndex, int pageSize,
|
||||
out long totalRecords, Direction orderDirection,
|
||||
AuditType[] auditTypeFilter,
|
||||
IQuery<IAuditItem> customFilter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (auditTypeFilter == null) auditTypeFilter = new AuditType[0];
|
||||
|
||||
var sql = GetBaseQuery(false);
|
||||
|
||||
if (query == null) query = new Query<IAuditItem>();
|
||||
var translatorIds = new SqlTranslator<IAuditItem>(sql, query);
|
||||
var translatedQuery = translatorIds.Translate();
|
||||
|
||||
var customFilterWheres = customFilter != null ? customFilter.GetWhereClauses().ToArray() : null;
|
||||
var hasCustomFilter = customFilterWheres != null && customFilterWheres.Length > 0;
|
||||
if (hasCustomFilter)
|
||||
{
|
||||
var filterSql = new Sql();
|
||||
var first = true;
|
||||
foreach (var filterClaus in customFilterWheres)
|
||||
{
|
||||
if (first == false)
|
||||
{
|
||||
filterSql.Append(" AND ");
|
||||
}
|
||||
filterSql.Append(string.Format("({0})", filterClaus.Item1), filterClaus.Item2);
|
||||
first = false;
|
||||
}
|
||||
|
||||
translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql);
|
||||
}
|
||||
|
||||
if (auditTypeFilter.Length > 0)
|
||||
{
|
||||
var filterSql = new Sql();
|
||||
var first = true;
|
||||
foreach (var filterClaus in auditTypeFilter)
|
||||
{
|
||||
if (first == false || hasCustomFilter)
|
||||
{
|
||||
filterSql.Append(" AND ");
|
||||
}
|
||||
filterSql.Append("(logHeader = @logHeader)", new {logHeader = filterClaus.ToString() });
|
||||
first = false;
|
||||
}
|
||||
|
||||
translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql);
|
||||
}
|
||||
|
||||
if (orderDirection == Direction.Descending)
|
||||
translatedQuery.OrderByDescending("Datestamp");
|
||||
else
|
||||
translatedQuery.OrderBy("Datestamp");
|
||||
|
||||
// Get page of results and total count
|
||||
var pagedResult = Database.Page<LogDto>(pageIndex + 1, pageSize, translatedQuery);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
|
||||
var pages = pagedResult.Items.Select(
|
||||
dto => new AuditItem(dto.Id, dto.Comment, Enum<AuditType>.Parse(dto.Header), dto.UserId)).ToArray();
|
||||
|
||||
//Mapping the DateStamp
|
||||
for (int i = 0; i < pages.Length; i++)
|
||||
{
|
||||
pages[i].CreateDate = pagedResult.Items[i].Datestamp;
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
#region Not Implemented - not needed
|
||||
|
||||
protected override void PersistUpdatedItem(AuditItem entity)
|
||||
protected override void PersistUpdatedItem(IAuditItem entity)
|
||||
{
|
||||
Database.Insert(new LogDto
|
||||
{
|
||||
@@ -35,26 +119,40 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
});
|
||||
}
|
||||
|
||||
protected override AuditItem PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<AuditItem> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<AuditItem> PerformGetByQuery(IQuery<AuditItem> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select(isCount ? "COUNT(*)" : "umbracoLog.id, umbracoLog.userId, umbracoLog.NodeId, umbracoLog.Datestamp, umbracoLog.logHeader, umbracoLog.logComment, umbracoUser.userName, umbracoUser.avatar as userAvatar")
|
||||
.From<LogDto>(SqlSyntax);
|
||||
if (isCount == false)
|
||||
{
|
||||
sql = sql.LeftJoin<UserDto>(SqlSyntax).On<UserDto, LogDto>(SqlSyntax, dto => dto.Id, dto => dto.UserId);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
#region Not Implemented - not needed currently
|
||||
|
||||
protected override void PersistNewItem(IAuditItem entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IAuditItem PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<IAuditItem> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<IAuditItem> PerformGetByQuery(IQuery<IAuditItem> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -71,6 +169,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
#endregion
|
||||
|
||||
private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql)
|
||||
{
|
||||
Sql filteredSql;
|
||||
|
||||
// Apply filter
|
||||
if (filterSql != null)
|
||||
{
|
||||
var sqlFilter = " WHERE " + filterSql.SQL.TrimStart("AND ");
|
||||
|
||||
//NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql
|
||||
// instance a couple of times to get the parameter order correct, for some reason the first
|
||||
// time the arguments don't show up correctly but the SQL argument parameter names are actually updated
|
||||
// accordingly - so we re-create it again. In v8 we don't need to do this and it's already taken care of.
|
||||
|
||||
filteredSql = new Sql(sql.SQL, sql.Arguments);
|
||||
var args = filteredSql.Arguments.Concat(filterSql.Arguments).ToArray();
|
||||
filteredSql = new Sql(
|
||||
string.Format("{0} {1}", filteredSql.SQL, sqlFilter),
|
||||
args);
|
||||
filteredSql = new Sql(filteredSql.SQL, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
//copy to var so that the original isn't changed
|
||||
filteredSql = new Sql(sql.SQL, sql.Arguments);
|
||||
}
|
||||
return filteredSql;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId);
|
||||
//TODO: IF we want to enable querying on content type information this will need to be joined
|
||||
//.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
|
||||
@@ -833,11 +835,21 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
}
|
||||
|
||||
public int CountPublished()
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
public int CountPublished(string contentTypeAlias = null)
|
||||
{
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true)
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
|
||||
@@ -889,7 +901,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
public void AddOrUpdatePermissions(ContentPermissionSet permission)
|
||||
{
|
||||
_permissionRepository.AddOrUpdate(permission);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to remove the content xml for a content item
|
||||
@@ -1063,8 +1075,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
protected override string GetDatabaseFieldNameForOrderBy(string orderBy)
|
||||
{
|
||||
//Some custom ones
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>
|
||||
/// This is limited to objects that are based in the umbracoNode-table.
|
||||
/// </remarks>
|
||||
internal class EntityRepository : DisposableObject, IEntityRepository
|
||||
internal class EntityRepository : DisposableObjectSlim, IEntityRepository
|
||||
{
|
||||
private readonly IDatabaseUnitOfWork _work;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal abstract class FileRepository<TId, TEntity> : DisposableObject, IUnitOfWorkRepository, IRepository<TId, TEntity>
|
||||
internal abstract class FileRepository<TId, TEntity> : DisposableObjectSlim, IUnitOfWorkRepository, IRepository<TId, TEntity>
|
||||
where TEntity : IFile
|
||||
{
|
||||
private IUnitOfWork _work;
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IAuditRepository : IRepository<int, AuditItem>
|
||||
public interface IAuditRepository : IRepository<int, IAuditItem>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Return the audit items as paged result
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// The query coming from the service
|
||||
/// </param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="auditTypeFilter">
|
||||
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
||||
/// so we need to do that here
|
||||
/// </param>
|
||||
/// <param name="customFilter">
|
||||
/// A user supplied custom filter
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IAuditItem> GetPagedResultsByQuery(
|
||||
IQuery<IAuditItem> query,
|
||||
long pageIndex, int pageSize, out long totalRecords,
|
||||
Direction orderDirection,
|
||||
AuditType[] auditTypeFilter,
|
||||
IQuery<IAuditItem> customFilter);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>
|
||||
/// We require this on the repo because the IQuery{IContent} cannot supply the 'newest' parameter
|
||||
/// </remarks>
|
||||
int CountPublished();
|
||||
int CountPublished(string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>, IRecycleBinRepository<IMedia>, IReadRepository<Guid, IMedia>, IDeleteMediaFilesRepository
|
||||
{
|
||||
IMedia GetMediaByPath(string mediaPath);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update published xml for the media item
|
||||
/// </summary>
|
||||
|
||||
@@ -91,7 +91,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
IProfile GetProfile(string username);
|
||||
IProfile GetProfile(int id);
|
||||
IDictionary<UserState, int> GetUserStates();
|
||||
|
||||
IDictionary<UserState, int> GetUserStates();
|
||||
|
||||
Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true);
|
||||
bool ValidateLoginSession(int userId, Guid sessionId);
|
||||
int ClearLoginSessions(int userId);
|
||||
int ClearLoginSessions(TimeSpan timespan);
|
||||
void ClearLoginSession(Guid sessionId);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -87,7 +87,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
|
||||
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
private Sql GetBaseQuery(BaseQueryType queryType, bool includeFilePaths)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsContentVersion.contentId" : "*"))
|
||||
@@ -95,14 +95,26 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax)
|
||||
//TODO: IF we want to enable querying on content type information this will need to be joined
|
||||
//.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
|
||||
if (includeFilePaths)
|
||||
{
|
||||
sql.InnerJoin<MediaDto>(SqlSyntax)
|
||||
.On<MediaDto, ContentVersionDto>(SqlSyntax, left => left.VersionId, right => right.VersionId);
|
||||
}
|
||||
|
||||
//TODO: IF we want to enable querying on content type information this will need to be joined
|
||||
//.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
|
||||
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
return GetBaseQuery(queryType, false);
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
|
||||
@@ -128,6 +140,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsDocument WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
|
||||
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsMedia WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
@@ -367,7 +380,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
|
||||
var factory = new MediaFactory(NodeObjectTypeId);
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
|
||||
@@ -380,7 +393,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sortOrder = maxSortOrder + 1;
|
||||
|
||||
//Create the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentDto.NodeDto;
|
||||
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
|
||||
nodeDto.Path = parent.Path;
|
||||
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
|
||||
nodeDto.SortOrder = sortOrder;
|
||||
@@ -415,15 +428,21 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
entity.Level = level;
|
||||
|
||||
//Create the Content specific data - cmsContent
|
||||
var contentDto = dto.ContentDto;
|
||||
var contentDto = dto.ContentVersionDto.ContentDto;
|
||||
contentDto.NodeId = nodeDto.NodeId;
|
||||
Database.Insert(contentDto);
|
||||
|
||||
//Create the first version - cmsContentVersion
|
||||
//Assumes a new Version guid and Version date (modified date) has been set
|
||||
var contentVersionDto = dto.ContentVersionDto;
|
||||
contentVersionDto.NodeId = nodeDto.NodeId;
|
||||
Database.Insert(contentVersionDto);
|
||||
|
||||
//Create the Media specific data for this version - cmsMedia
|
||||
//Assumes a new Version guid has been generated
|
||||
dto.NodeId = nodeDto.NodeId;
|
||||
Database.Insert(dto);
|
||||
|
||||
|
||||
//Create the PropertyData for this version - cmsPropertyData
|
||||
var propertyFactory = new PropertyFactory(entity.ContentType.CompositionPropertyTypes.ToArray(), entity.Version, entity.Id);
|
||||
var propertyDataDtos = propertyFactory.BuildDto(entity.Properties);
|
||||
@@ -471,14 +490,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
entity.SortOrder = maxSortOrder + 1;
|
||||
}
|
||||
|
||||
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
|
||||
var factory = new MediaFactory(NodeObjectTypeId);
|
||||
//Look up Content entry to get Primary for updating the DTO
|
||||
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||
var contentDto = Database.First<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||
factory.SetPrimaryKey(contentDto.PrimaryKey);
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
//Updates the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentDto.NodeDto;
|
||||
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
|
||||
nodeDto.ValidatePathWithException();
|
||||
var o = Database.Update(nodeDto);
|
||||
|
||||
@@ -486,15 +505,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (contentDto.ContentTypeId != entity.ContentTypeId)
|
||||
{
|
||||
//Create the Content specific data - cmsContent
|
||||
var newContentDto = dto.ContentDto;
|
||||
var newContentDto = dto.ContentVersionDto.ContentDto;
|
||||
Database.Update(newContentDto);
|
||||
}
|
||||
|
||||
//In order to update the ContentVersion we need to retrieve its primary key id
|
||||
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
|
||||
dto.Id = contentVerDto.Id;
|
||||
var contentVerDto = Database.First<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
|
||||
dto.ContentVersionDto.Id = contentVerDto.Id;
|
||||
//Updates the current version - cmsContentVersion
|
||||
//Assumes a Version guid exists and Version date (modified date) has been set/updated
|
||||
Database.Update(dto.ContentVersionDto);
|
||||
|
||||
//now update the media entry
|
||||
Database.Update(dto);
|
||||
|
||||
//Create the PropertyData for this version - cmsPropertyData
|
||||
@@ -639,7 +661,39 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMedia"/> object from the path stored in the for the media item.
|
||||
/// </summary>
|
||||
/// <param name="mediaPath">Path of the media item to retrieve (for example: /media/1024/koala_403x328.jpg)</param>
|
||||
/// <returns><see cref="IMedia"/></returns>
|
||||
public IMedia GetMediaByPath(string mediaPath)
|
||||
{
|
||||
var umbracoFileValue = mediaPath;
|
||||
|
||||
const string pattern = ".*[_][0-9]+[x][0-9]+[.].*";
|
||||
var isResized = Regex.IsMatch(mediaPath, pattern);
|
||||
|
||||
// If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url.
|
||||
if (isResized)
|
||||
{
|
||||
var underscoreIndex = mediaPath.LastIndexOf('_');
|
||||
var dotIndex = mediaPath.LastIndexOf('.');
|
||||
umbracoFileValue = string.Concat(mediaPath.Substring(0, underscoreIndex), mediaPath.Substring(dotIndex));
|
||||
}
|
||||
|
||||
var sql = GetBaseQuery(BaseQueryType.FullSingle, true);
|
||||
sql.Where<MediaDto>(mediaDto => mediaDto.MediaPath == umbracoFileValue, SqlSyntax);
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
var content = CreateMediaFromDto(dto, sql);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged media results
|
||||
@@ -703,8 +757,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (EnsureUniqueNaming == false)
|
||||
return nodeName;
|
||||
|
||||
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
|
||||
new { objectType = NodeObjectTypeId, parentId });
|
||||
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
|
||||
new { objectType = NodeObjectTypeId, parentId });
|
||||
|
||||
return SimilarNodeName.GetUniqueName(names, id, nodeName);
|
||||
}
|
||||
|
||||
@@ -183,17 +183,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
|
||||
{
|
||||
var sql = new Sql()
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("un.*")
|
||||
.From("umbracoNode AS un")
|
||||
.InnerJoin("cmsMember2MemberGroup")
|
||||
.On("un.id = cmsMember2MemberGroup.MemberGroup")
|
||||
.LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
|
||||
.On("member.id = cmsMember2MemberGroup.Member")
|
||||
.Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
|
||||
.Where("member.LoginName=@loginName", new {loginName = username});
|
||||
|
||||
.On("cmsMember2MemberGroup.MemberGroup = un.id")
|
||||
.InnerJoin("cmsMember")
|
||||
.On("cmsMember.nodeId = cmsMember2MemberGroup.Member")
|
||||
.Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId })
|
||||
.Where("cmsMember.LoginName=@loginName", new { loginName = username });
|
||||
|
||||
return Database.Fetch<NodeDto>(sql)
|
||||
.DistinctBy(dto => dto.NodeId)
|
||||
.Select(x => _modelFactory.BuildEntity(x));
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
@@ -53,15 +48,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
@"DELETE FROM umbracoRedirectUrl WHERE umbracoRedirectUrl.id IN(
|
||||
SELECT TB1.id FROM umbracoRedirectUrl as TB1
|
||||
INNER JOIN umbracoNode as TB2 ON TB1.contentKey = TB2.uniqueId
|
||||
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
FormatDeleteStatement("umbracoUserStartNode", "startNode"),
|
||||
FormatUpdateStatement("umbracoUserGroup", "startContentId"),
|
||||
FormatUpdateStatement("umbracoUserGroup", "startMediaId"),
|
||||
FormatUpdateStatement("umbracoUserGroup", "startMediaId"),
|
||||
FormatDeleteStatement("umbracoRelation", "parentId"),
|
||||
FormatDeleteStatement("umbracoRelation", "childId"),
|
||||
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
|
||||
FormatDeleteStatement("umbracoDomains", "domainRootStructureID"),
|
||||
FormatDeleteStatement("cmsDocument", "nodeId"),
|
||||
FormatDeleteStatement("cmsMedia", "nodeId"),
|
||||
FormatDeleteStatement("cmsPropertyData", "contentNodeId"),
|
||||
FormatDeleteStatement("cmsPreviewXml", "nodeId"),
|
||||
FormatDeleteStatement("cmsContentVersion", "ContentId"),
|
||||
|
||||
@@ -4,14 +4,13 @@ using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal abstract class RepositoryBase : DisposableObject
|
||||
internal abstract class RepositoryBase : DisposableObjectSlim
|
||||
{
|
||||
private readonly IScopeUnitOfWork _work;
|
||||
private readonly CacheHelper _globalCache;
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var name = Name;
|
||||
|
||||
// cater for instances where node has no name.
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return _numPos;
|
||||
}
|
||||
|
||||
if (name[name.Length - 1] != ')')
|
||||
return _numPos = -1;
|
||||
|
||||
@@ -106,7 +112,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
||||
return uniqueing || string.IsNullOrWhiteSpace(nodeName) ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Web.Security;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -187,18 +188,95 @@ UNION
|
||||
SELECT '5CountOfInvited' AS colName, COUNT(id) AS num FROM umbracoUser WHERE lastLoginDate IS NULL AND userDisabled = 1 AND invitedDate IS NOT NULL
|
||||
ORDER BY colName";
|
||||
|
||||
var result = Database.Fetch<dynamic>(sql);
|
||||
|
||||
var result = Database.Fetch<dynamic>(sql);
|
||||
|
||||
return new Dictionary<UserState, int>
|
||||
{
|
||||
{UserState.All, result[0].num},
|
||||
{UserState.Active, result[1].num},
|
||||
{UserState.Disabled, result[2].num},
|
||||
{UserState.LockedOut, result[3].num},
|
||||
{UserState.Invited, result[4].num}
|
||||
{UserState.All, (int)result[0].num},
|
||||
{UserState.Active, (int)result[1].num},
|
||||
{UserState.Disabled, (int)result[2].num},
|
||||
{UserState.LockedOut, (int)result[3].num},
|
||||
{UserState.Invited, (int)result[4].num}
|
||||
};
|
||||
}
|
||||
|
||||
public Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true)
|
||||
{
|
||||
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
|
||||
//and also business logic models for these objects but that's just so overkill for what we are doing
|
||||
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
|
||||
var now = DateTime.UtcNow;
|
||||
var dto = new UserLoginDto
|
||||
{
|
||||
UserId = userId,
|
||||
IpAddress = requestingIpAddress,
|
||||
LoggedInUtc = now,
|
||||
LastValidatedUtc = now,
|
||||
LoggedOutUtc = null,
|
||||
SessionId = Guid.NewGuid()
|
||||
};
|
||||
Database.Insert(dto);
|
||||
|
||||
if (cleanStaleSessions)
|
||||
{
|
||||
ClearLoginSessions(TimeSpan.FromDays(15));
|
||||
}
|
||||
|
||||
return dto.SessionId;
|
||||
}
|
||||
|
||||
public bool ValidateLoginSession(int userId, Guid sessionId)
|
||||
{
|
||||
var found = Database.FirstOrDefault<UserLoginDto>("WHERE sessionId=@sessionId", new {sessionId = sessionId});
|
||||
if (found == null || found.UserId != userId || found.LoggedOutUtc.HasValue)
|
||||
return false;
|
||||
|
||||
//now detect if there's been a timeout
|
||||
if (DateTime.UtcNow - found.LastValidatedUtc > TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes))
|
||||
{
|
||||
//timeout detected, update the record
|
||||
ClearLoginSession(sessionId);
|
||||
return false;
|
||||
}
|
||||
|
||||
//update the validate date
|
||||
found.LastValidatedUtc = DateTime.UtcNow;
|
||||
Database.Update(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int ClearLoginSessions(int userId)
|
||||
{
|
||||
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
|
||||
//and also business logic models for these objects but that's just so overkill for what we are doing
|
||||
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
|
||||
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE userId=@userId", new { userId = userId });
|
||||
Database.Execute("DELETE FROM umbracoUserLogin WHERE userId=@userId", new {userId = userId});
|
||||
return count;
|
||||
}
|
||||
|
||||
public int ClearLoginSessions(TimeSpan timespan)
|
||||
{
|
||||
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
|
||||
//and also business logic models for these objects but that's just so overkill for what we are doing
|
||||
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
|
||||
|
||||
var fromDate = DateTime.UtcNow - timespan;
|
||||
|
||||
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
|
||||
Database.Execute("DELETE FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
|
||||
return count;
|
||||
}
|
||||
|
||||
public void ClearLoginSession(Guid sessionId)
|
||||
{
|
||||
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
|
||||
//and also business logic models for these objects but that's just so overkill for what we are doing
|
||||
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
|
||||
Database.Execute("UPDATE umbracoUserLogin SET loggedOutUtc=@now WHERE sessionId=@sessionId",
|
||||
new { now = DateTime.UtcNow, sessionId = sessionId });
|
||||
}
|
||||
|
||||
protected override IEnumerable<IUser> PerformGetAll(params int[] ids)
|
||||
{
|
||||
var sql = GetQueryWithGroups();
|
||||
@@ -401,7 +479,8 @@ ORDER BY colName";
|
||||
{"updateDate", "UpdateDate"},
|
||||
{"avatar", "Avatar"},
|
||||
{"emailConfirmedDate", "EmailConfirmedDate"},
|
||||
{"invitedDate", "InvitedDate"}
|
||||
{"invitedDate", "InvitedDate"},
|
||||
{"tourData", "TourData"}
|
||||
};
|
||||
|
||||
//create list of properties that have changed
|
||||
@@ -834,14 +913,6 @@ ORDER BY colName";
|
||||
{
|
||||
return dtos.Select(UserFactory.BuildEntity);
|
||||
}
|
||||
|
||||
//private IEnumerable<IUserGroup> ConvertFromDtos(IEnumerable<UserGroupDto> dtos)
|
||||
//{
|
||||
// return dtos.Select(dto =>
|
||||
// {
|
||||
// var userGroupFactory = new UserGroupFactory();
|
||||
// return userGroupFactory.BuildEntity(dto);
|
||||
// });
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <summary>
|
||||
/// Represents a scoped unit of work.
|
||||
/// </summary>
|
||||
internal class ScopeUnitOfWork : DisposableObject, IScopeUnitOfWork
|
||||
internal class ScopeUnitOfWork : DisposableObjectSlim, IScopeUnitOfWork
|
||||
{
|
||||
private readonly Queue<Operation> _operations = new Queue<Operation>();
|
||||
private readonly IsolationLevel _isolationLevel;
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Umbraco.Core
|
||||
return _currentAssembliesHash;
|
||||
|
||||
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
|
||||
{
|
||||
{
|
||||
// the bin folder and everything in it
|
||||
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), false),
|
||||
// the app code folder and everything in it
|
||||
@@ -216,7 +216,7 @@ namespace Umbraco.Core
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
|
||||
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
}, _logger);
|
||||
}, _logger);
|
||||
|
||||
return _currentAssembliesHash;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -610,14 +610,14 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => discovered
|
||||
.Where(x => typeof (T).IsAssignableFrom(x)),
|
||||
.Where(x => typeof(T).IsAssignableFrom(x)),
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -640,7 +640,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -649,12 +649,12 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => discovered
|
||||
.Where(x => typeof(T).IsAssignableFrom(x))
|
||||
.Where(x => x.GetCustomAttributes<TAttribute>(false).Any()),
|
||||
@@ -676,7 +676,7 @@ namespace Umbraco.Core
|
||||
cache &= specificAssemblies == null;
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (object), typeof (TAttribute),
|
||||
typeof(object), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -694,13 +694,13 @@ namespace Umbraco.Core
|
||||
var name = ResolvedName(baseType, attributeType);
|
||||
|
||||
lock (_typesLock)
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolvedName(Type baseType, Type attributeType)
|
||||
@@ -836,7 +836,7 @@ namespace Umbraco.Core
|
||||
|
||||
public TypeListKey(Type baseType, Type attributeType)
|
||||
{
|
||||
BaseType = baseType ?? typeof (object);
|
||||
BaseType = baseType ?? typeof(object);
|
||||
AttributeType = attributeType;
|
||||
}
|
||||
|
||||
@@ -854,7 +854,7 @@ namespace Umbraco.Core
|
||||
|
||||
var hash = 5381;
|
||||
hash = ((hash << 5) + hash) ^ BaseType.GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof (TypeListKey)).GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof(TypeListKey)).GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -917,7 +917,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
// look for IParameterEditor (fast, IDiscoverable) then filter
|
||||
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => propertyEditor.IsAssignableFrom(x) && x != propertyEditor);
|
||||
@@ -932,8 +932,8 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static IEnumerable<Type> ResolveParameterEditors(this PluginManager mgr)
|
||||
{
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var parameterEditor = typeof (ParameterEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
var parameterEditor = typeof(ParameterEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => x != propertyEditor && x != parameterEditor);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Umbraco.Core")]
|
||||
@@ -12,8 +10,8 @@ using System.Security.Permissions;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Umbraco CMS")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
@@ -50,6 +48,8 @@ using System.Security.Permissions;
|
||||
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Umbraco.Headless")]
|
||||
|
||||
//allow this to be mocked in our unit tests
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
||||
|
||||
@@ -178,29 +178,25 @@ namespace Umbraco.Core.PropertyEditors
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ConvertItemsToJsonIfDetected(IDictionary<string, object> result)
|
||||
protected void ConvertItemsToJsonIfDetected(IDictionary<string, object> result)
|
||||
{
|
||||
//now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects
|
||||
// so they can be consumed as real json in angular!
|
||||
// convert values that are Json to true Json objects that can be consumed by Angular
|
||||
|
||||
var keys = result.Keys.ToArray();
|
||||
for (var i = 0; i < keys.Length; i++)
|
||||
{
|
||||
if (result[keys[i]] is string)
|
||||
if ((result[keys[i]] is string) == false) continue;
|
||||
|
||||
var asString = result[keys[i]].ToString();
|
||||
if (asString.DetectIsJson() == false) continue;
|
||||
|
||||
try
|
||||
{
|
||||
var asString = result[keys[i]].ToString();
|
||||
if (asString.DetectIsJson())
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.DeserializeObject(asString);
|
||||
result[keys[i]] = json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
//swallow this exception, we thought it was json but it really isn't so continue returning a string
|
||||
}
|
||||
}
|
||||
result[keys[i]] = JsonConvert.DeserializeObject(asString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// swallow this exception, we thought it was Json but it really isn't so continue returning a string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -176,6 +176,12 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <returns></returns>
|
||||
internal Attempt<object> TryConvertValueToCrlType(object value)
|
||||
{
|
||||
var jv = value as JValue;
|
||||
if (jv != null)
|
||||
{
|
||||
value = value.ToString();
|
||||
}
|
||||
|
||||
//this is a custom check to avoid any errors, if it's a string and it's empty just make it null
|
||||
var s = value as string;
|
||||
if (s != null)
|
||||
@@ -391,4 +397,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
[DefaultPropertyValueConverter]
|
||||
[PropertyValueType(typeof(string))]
|
||||
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
|
||||
public class ColorPickerValueConverter : PropertyValueConverterBase
|
||||
public class ColorPickerValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
|
||||
{
|
||||
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
@@ -18,11 +19,60 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||
{
|
||||
return UseLabel(propertyType) ? typeof(PickedColor) : typeof(string);
|
||||
}
|
||||
|
||||
public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
|
||||
{
|
||||
return PropertyCacheLevel.Content;
|
||||
}
|
||||
|
||||
private bool UseLabel(PublishedPropertyType propertyType)
|
||||
{
|
||||
var preValues = ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
|
||||
PreValue preValue;
|
||||
return preValues.PreValuesAsDictionary.TryGetValue("useLabel", out preValue) && preValue.Value == "1";
|
||||
}
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// make sure it's a string
|
||||
return source == null ? string.Empty : source.ToString();
|
||||
var useLabel = UseLabel(propertyType);
|
||||
|
||||
if (source == null) return useLabel ? null : string.Empty;
|
||||
|
||||
var ssource = source.ToString();
|
||||
if (ssource.DetectIsJson())
|
||||
{
|
||||
try
|
||||
{
|
||||
var jo = JsonConvert.DeserializeObject<JObject>(ssource);
|
||||
if (useLabel) return new PickedColor(jo["value"].ToString(), jo["label"].ToString());
|
||||
return jo["value"].ToString();
|
||||
}
|
||||
catch { /* not json finally */ }
|
||||
}
|
||||
|
||||
if (useLabel) return new PickedColor(ssource, ssource);
|
||||
return ssource;
|
||||
}
|
||||
|
||||
public class PickedColor
|
||||
{
|
||||
public PickedColor(string color, string label)
|
||||
{
|
||||
Color = color;
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public string Color { get; private set; }
|
||||
public string Label { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Color;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
var gridConfig = UmbracoConfig.For.GridConfig(
|
||||
ApplicationContext.Current.ProfilingLogger.Logger,
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache,
|
||||
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.AppPlugins)),
|
||||
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.Config)),
|
||||
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)),
|
||||
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)),
|
||||
HttpContext.Current.IsDebuggingEnabled);
|
||||
|
||||
var sections = GetArray(obj, "sections");
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
return Constants.PropertyEditors.MultipleTextstringAlias.Equals(propertyType.PropertyEditorAlias);
|
||||
}
|
||||
|
||||
private static readonly string[] NewLineDelimiters = { "\r\n", "\r", "\n" };
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// data is (both in database and xml):
|
||||
@@ -53,7 +55,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
// Fall back on normal behaviour
|
||||
if (values.Any() == false)
|
||||
{
|
||||
return sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
return sourceString.Split(NewLineDelimiters, StringSplitOptions.None);
|
||||
}
|
||||
|
||||
return values.ToArray();
|
||||
|
||||
@@ -401,9 +401,10 @@ namespace Umbraco.Core.Publishing
|
||||
content.Name, content.Id));
|
||||
}
|
||||
|
||||
// if newest is published, unpublish
|
||||
if (content.Published)
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
// make sure we dirty .Published and always unpublish
|
||||
// the version we have here could be the newest, !Published
|
||||
content.ChangePublishedState(PublishedState.Published);
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
|
||||
_logger.Info<PublishingStrategy>(
|
||||
string.Format("Content '{0}' with Id '{1}' has been unpublished.",
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Umbraco.Core.Scoping
|
||||
public NoScope(ScopeProvider scopeProvider)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
Timestamp = DateTime.Now;
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.RegisterScope(this);
|
||||
#endif
|
||||
@@ -28,6 +29,8 @@ namespace Umbraco.Core.Scoping
|
||||
private readonly Guid _instanceId = Guid.NewGuid();
|
||||
public Guid InstanceId { get { return _instanceId; } }
|
||||
|
||||
public DateTime Timestamp { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CallContext { get { return false; } }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user