Compare commits
694 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25c68a5a36 | |||
| 82d433d291 | |||
| 69ae715998 | |||
| 1930022f19 | |||
| bece953fa3 | |||
| a41d37b1cf | |||
| 31603f7063 | |||
| 87ae16b89a | |||
| 4520c06454 | |||
| 9f0c00827d | |||
| 0df0b78b90 | |||
| c6b6685dc7 | |||
| 7c3c477f18 | |||
| d7cc626396 | |||
| 9d50a4d2a5 | |||
| 321e2f9de0 | |||
| 9834352de2 | |||
| 2341622243 | |||
| 956a51ab67 | |||
| e358d3b170 | |||
| 752ce55cfa | |||
| e6d6d3dd69 | |||
| dca14f3eb4 | |||
| 1abdca2432 | |||
| 6db4fedcad | |||
| 3e66235932 | |||
| 1ab321c7b1 | |||
| dffcf10032 | |||
| 054033c73f | |||
| 4fcd23d8dc | |||
| 209ccb9ae7 | |||
| 2850c8aa05 | |||
| 87a9a57e6d | |||
| f3f6504398 | |||
| c47d59a257 | |||
| d61d6008da | |||
| 403f64eeed | |||
| 5aa29cc150 | |||
| 9be3bdb8a5 | |||
| c0baac2b38 | |||
| b20fc22577 | |||
| 74ebf67aef | |||
| 33036f80fe | |||
| 0688550143 | |||
| d43b673092 | |||
| 4ec37b9d3a | |||
| 4eb0981552 | |||
| ed8cc2ad0d | |||
| 10bc7d0c1e | |||
| aa11d3504d | |||
| 6903ebda1f | |||
| 6c6e398c53 | |||
| bccb9655d0 | |||
| dd143681f1 | |||
| b1d4a99510 | |||
| b8ea50d644 | |||
| 1eab26c0f6 | |||
| 0543c620eb | |||
| 646e5f173d | |||
| b5594517bd | |||
| 5501c9531b | |||
| 8824ab2b27 | |||
| b60e292d5f | |||
| e1dcf6b061 | |||
| 246dfba8ac | |||
| c0c7d21001 | |||
| 7d189a6fb8 | |||
| e60dcfa144 | |||
| 3f8b779ae4 | |||
| 72dc5fc477 | |||
| 737f2fb707 | |||
| a13ead0d10 | |||
| 414dbe45a6 | |||
| 340328aeba | |||
| f875dc15f7 | |||
| 71242430da | |||
| 1e04735826 | |||
| b975a02bfa | |||
| 88ec514630 | |||
| 6f7ec84f18 | |||
| 4ee4f296fc | |||
| b9e4950e36 | |||
| ba52f9235e | |||
| 33ccf490ab | |||
| 101e116baf | |||
| ebf24d69b5 | |||
| c94662e49c | |||
| 5d37eaf993 | |||
| 1ded2c9baf | |||
| 2daacd8d57 | |||
| 3f4fa8b8ed | |||
| 03ba8c9e14 | |||
| 13b57c7638 | |||
| a8b297903d | |||
| 89b5774106 | |||
| 3a4d0a02eb | |||
| 2666742851 | |||
| 38c1e154d4 | |||
| 774f201514 | |||
| 067eceffce | |||
| 573c08295a | |||
| fc558198d2 | |||
| de1b69d5ae | |||
| 03358ca1ca | |||
| 81d8a90f59 | |||
| 6a28f246f6 | |||
| 15a1887ccb | |||
| 2d5e5e9b3c | |||
| 03a845c957 | |||
| b0d03e7493 | |||
| ee2410ebe2 | |||
| 7d52ed873e | |||
| de34379ddf | |||
| b176d7dca0 | |||
| 12e2aa679b | |||
| 873eac7f24 | |||
| 1978930fa2 | |||
| d2ee281bfa | |||
| 468cdf64ff | |||
| 5752bb0d18 | |||
| a0f5a717f2 | |||
| 8b34b4e8a1 | |||
| 6152079af1 | |||
| e638cbf4e6 | |||
| a9277113b1 | |||
| 1805857eea | |||
| 32057bd7ee | |||
| 747ce1c013 | |||
| 3a0a839503 | |||
| f4c2b7419a | |||
| bbe8c8e8e6 | |||
| 49d1171a99 | |||
| b44699ff4a | |||
| 086d81438c | |||
| 895ecf5f16 | |||
| 9d95e9f4d3 | |||
| bba0a58bac | |||
| 14cbef5edf | |||
| 65a2b79189 | |||
| 42dd38fb29 | |||
| 023a8da5ba | |||
| 4dc559a8d1 | |||
| 1686e2f4bf | |||
| 69244f6991 | |||
| 4fe029a695 | |||
| 0fd37d708d | |||
| f6abb0f603 | |||
| 9bb8ee5f95 | |||
| ac55724231 | |||
| 5789ac28d2 | |||
| bc6ef58235 | |||
| 9236225bdf | |||
| 6dbc30e094 | |||
| 1d4d8066ed | |||
| 7a39751101 | |||
| c4e4f9579b | |||
| 43ae301683 | |||
| d978db588c | |||
| 9782f66507 | |||
| 16f8db9dea | |||
| e9938afc83 | |||
| fcda9821d0 | |||
| bdc234b18a | |||
| 648cf69d45 | |||
| a0fcd2c7f9 | |||
| d603c08e1f | |||
| 53097a9b84 | |||
| beda0968ae | |||
| 1ccee3f305 | |||
| 908afbd264 | |||
| c548ad6a98 | |||
| ca2dd3eb47 | |||
| 8ea5b6fb46 | |||
| f6ec4798a6 | |||
| 096e10aad4 | |||
| 817d5aab0d | |||
| 0aff4b4d05 | |||
| 5e738a2b7b | |||
| 652b9d1a86 | |||
| dd72fd04bd | |||
| 83668db6e3 | |||
| ba7a5a0e8a | |||
| a71bfaa14c | |||
| 6b455aaa19 | |||
| b2ca814fdb | |||
| 9523e5c854 | |||
| 99b2a2da38 | |||
| db22dfcf7d | |||
| 15ed458ad3 | |||
| 9fc51d8a12 | |||
| dc32138006 | |||
| 1a3d3994c3 | |||
| 4aedfc419e | |||
| 21c6cf71cb | |||
| 247b5b648d | |||
| fa4a268556 | |||
| 1449966537 | |||
| 24a6ae4e32 | |||
| d2f9bd6896 | |||
| 6e3d0b5d91 | |||
| bf58359eed | |||
| 59b4940979 | |||
| 8f2f4b579e | |||
| 5efabee0bd | |||
| fc19748fbf | |||
| 6ea64c84d8 | |||
| 7dfa633fb3 | |||
| 6ce303a918 | |||
| 62d3dc78fa | |||
| 3374f9e023 | |||
| 2675992d82 | |||
| 0a321e37fc | |||
| f3ca9e067b | |||
| 4814fb3e96 | |||
| ce29f54663 | |||
| 7261162d8a | |||
| 5f9b6c14f4 | |||
| 7685e53cfe | |||
| 60e81a051d | |||
| b2436bd9b6 | |||
| 1c83114c78 | |||
| 46c07e55e4 | |||
| 6ee078108d | |||
| 90eb5dc7ac | |||
| d6833c9b86 | |||
| 2325c8a64f | |||
| d4e205a47a | |||
| 84df6e5315 | |||
| af3b655073 | |||
| f8e366bb4a | |||
| 6439d7337d | |||
| 56dddbca1c | |||
| 53ea491bd6 | |||
| b5f900e789 | |||
| cad946905c | |||
| 49c26f4a5e | |||
| 45b173c0db | |||
| 5165d70fbb | |||
| c2816e759b | |||
| 7ab8ea4da4 | |||
| 1e8f016234 | |||
| d07ba12e73 | |||
| 7d7fa0e389 | |||
| fdab6d5de5 | |||
| ff94eba3a7 | |||
| 834de2099f | |||
| 904ee40a34 | |||
| 99973841e3 | |||
| e200e2fc48 | |||
| 69c7ecad59 | |||
| b1350fa4a6 | |||
| 6014608d5a | |||
| 929180224f | |||
| 22dde5a421 | |||
| ee5c532be7 | |||
| c9b27b5a70 | |||
| 7f510c107a | |||
| 2ebfd8f3d4 | |||
| 6e20be993b | |||
| 84fad1300b | |||
| fd7930256b | |||
| 6319b10881 | |||
| c24775adb0 | |||
| 0d164ffb08 | |||
| dd83dced4c | |||
| ea56363f6d | |||
| f86902bc89 | |||
| b207521f4b | |||
| a2aaa9a1ea | |||
| 217f591db0 | |||
| b835cfb76d | |||
| 82cfbab17a | |||
| d453aedd0d | |||
| 851181f861 | |||
| 4680b643f6 | |||
| 05be2a5e86 | |||
| 8445c9c29b | |||
| e8eea039e1 | |||
| cd8b8dc704 | |||
| e98ed8561a | |||
| 626e155c07 | |||
| de968a18dd | |||
| 8d1d2f2414 | |||
| 4bb38609ad | |||
| 0370a1bcf2 | |||
| 188613e5e7 | |||
| a9261d217e | |||
| 4693fb23c8 | |||
| 6e78a9f98f | |||
| 92c19ba76c | |||
| 7848a3095d | |||
| 1a75e7beb1 | |||
| 388eea95e5 | |||
| fdf1218a9e | |||
| 1a1aba92ef | |||
| 39ee22cbd6 | |||
| 119acaa28a | |||
| e22282c1e8 | |||
| 6167d1584a | |||
| 0c2d1f72b9 | |||
| 8c36dac67a | |||
| d7f8b10b28 | |||
| 7e8be5fd67 | |||
| e515b716f0 | |||
| fd49c81940 | |||
| cc6099c953 | |||
| fdadd80414 | |||
| fcd7e9517d | |||
| 72f53e4645 | |||
| 2c9a287bda | |||
| e2f4cfaf82 | |||
| 8729156fbf | |||
| bfd0c5e309 | |||
| 43669e62b4 | |||
| 6b128ea7d4 | |||
| 60d6ae44b5 | |||
| b3035d160c | |||
| f27f458a5d | |||
| 3f09453345 | |||
| 4857107b5e | |||
| 080bf7293d | |||
| b3288f0c91 | |||
| d5993a4783 | |||
| b254f81f4a | |||
| 17b0547cfc | |||
| 86c2a6e8a9 | |||
| 874f464a2b | |||
| 93151a43ac | |||
| b3c41e051f | |||
| a88637910f | |||
| 1af7649a87 | |||
| 431e93000e | |||
| f83bbdbf90 | |||
| 5a810b584e | |||
| bcae3afa45 | |||
| 3aff474648 | |||
| 1a821b4073 | |||
| efda233fcf | |||
| ed2e409707 | |||
| 8645e0b193 | |||
| a89b9e9da6 | |||
| 8917701fa3 | |||
| 68b17bff08 | |||
| 9b2b961f83 | |||
| 7a12060c37 | |||
| f3c7533796 | |||
| 59116fc7fa | |||
| a66670263f | |||
| 1e1d49f305 | |||
| 1be2706c30 | |||
| eaf2956524 | |||
| 525280d466 | |||
| 496beca8f2 | |||
| 637a22293d | |||
| 3c8517edcb | |||
| 45e6127d00 | |||
| 6eb5d3f7ef | |||
| e6986432df | |||
| 5bee398d09 | |||
| c53d812742 | |||
| 966a29954c | |||
| 9f28aea10d | |||
| 097014454f | |||
| 48143bc0c3 | |||
| 75c26e4d92 | |||
| ad4135f629 | |||
| 8e7ed865f0 | |||
| f627de2433 | |||
| 303aa07235 | |||
| b404cc6438 | |||
| 80103e6155 | |||
| 4fbf65e684 | |||
| 0aeac407c5 | |||
| be98ceea6c | |||
| 07e0cd2957 | |||
| 9da7d56b9c | |||
| 5a3890159f | |||
| 70da51113c | |||
| 0f7c407a7c | |||
| 392634eb76 | |||
| 7bc5588d6d | |||
| 259bc863f0 | |||
| 70514b88c5 | |||
| 582bafdefd | |||
| 9924ef2381 | |||
| 12cdd3326a | |||
| 859fbaaa05 | |||
| 27764a2c82 | |||
| 5fdcec6904 | |||
| 0b678f1d3b | |||
| 72991334c4 | |||
| 309adbfb20 | |||
| c659d5b85e | |||
| 3ecc974325 | |||
| a4bf4b0977 | |||
| bef6ce66f1 | |||
| a18a2198a8 | |||
| 38c4bc72cd | |||
| 7cf7ac687c | |||
| 6b626baffb | |||
| 7ce0272169 | |||
| acb934e3f1 | |||
| 6485e625b1 | |||
| 3aa486a463 | |||
| 5aa0621c90 | |||
| 625b8f7e65 | |||
| 72b7b443e9 | |||
| 53c1c97aeb | |||
| 5011eee037 | |||
| e27163f3a7 | |||
| 22bace9545 | |||
| 8c36a683e6 | |||
| a23fb7bc74 | |||
| 2ccec3be33 | |||
| 65ea6f94d3 | |||
| 3af3e054d7 | |||
| 9158ea1ace | |||
| e8f7f77bb6 | |||
| 6b5cb3a9cf | |||
| bb655afd72 | |||
| ad3502d438 | |||
| dfb40802ff | |||
| 8ded6fff93 | |||
| 56111be82d | |||
| 75e003185a | |||
| 9d12e1c7df | |||
| f25463babe | |||
| b8575ef158 | |||
| 3e0cd0a605 | |||
| 1c23940b0b | |||
| d3bb750f98 | |||
| 7ec13146b2 | |||
| 48e0e5da75 | |||
| 448bfc166f | |||
| 7b48a59ca2 | |||
| 1f161b2b9c | |||
| 6f27f839e0 | |||
| f7e046deaf | |||
| 09b34625b9 | |||
| 3b3e412450 | |||
| 3c6bf1ce92 | |||
| dfb08b9ec7 | |||
| eef7ce45d3 | |||
| 697d4e3b23 | |||
| 6d77121c05 | |||
| a7928f87ee | |||
| 07d431f52e | |||
| bb71efd4fa | |||
| 48b60961b5 | |||
| 2bea8c5691 | |||
| 32ed0de4f6 | |||
| c67f0baf74 | |||
| 95ff1a03fb | |||
| c0b22c4349 | |||
| 5d3d2e9331 | |||
| de266351d1 | |||
| f8b4caea85 | |||
| 5691bb0de8 | |||
| fa56413d02 | |||
| cb5fa8ac13 | |||
| 531eba6e28 | |||
| 4c6ec3ac4b | |||
| 46eaba668c | |||
| 889736f63b | |||
| a03e46339d | |||
| 37fbc3bab4 | |||
| 9f146753b5 | |||
| acc6d46b4b | |||
| d57c572116 | |||
| 70fcd6fab7 | |||
| 45076491cb | |||
| 6019ab13df | |||
| 83fd0ab7e3 | |||
| 4421681538 | |||
| e59768dda0 | |||
| 0b114c88f0 | |||
| 4238b79c67 | |||
| d9374c34d1 | |||
| 2b7526ab2e | |||
| 2fde10494c | |||
| 0da4879400 | |||
| 07172067ab | |||
| 681a3e3046 | |||
| e5dea8e4be | |||
| 087dd1fe9e | |||
| 37afe6dd29 | |||
| 338a125eaa | |||
| 18fc6ccc99 | |||
| 8a1db41fc4 | |||
| d2119c17c1 | |||
| 9d4afbd007 | |||
| c4f764d5fc | |||
| db3d86aeca | |||
| c17e60f709 | |||
| 68860b6c54 | |||
| 5834757476 | |||
| 91ddbff990 | |||
| 5fd1d7c9e5 | |||
| 32cfdbe968 | |||
| a754cecaf7 | |||
| 8ccb06c91d | |||
| d8e35ffc08 | |||
| 34dcde645c | |||
| 0474605d36 | |||
| 4dd3f2f8ac | |||
| d7a0447e2e | |||
| cbe23663e3 | |||
| bff415a4c6 | |||
| caa59f2c80 | |||
| 2ff1c973e0 | |||
| f056d3fae7 | |||
| 41a455c9ac | |||
| f834155763 | |||
| 15cc3d19c1 | |||
| 3bb563ec55 | |||
| b5282244f0 | |||
| aa5b5708b2 | |||
| ec0d6959a9 | |||
| 320df6706e | |||
| 519dd8b476 | |||
| 0434b3e5bd | |||
| ff42a1b79c | |||
| 4e433becd6 | |||
| 8aafc583de | |||
| d6b0ed2d40 | |||
| ebbbd80bb3 | |||
| 02eff33d29 | |||
| 7b33eca014 | |||
| f8e06e54de | |||
| 23d9ef624b | |||
| cdb5b0470d | |||
| b43f39a0cb | |||
| d0cb27d1c1 | |||
| e8990eb6b2 | |||
| 05524c643e | |||
| ed989079b4 | |||
| feed5c5e51 | |||
| 754a6e327e | |||
| 650fb94057 | |||
| 0af19fa442 | |||
| a4b25c3f84 | |||
| 5b17bc6d5f | |||
| 06a1f790dd | |||
| e7994d077e | |||
| f55f9edb71 | |||
| df3bc873df | |||
| d1c5ddc03d | |||
| f11d209177 | |||
| 16a1720bf7 | |||
| ea32776174 | |||
| 92ef071883 | |||
| b42d0bc17e | |||
| 020a416dc5 | |||
| d491c133a7 | |||
| b68dd6b35c | |||
| ba9d023cc0 | |||
| eb6fe1e4d3 | |||
| c972ae566e | |||
| 320b53ae3c | |||
| 84f94d6471 | |||
| f758eb6a62 | |||
| 7ec4d3f26d | |||
| 2a0d2bdb86 | |||
| 24d3e465ea | |||
| d987dfb5a2 | |||
| 2ede32fe70 | |||
| fce8c9d3ec | |||
| f5194efb6e | |||
| 1b360226d6 | |||
| 74806984b3 | |||
| c8c278c7bc | |||
| 524d6fb76a | |||
| 7185a6dba5 | |||
| 2496a8e77e | |||
| 13e8e972eb | |||
| a8e4c3eb9a | |||
| d631039c0d | |||
| b1e879bf68 | |||
| 113b866485 | |||
| 6c6ef8d144 | |||
| 95b9a46b88 | |||
| 99250cefbb | |||
| dc651a1c42 | |||
| 766ca78725 | |||
| 3ca4fb76b2 | |||
| 0a76757cea | |||
| 8fb446960c | |||
| 1cf40aca07 | |||
| 4e9fd5a223 | |||
| e2c36d7212 | |||
| aadcdc9c15 | |||
| e04eed41e7 | |||
| 62887607ff | |||
| 54a639ffcd | |||
| 9f16bee040 | |||
| 976d1b1ff6 | |||
| 8a4e39a999 | |||
| 695cc07912 | |||
| f325794523 | |||
| 0b3733f992 | |||
| 2926a79c3f | |||
| ef7a6f19ba | |||
| 95a4bb9ba6 | |||
| 32b7871eaf | |||
| 3ec6d2b82f | |||
| cd03e80410 | |||
| 9ed3561b56 | |||
| 28da44f0de | |||
| f8d7aeec80 | |||
| dacec47cd9 | |||
| 57f06ba447 | |||
| 46f57a3115 | |||
| 82c1c95e35 | |||
| fa6c215849 | |||
| 420746397d | |||
| 142a9963af | |||
| b656db3af3 | |||
| d697c590e7 | |||
| 1dd103caa3 | |||
| a55cd67fd6 | |||
| 43bfd6e472 | |||
| afa81be102 | |||
| b07665f4d4 | |||
| 0ebd66d926 | |||
| 8d45898274 | |||
| a99a33477a | |||
| b42d3b226d | |||
| b6925f9f07 | |||
| ddd0e8abb5 | |||
| e07f573683 | |||
| ffbb1a85ab | |||
| ac9c938e92 | |||
| 1f88960b3f | |||
| d8edf67b82 | |||
| 762656e964 | |||
| 46a4f9242c | |||
| 8cae2526d5 | |||
| 6b313aba48 | |||
| 365a8e85e9 | |||
| 7b4c58538d | |||
| e41c99d5bc | |||
| a49dd36461 | |||
| 82a1e85431 | |||
| 5f9dddafd8 | |||
| 836297df58 | |||
| 1c7d83f589 | |||
| 157428dbc8 | |||
| a2a81222e6 | |||
| 5abbca0f5d | |||
| 0bcaa0da7f | |||
| 922942197f | |||
| b8975de04d | |||
| f00950bfe4 | |||
| 80d012f3ad | |||
| 654efd1128 | |||
| aa2375b8ba | |||
| f6532d2743 | |||
| 8c334a8c95 | |||
| 42cd71ef6c | |||
| 5956394bf1 | |||
| d1e9fb6d9f | |||
| 48dcc373dc | |||
| c9961b5192 | |||
| 03d3503531 | |||
| 9acba1d6d7 | |||
| 659f47c038 | |||
| 78613823ae | |||
| cb7f26c998 | |||
| 16b5c86192 | |||
| c83ab00827 | |||
| 937e840104 | |||
| 8d49c730d5 | |||
| fadb503b97 | |||
| 289bed8fd2 | |||
| eec1048631 | |||
| 313d77eb91 | |||
| b048341da0 | |||
| 03cb54e931 | |||
| 150b67d10f | |||
| ac88da4188 | |||
| cca27e064a | |||
| 4e5019d5af | |||
| f1a312b14a | |||
| da0b935604 | |||
| b91a06ab98 | |||
| d61fb5d94d | |||
| 3b424274ee | |||
| b763c2ab2f | |||
| 309e460f07 | |||
| 6fae57c5e9 | |||
| beb1979b46 | |||
| 672672d0aa | |||
| c9d1123f0a | |||
| 07229c4bd2 |
@@ -110,3 +110,4 @@ src/Umbraco.Web.UI/umbraco/js/init.js
|
||||
build/ApiDocs/*
|
||||
build/ApiDocs/Output/*
|
||||
src/Umbraco.Web.UI.Client/bower_components/*
|
||||
src/umbraco.sln.ide
|
||||
|
||||
+39
-15
@@ -1,6 +1,19 @@
|
||||
@ECHO OFF
|
||||
SET release=7.1.2
|
||||
SET comment=
|
||||
IF NOT EXIST UmbracoVersion.txt (
|
||||
ECHO UmbracoVersion.txt missing!
|
||||
GOTO :showerror
|
||||
)
|
||||
|
||||
REM Get the version and comment from UmbracoVersion.txt lines 2 and 3
|
||||
SET "release="
|
||||
SET "comment="
|
||||
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
|
||||
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED comment SET "comment=%%i"
|
||||
|
||||
REM If there's arguments on the command line overrule UmbracoVersion.txt and use that as the version
|
||||
IF [%1] NEQ [] (SET release=%1)
|
||||
IF [%2] NEQ [] (SET comment=%2) ELSE (IF [%1] NEQ [] (SET "comment="))
|
||||
|
||||
SET version=%release%
|
||||
|
||||
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
|
||||
@@ -10,30 +23,41 @@ ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %rel
|
||||
|
||||
ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
|
||||
SET nuGetFolder=%CD%\..\src\packages\
|
||||
..\src\.nuget\NuGet.exe sources Remove -Name MyGetUmbracoCore
|
||||
..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/
|
||||
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
|
||||
..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder%
|
||||
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder%
|
||||
|
||||
ECHO Removing the belle build folder to make sure everything is clean as a whistle
|
||||
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
|
||||
|
||||
ECHO Removing existing built files to make sure everything is clean as a whistle
|
||||
RMDIR /Q /S _BuildOutput
|
||||
DEL /F /Q UmbracoCms.*.zip
|
||||
DEL /F /Q UmbracoCms.*.nupkg
|
||||
DEL /F /Q webpihash.txt
|
||||
|
||||
ECHO Performing MSBuild and producing Umbraco binaries zip files
|
||||
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment%
|
||||
|
||||
ECHO Adding dummy files to include in the NuGet package so that empty folders actually get created
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Code\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Data\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Plugins\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\css\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\masterpages\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\media\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\scripts\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\usercontrols\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\Partials\dummy.txt
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt
|
||||
SET dummytext=This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete.
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Code\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Data\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Plugins\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\css\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\masterpages\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\media\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\scripts\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\usercontrols\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\Views\Partials\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt
|
||||
|
||||
ECHO Adding Web.config transform files to the NuGet package
|
||||
ren .\_BuildOutput\WebApp\MacroScripts\Web.config Web.config.transform
|
||||
ren .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
|
||||
ren .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
|
||||
REN .\_BuildOutput\WebApp\MacroScripts\Web.config Web.config.transform
|
||||
REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
|
||||
REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
|
||||
|
||||
ECHO Packing the NuGet release files
|
||||
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version%
|
||||
|
||||
+1
-4
@@ -252,14 +252,11 @@
|
||||
</Target>
|
||||
|
||||
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
|
||||
<!-- Also, it places umbraco.XmlSerializers.dll in the root, even though it's also in the bin folder. Kill! -->
|
||||
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
|
||||
<ItemGroup>
|
||||
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
|
||||
<XmlSerializersDll Include="$(WebAppFolder)umbraco.XmlSerializers.dll" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(PresentationFolderToDelete)" />
|
||||
<Delete Files="@(XmlSerializersDll)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CompileProjects" DependsOnTargets="CompileExternalExamineProjects">
|
||||
@@ -325,7 +322,7 @@
|
||||
Regex="CurrentComment { get { return "([a-zA-Z]+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.targets"
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET release=%1
|
||||
ECHO Installing Npm NuGet Package
|
||||
|
||||
SET nuGetFolder=%CD%\..\src\packages\
|
||||
@@ -26,7 +27,7 @@ ECHO Do npm install and the grunt build of Belle
|
||||
call npm install
|
||||
call npm install -g grunt-cli
|
||||
call npm install -g bower
|
||||
call grunt build --buildversion=7.1.2
|
||||
call grunt build --buildversion=%release%
|
||||
|
||||
ECHO Reset path to what it was before
|
||||
path=%oldPath%
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.0, 3.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.20710.0, 3.0.0)" />
|
||||
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.6, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
@@ -29,9 +31,10 @@
|
||||
<dependency id="ClientDependency" version="[1.7.1.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.2, 7.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[1.9.0, 2.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[3.2.3, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.4, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[1.9.5, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[3.3.1, 5.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -41,7 +44,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
@@ -64,8 +66,8 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.XmlSerializers.dll" target="lib\umbraco.XmlSerializers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForPackageDependsOn>
|
||||
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
AddUmbracoFilesToOutput;
|
||||
$(CopyAllFilesToSingleFolderForPackageDependsOn);
|
||||
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<UmbracoVersion>7.1.2</UmbracoVersion>
|
||||
</PropertyGroup>
|
||||
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
|
||||
<PropertyGroup>
|
||||
<UmbracoFilesFolder>..\packages\UmbracoCms.$(UmbracoVersion)\UmbracoFiles\</UmbracoFilesFolder>
|
||||
<UmbracoFilesFolder>$(MSBuildThisFileDirectory)..\UmbracoFiles\</UmbracoFilesFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include="$(UmbracoFilesFolder)**\*" />
|
||||
@@ -33,6 +30,9 @@
|
||||
<CustomFilesToInclude Include=".\Config\Splashes\**\*">
|
||||
<Dir>Config\Splashes</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\data\**\*">
|
||||
<Dir>data</Dir>
|
||||
</CustomFilesToInclude>
|
||||
<CustomFilesToInclude Include=".\umbraco\**\*">
|
||||
<Dir>umbraco</Dir>
|
||||
</CustomFilesToInclude>
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
param($rootPath, $toolsPath, $package, $project)
|
||||
|
||||
if ($project) {
|
||||
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
|
||||
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
|
||||
$copyLogsPath = Join-Path $backupPath "CopyLogs"
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
|
||||
# Create backup folder and logs folder if it doesn't exist yet
|
||||
New-Item -ItemType Directory -Force -Path $backupPath
|
||||
New-Item -ItemType Directory -Force -Path $copyLogsPath
|
||||
|
||||
# After backing up, remove all umbraco dlls from bin folder in case dll files are included in the VS project
|
||||
# See: http://issues.umbraco.org/issue/U4-4930
|
||||
$umbracoBinFolder = Join-Path $projectDestinationPath "bin"
|
||||
if(Test-Path $umbracoBinFolder) {
|
||||
$umbracoBinBackupPath = Join-Path $backupPath "bin"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
|
||||
|
||||
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
|
||||
|
||||
# Delete files Umbraco brings in
|
||||
if(Test-Path $umbracoBinFolder\businesslogic.dll) { Remove-Item $umbracoBinFolder\businesslogic.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\cms.dll) { Remove-Item $umbracoBinFolder\cms.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\controls.dll) { Remove-Item $umbracoBinFolder\controls.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\interfaces.dll) { Remove-Item $umbracoBinFolder\interfaces.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\SQLCE4Umbraco.dll) { Remove-Item $umbracoBinFolder\SQLCE4Umbraco.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\TidyNet.dll) { Remove-Item $umbracoBinFolder\TidyNet.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.dll) { Remove-Item $umbracoBinFolder\umbraco.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Umbraco.Core.dll) { Remove-Item $umbracoBinFolder\Umbraco.Core.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.DataLayer.dll) { Remove-Item $umbracoBinFolder\umbraco.DataLayer.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.editorControls.dll) { Remove-Item $umbracoBinFolder\umbraco.editorControls.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.MacroEngines.dll) { Remove-Item $umbracoBinFolder\umbraco.MacroEngines.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.providers.dll) { Remove-Item $umbracoBinFolder\umbraco.providers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Umbraco.Web.UI.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.UI.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\UmbracoExamine.dll) { Remove-Item $umbracoBinFolder\UmbracoExamine.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\UrlRewritingNet.UrlRewriter.dll) { Remove-Item $umbracoBinFolder\UrlRewritingNet.UrlRewriter.dll -Force -Confirm:$false }
|
||||
|
||||
# Delete files Umbraco depends upon
|
||||
$amd64Folder = Join-Path $projectDestinationPath "bin\amd64"
|
||||
if(Test-Path $amd64Folder) { Remove-Item $amd64Folder -Force -Recurse -Confirm:$false }
|
||||
$x86Folder = Join-Path $projectDestinationPath "bin\x86"
|
||||
if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\AutoMapper.dll) { Remove-Item $umbracoBinFolder\AutoMapper.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\AutoMapper.Net4.dll) { Remove-Item $umbracoBinFolder\AutoMapper.Net4.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ClientDependency.Core.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ClientDependency.Core.Mvc.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.Mvc.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\CookComputing.XmlRpcV2.dll) { Remove-Item $umbracoBinFolder\CookComputing.XmlRpcV2.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Examine.dll) { Remove-Item $umbracoBinFolder\Examine.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\HtmlAgilityPack.dll) { Remove-Item $umbracoBinFolder\HtmlAgilityPack.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ICSharpCode.SharpZipLib.dll) { Remove-Item $umbracoBinFolder\ICSharpCode.SharpZipLib.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.Razor.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.WebPages.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Deployment.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Deployment.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Razor.dll -Force -Confirm:$false }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.1.9
|
||||
Binary file not shown.
@@ -77,6 +77,8 @@
|
||||
<Compile Include="SqlCEUtility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -229,5 +229,15 @@ namespace SqlCE4Umbraco
|
||||
return new SqlCeDataReaderHelper(SqlCeApplicationBlock.ExecuteReader(ConnectionString, CommandType.Text,
|
||||
commandText, parameters));
|
||||
}
|
||||
|
||||
internal IRecordsReader ExecuteReader(string commandText)
|
||||
{
|
||||
return ExecuteReader(commandText, new SqlCEParameter(string.Empty, string.Empty));
|
||||
}
|
||||
|
||||
internal int ExecuteNonQuery(string commandText)
|
||||
{
|
||||
return ExecuteNonQuery(commandText, new SqlCEParameter(string.Empty, string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -74,7 +74,7 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
private bool ShouldExecute(ApplicationContext applicationContext)
|
||||
{
|
||||
if (applicationContext.IsConfigured && applicationContext.DatabaseContext.IsDatabaseConfigured)
|
||||
if (applicationContext.IsConfigured && applicationContext.DatabaseContext.CanConnect)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!applicationContext.DatabaseContext.IsDatabaseConfigured && ExecuteWhenDatabaseNotConfigured)
|
||||
if (!applicationContext.DatabaseContext.CanConnect && ExecuteWhenDatabaseNotConfigured)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,218 +1,204 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
{
|
||||
protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
protected abstract DictionaryCacheWrapper DictionaryCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears everything in umbraco's runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not clear other stuff the user has put in httpruntime.cache!
|
||||
/// </remarks>
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null)
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
if (DictionaryCache[GetCacheKey(key)] == null) return;
|
||||
DictionaryCache.Remove(GetCacheKey(key)); ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type name as the
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT;
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT && predicate(k, (T)v);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var keysToRemove = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
keysToRemove.Add(c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from object item in DictionaryCache
|
||||
select new DictionaryItemWrapper(item)
|
||||
into c
|
||||
where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
var found = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
found.Add(c.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = DictionaryCache.Get(GetCacheKey(cacheKey));
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
/// <summary>
|
||||
/// We prefix all cache keys with this so that we know which ones this class has created when
|
||||
/// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create.
|
||||
/// </summary>
|
||||
// prefix cache keys so we know which one are ours
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
// manupulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
// read-write lock the underlying cache
|
||||
protected abstract IDisposable ReadLock { get; }
|
||||
protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
using (WriteLock)
|
||||
{
|
||||
RemoveEntry(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
// run predicate on the 'public key' part only, ie without prefix
|
||||
&& predicate(((string)x.Key).Substring(plen), (T)value);
|
||||
}))
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
Lazy<object> result;
|
||||
using (ReadLock)
|
||||
{
|
||||
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryCacheWrapper : IEnumerable
|
||||
{
|
||||
private readonly IEnumerable _inner;
|
||||
private readonly Func<object, object> _get;
|
||||
private readonly Action<object> _remove;
|
||||
|
||||
public DictionaryCacheWrapper(
|
||||
IEnumerable inner,
|
||||
Func<object, object> get,
|
||||
Action<object> remove)
|
||||
{
|
||||
_inner = inner;
|
||||
_get = get;
|
||||
_remove = remove;
|
||||
}
|
||||
|
||||
public object this[object key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public object Get(object key)
|
||||
{
|
||||
return _get(key);
|
||||
}
|
||||
|
||||
public void Remove(object key)
|
||||
{
|
||||
_remove(key);
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return _inner.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryItemWrapper
|
||||
{
|
||||
public DictionaryItemWrapper(dynamic item)
|
||||
{
|
||||
Key = item.Key;
|
||||
Value = item.Value;
|
||||
}
|
||||
|
||||
public object Key { get; private set; }
|
||||
public object Value { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -12,36 +11,115 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
{
|
||||
private readonly Func<HttpContextBase> _context;
|
||||
// context provider
|
||||
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
||||
// that is initialized with a method that returns the "current" context.
|
||||
// NOTE
|
||||
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
||||
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
||||
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
||||
//private readonly Func<HttpContextBase> _context;
|
||||
|
||||
public HttpRequestCacheProvider(HttpContext context)
|
||||
// NOTE
|
||||
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
||||
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
||||
// _context1 is null?
|
||||
//private readonly HttpContextBase _context1;
|
||||
//private readonly Func<HttpContext> _context2;
|
||||
private readonly HttpContextBase _context;
|
||||
|
||||
private IDictionary ContextItems
|
||||
{
|
||||
_context = () => new HttpContextWrapper(context);
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
}
|
||||
|
||||
public HttpRequestCacheProvider(Func<HttpContextBase> context)
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
{
|
||||
//_context2 = context;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return ContextItems.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
ContextItems.Remove(key);
|
||||
}
|
||||
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
return ContextItems[key];
|
||||
}
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
// there's no difference between ReadLock and WriteLock here
|
||||
get { return WriteLock; }
|
||||
}
|
||||
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
// NOTE
|
||||
// could think about just overriding base.Locker to return a different
|
||||
// object but then we'd create a ReaderWriterLockSlim per request,
|
||||
// which is less efficient than just using a basic monitor lock.
|
||||
|
||||
get
|
||||
{
|
||||
var ctx = _context();
|
||||
return new DictionaryCacheWrapper(
|
||||
ctx.Items,
|
||||
o => ctx.Items[o],
|
||||
o => ctx.Items.Remove(o));
|
||||
return new MonitorLock(ContextItems.SyncRoot);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var ctx = _context();
|
||||
var ck = GetCacheKey(cacheKey);
|
||||
return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem());
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (WriteLock)
|
||||
{
|
||||
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
ContextItems[cacheKey] = result;
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Logging;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -14,48 +13,52 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
{
|
||||
// locker object that supports upgradeable read locking
|
||||
// does not need to support recursion if we implement the cache correctly and ensure
|
||||
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
private readonly DictionaryCacheWrapper _wrapper;
|
||||
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
_wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString()));
|
||||
}
|
||||
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
get { return _wrapper; }
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return _cache.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
get { return new ReadLock(_locker); }
|
||||
}
|
||||
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
get { return new WriteLock(_locker); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate
|
||||
/// </summary>
|
||||
public override void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
var key = c.Key.ToString();
|
||||
if (_cache[key] != null
|
||||
&& _cache[key] is T
|
||||
&& predicate(key, (T)_cache[key]))
|
||||
{
|
||||
_cache.Remove(c.Key.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<CacheHelper>("Cache clearing error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
@@ -81,25 +84,64 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
|
||||
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
|
||||
// nasty performance issues.
|
||||
|
||||
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
|
||||
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
|
||||
// application lock has been released.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may produce a null value.
|
||||
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
|
||||
// So if we find an entry in the cache that already has its value created and is null,
|
||||
// pretend it was not there. If value is not already created, wait... and return null, that's
|
||||
// what prior code did.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may throw.
|
||||
|
||||
// So... the null value _will_ be in the cache but never returned
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
// Fast!
|
||||
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
|
||||
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
|
||||
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
|
||||
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
|
||||
// it's worth it, though...
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
var result = DictionaryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
var value = result == null ? null : GetSafeLazyValue(result);
|
||||
if (value != null) return value;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
@@ -112,6 +154,10 @@ namespace Umbraco.Core.Cache
|
||||
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
@@ -124,15 +170,22 @@ namespace Umbraco.Core.Cache
|
||||
/// <param name="dependency"></param>
|
||||
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
var result = getCacheItem();
|
||||
if (result == null) return;
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
@@ -144,5 +197,7 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,37 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
{
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
MemoryCache.DisposeIfDisposable();
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
@@ -35,7 +55,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
if (MemoryCache[key] == null) return;
|
||||
MemoryCache.Remove(key);
|
||||
@@ -44,131 +64,179 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof (T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT)
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
&& predicate(x.Key, (T) value);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
return result;
|
||||
Lazy<object> result;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal,CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>;
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
object result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
new HttpRequestCacheProvider())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
new HttpRequestCacheProvider())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,5 +5,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
public interface IScheduledTasksSection : IUmbracoConfigurationSection
|
||||
{
|
||||
IEnumerable<IScheduledTask> Tasks { get; }
|
||||
|
||||
string BaseUrl { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,8 @@
|
||||
string ForcePortnumber { get; }
|
||||
string ForceProtocol { get; }
|
||||
string ServerAddress { get; }
|
||||
|
||||
string AppId { get; }
|
||||
string ServerName { get; }
|
||||
}
|
||||
}
|
||||
@@ -16,5 +16,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
get { return Tasks; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("baseUrl", IsRequired = false, DefaultValue = null)]
|
||||
public string BaseUrl
|
||||
{
|
||||
get { return (string)base["baseUrl"]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,5 +26,24 @@
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
|
||||
public string AppId
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("appId") == null
|
||||
? null
|
||||
: RawXml.Attribute("appId").Value;
|
||||
}
|
||||
}
|
||||
public string ServerName
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("serverName") == null
|
||||
? null
|
||||
: RawXml.Attribute("serverName").Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.1.2");
|
||||
private static readonly Version Version = new Version("7.1.9");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -279,6 +279,22 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string AltTemplate = "altTemplate";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the alias identifiers for Umbraco relation types.
|
||||
/// </summary>
|
||||
public static class RelationTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyName = "Relate Document On Copy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,55 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the constants used for Umbraco packages in the package.config xml
|
||||
/// </summary>
|
||||
public static class Packaging
|
||||
{
|
||||
public const string UmbPackageNodeName = "umbPackage";
|
||||
public const string DataTypesNodeName = "DataTypes";
|
||||
public const string PackageXmlFileName = "package.xml";
|
||||
public const string UmbracoPackageExtention = ".umb";
|
||||
public const string DataTypeNodeName = "DataType";
|
||||
public const string LanguagesNodeName = "Languages";
|
||||
public const string FilesNodeName = "Files";
|
||||
public const string StylesheetsNodeName = "Stylesheets";
|
||||
public const string TemplatesNodeName = "Templates";
|
||||
public const string OrgnameNodeName = "orgName";
|
||||
public const string NameNodeName = "Name";
|
||||
public const string TemplateNodeName = "Template";
|
||||
public const string AliasNodeName = "Alias";
|
||||
public const string DictionaryitemsNodeName = "DictionaryItems";
|
||||
public const string MacrosNodeName = "macros";
|
||||
public const string DocumentSetNodeName = "DocumentSet";
|
||||
public const string DocumentTypesNodeName = "DocumentTypes";
|
||||
public const string DocumentTypeNodeName = "DocumentType";
|
||||
public const string FileNodeName = "file";
|
||||
public const string OrgPathNodeName = "orgPath";
|
||||
public const string GuidNodeName = "guid";
|
||||
public const string StylesheetNodeName = "styleSheet";
|
||||
public const string MacroNodeName = "macro";
|
||||
public const string InfoNodeName = "info";
|
||||
public const string PackageRequirementsMajorXpath = "/package/requirements/major";
|
||||
public const string PackageRequirementsMinorXpath = "/package/requirements/minor";
|
||||
public const string PackageRequirementsPatchXpath = "/package/requirements/patch";
|
||||
public const string PackageNameXpath = "/package/name";
|
||||
public const string PackageVersionXpath = "/package/version";
|
||||
public const string PackageUrlXpath = "/package/url";
|
||||
public const string PackageLicenseXpath = "/package/license";
|
||||
public const string AuthorNameXpath = "/author/name";
|
||||
public const string AuthorWebsiteXpath = "/author/website";
|
||||
public const string ReadmeXpath = "/readme";
|
||||
public const string ControlNodeName = "control";
|
||||
public const string ActionNodeName = "Action";
|
||||
public const string ActionsNodeName = "Actions";
|
||||
public const string UndoNodeAttribute = "undo";
|
||||
public const string RunatNodeAttribute = "runat";
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the constants used for Umbraco packages in the package.config xml
|
||||
/// </summary>
|
||||
public static class Packaging
|
||||
{
|
||||
public const string UmbPackageNodeName = "umbPackage";
|
||||
public const string DataTypesNodeName = "DataTypes";
|
||||
public const string PackageXmlFileName = "package.xml";
|
||||
public const string UmbracoPackageExtention = ".umb";
|
||||
public const string DataTypeNodeName = "DataType";
|
||||
public const string LanguagesNodeName = "Languages";
|
||||
public const string FilesNodeName = "files";
|
||||
public const string StylesheetsNodeName = "Stylesheets";
|
||||
public const string TemplatesNodeName = "Templates";
|
||||
public const string NameNodeName = "Name";
|
||||
public const string TemplateNodeName = "Template";
|
||||
public const string AliasNodeNameSmall = "alias";
|
||||
public const string AliasNodeNameCapital = "Alias";
|
||||
public const string DictionaryItemsNodeName = "DictionaryItems";
|
||||
public const string DictionaryItemNodeName = "DictionaryItem";
|
||||
public const string MacrosNodeName = "Macros";
|
||||
public const string DocumentsNodeName = "Documents";
|
||||
public const string DocumentSetNodeName = "DocumentSet";
|
||||
public const string DocumentTypesNodeName = "DocumentTypes";
|
||||
public const string DocumentTypeNodeName = "DocumentType";
|
||||
public const string FileNodeName = "file";
|
||||
public const string OrgNameNodeName = "orgName";
|
||||
public const string OrgPathNodeName = "orgPath";
|
||||
public const string GuidNodeName = "guid";
|
||||
public const string StylesheetNodeName = "styleSheet";
|
||||
public const string MacroNodeName = "macro";
|
||||
public const string InfoNodeName = "info";
|
||||
public const string PackageRequirementsMajorXpath = "./package/requirements/major";
|
||||
public const string PackageRequirementsMinorXpath = "./package/requirements/minor";
|
||||
public const string PackageRequirementsPatchXpath = "./package/requirements/patch";
|
||||
public const string PackageNameXpath = "./package/name";
|
||||
public const string PackageVersionXpath = "./package/version";
|
||||
public const string PackageUrlXpath = "./package/url";
|
||||
public const string PackageLicenseXpath = "./package/license";
|
||||
public const string PackageLicenseXpathUrlAttribute = "url";
|
||||
public const string AuthorNameXpath = "./author/name";
|
||||
public const string AuthorWebsiteXpath = "./author/website";
|
||||
public const string ReadmeXpath = "./readme";
|
||||
public const string ControlNodeName = "control";
|
||||
public const string ActionNodeName = "Action";
|
||||
public const string ActionsNodeName = "Actions";
|
||||
public const string UndoNodeAttribute = "undo";
|
||||
public const string RunatNodeAttribute = "runat";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,9 +329,8 @@ namespace Umbraco.Core
|
||||
UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver(
|
||||
typeof (DefaultUrlSegmentProvider));
|
||||
|
||||
// keep it internal for now
|
||||
// by default, no factory is activated
|
||||
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
|
||||
// new PublishedContentModelFactoryImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace Umbraco.Core
|
||||
{
|
||||
private readonly IDatabaseFactory _factory;
|
||||
private bool _configured;
|
||||
private bool _canConnect;
|
||||
private volatile bool _connectCheck = false;
|
||||
private readonly object _locker = new object();
|
||||
private string _connectionString;
|
||||
private string _providerName;
|
||||
private DatabaseSchemaResult _result;
|
||||
@@ -56,6 +59,32 @@ namespace Umbraco.Core
|
||||
get { return _configured; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the db can be connected to
|
||||
/// </summary>
|
||||
public bool CanConnect
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsDatabaseConfigured == false) return false;
|
||||
|
||||
//double check lock so that it is only checked once and is fast
|
||||
if (_connectCheck == false)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_canConnect == false)
|
||||
{
|
||||
_canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
|
||||
_connectCheck = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _canConnect;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured umbraco db connection string.
|
||||
/// </summary>
|
||||
|
||||
@@ -117,13 +117,12 @@ namespace Umbraco.Core.Dynamics
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var sb = new StringBuilder("An error occurred finding an executing an extension method for type ");
|
||||
sb.Append(typeof (T));
|
||||
sb.Append("Types searched for extension methods were ");
|
||||
foreach(var t in findExtensionMethodsOnTypes)
|
||||
{
|
||||
sb.Append(t + ",");
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("An error occurred finding and executing extension method \"{0}\" ", binder.Name);
|
||||
sb.AppendFormat("for type \"{0}\". ", typeof (T));
|
||||
sb.Append("Types searched for extension methods were ");
|
||||
sb.Append(string.Join(", ", findExtensionMethodsOnTypes));
|
||||
sb.Append(".");
|
||||
LogHelper.Error<DynamicInstanceHelper>(sb.ToString(), ex);
|
||||
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(mresult, ex);
|
||||
@@ -171,7 +170,7 @@ namespace Umbraco.Core.Dynamics
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MissingMethodException();
|
||||
throw new MissingMethodException(typeof(T).FullName, name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,47 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId) : base(original, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId)
|
||||
: base(original, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId) : base(eventObject)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId)
|
||||
: base(eventObject)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The copied entity
|
||||
/// </summary>
|
||||
public TEntity Copy { get; set; }
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, bool canCancel, int parentId, bool relateToOriginal)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
RelateToOriginal = relateToOriginal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The original entity
|
||||
/// </summary>
|
||||
public TEntity Original
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The copied entity
|
||||
/// </summary>
|
||||
public TEntity Copy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The original entity
|
||||
/// </summary>
|
||||
public TEntity Original
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
|
||||
public bool RelateToOriginal { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
{
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.Migrations;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IEnumerable<IMigration>>
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ConfiguredVersion = configuredVersion;
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
internal MigrationEventArgs(IEnumerable<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
internal MigrationEventArgs(IList<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
MigrationContext = migrationContext;
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion)
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion)
|
||||
: base(eventObject)
|
||||
{
|
||||
ConfiguredVersion = configuredVersion;
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Returns all migrations that were used in the migration runner
|
||||
/// </summary>
|
||||
public IEnumerable<IMigration> Migrations
|
||||
public IList<IMigration> Migrations
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ namespace Umbraco.Core
|
||||
AddInt(d.GetHashCode());
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
|
||||
}
|
||||
|
||||
internal void AddCaseInsensitiveString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -19,7 +18,10 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static void CopyFile(this IFileSystem fs, string path, string newPath)
|
||||
{
|
||||
fs.AddFile(newPath, fs.OpenFile(path));
|
||||
using (var stream = fs.OpenFile(path))
|
||||
{
|
||||
fs.AddFile(newPath, stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetExtension(this IFileSystem fs, string path)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
|
||||
@@ -3,10 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -93,8 +90,9 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideIfExists)
|
||||
{
|
||||
if (FileExists(path) && !overrideIfExists) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
var exists = FileExists(path);
|
||||
if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
EnsureDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
if (stream.CanSeek)
|
||||
|
||||
@@ -154,19 +154,18 @@ namespace Umbraco.Core.IO
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = new Size(-1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _size.Value;
|
||||
}
|
||||
@@ -179,7 +178,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
var fileNameThumb = DoResize(width, height, -1, string.Empty);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -192,7 +191,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
var fileNameThumb = DoResize(-1, -1, maxWidthHeight, fileNameAddition);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -206,14 +205,14 @@ namespace Umbraco.Core.IO
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
|
||||
var thumbnail = maxWidthHeight == -1
|
||||
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
|
||||
: ImageHelper.GenerateThumbnail(image, maxWidthHeight, fileNameThumb, Extension, _fs);
|
||||
|
||||
return thumbnail.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileSupportsResizing()
|
||||
@@ -222,6 +221,6 @@ namespace Umbraco.Core.IO
|
||||
throw new InvalidOperationException(string.Format("The file {0} is not an image, so can't get dimensions", Filename));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
internal class ManifestWatcher : DisposableObject
|
||||
{
|
||||
private readonly List<FileSystemWatcher> _fws = new List<FileSystemWatcher>();
|
||||
private static volatile bool _isRestarting = false;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
public void Start(params string[] packageFolders)
|
||||
{
|
||||
foreach (var packageFolder in packageFolders)
|
||||
{
|
||||
if (Directory.Exists(packageFolder) && File.Exists(Path.Combine(packageFolder, "package.manifest")))
|
||||
{
|
||||
//NOTE: for some reason *.manifest doesn't work!
|
||||
var fsw = new FileSystemWatcher(packageFolder, "*package.*")
|
||||
{
|
||||
IncludeSubdirectories = false,
|
||||
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
|
||||
};
|
||||
_fws.Add(fsw);
|
||||
fsw.Changed += FswChanged;
|
||||
fsw.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FswChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (e.Name.InvariantContains("package.manifest"))
|
||||
{
|
||||
//Ensure the app is not restarted multiple times for multiple saving during the same app domain execution
|
||||
if (_isRestarting == false)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_isRestarting == false)
|
||||
{
|
||||
_isRestarting = true;
|
||||
|
||||
LogHelper.Info<ManifestWatcher>("manifest has changed, app pool is restarting (" + e.FullPath + ")");
|
||||
HttpRuntime.UnloadAppDomain();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
foreach (var fw in _fws)
|
||||
{
|
||||
fw.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,14 +44,18 @@ namespace Umbraco.Core.Media
|
||||
|
||||
var result = new List<ResizedImage>();
|
||||
|
||||
var allSizes = new List<int> {100, 500};
|
||||
var allSizesDictionary = new Dictionary<int,string> {{100,"thumb"}, {500,"big-thumb"}};
|
||||
var allSizes = allSizesDictionary.Select(kv => kv.Key).ToList();
|
||||
allSizes.AddRange(additionalThumbSizes.Where(x => x > 0).Distinct());
|
||||
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s]: "");
|
||||
|
||||
foreach (var s in allSizes)
|
||||
foreach (var s in sizesDictionary)
|
||||
{
|
||||
if (originalImage.Width >= s && originalImage.Height >= s)
|
||||
var size = s.Key;
|
||||
var name = s.Value;
|
||||
if (originalImage.Width >= size && originalImage.Height >= size)
|
||||
{
|
||||
result.Add(Resize(fs, fileName, extension, s, "thumb", originalImage));
|
||||
result.Add(Resize(fs, fileName, extension, size, name, originalImage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +165,16 @@ namespace Umbraco.Core.Media
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
}
|
||||
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
|
||||
@@ -9,6 +9,8 @@ using System.Linq;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -238,6 +240,28 @@ namespace Umbraco.Core.Models
|
||||
return content.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Contains(recycleBinId.ToInvariantString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes characters that are not valide XML characters from all entity properties
|
||||
/// of type string. See: http://stackoverflow.com/a/961504/5018
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
|
||||
/// </remarks>
|
||||
/// <param name="entity"></param>
|
||||
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
|
||||
{
|
||||
entity.Name = entity.Name.ToValidXmlString();
|
||||
foreach (var property in entity.Properties)
|
||||
{
|
||||
if (property.Value is string)
|
||||
{
|
||||
var value = (string)property.Value;
|
||||
property.Value = value.ToValidXmlString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the IContentBase has children
|
||||
@@ -539,7 +563,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
|
||||
}
|
||||
|
||||
|
||||
#region Tag methods
|
||||
|
||||
///// <summary>
|
||||
@@ -567,12 +591,33 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
|
||||
/// <returns></returns>
|
||||
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
|
||||
/// </summary>
|
||||
/// <param name="content">The content item to assign the tags to</param>
|
||||
/// <param name="storageType">The tag storage type in cache (default is csv)</param>
|
||||
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
|
||||
/// <param name="tags">The tags to assign</param>
|
||||
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
|
||||
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
|
||||
/// <returns></returns>
|
||||
public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
var property = content.Properties[propertyTypeAlias];
|
||||
if (property == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup);
|
||||
}
|
||||
|
||||
internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
if (property == null) throw new ArgumentNullException("property");
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
@@ -583,15 +628,39 @@ namespace Umbraco.Core.Models
|
||||
//ensure the property value is set to the same thing
|
||||
if (replaceTags)
|
||||
{
|
||||
property.Value = string.Join(",", trimmedTags);
|
||||
switch (storageType)
|
||||
{
|
||||
case TagCacheStorageType.Csv:
|
||||
property.Value = string.Join(",", trimmedTags);
|
||||
break;
|
||||
case TagCacheStorageType.Json:
|
||||
//json array
|
||||
property.Value = JsonConvert.SerializeObject(trimmedTags);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var currTags = property.Value.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
|
||||
switch (storageType)
|
||||
{
|
||||
case TagCacheStorageType.Csv:
|
||||
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim());
|
||||
property.Value = string.Join(",", trimmedTags.Union(currTags));
|
||||
property.Value = string.Join(",", trimmedTags.Union(currTags));
|
||||
break;
|
||||
case TagCacheStorageType.Json:
|
||||
var currJson = JsonConvert.DeserializeObject<JArray>(property.Value.ToString());
|
||||
//need to append the new ones
|
||||
foreach (var tag in trimmedTags)
|
||||
{
|
||||
currJson.Add(tag);
|
||||
}
|
||||
//json array
|
||||
property.Value = JsonConvert.SerializeObject(currJson);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -664,7 +733,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
@@ -687,10 +756,6 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -133,29 +133,6 @@ namespace Umbraco.Core.Models
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentType)base.DeepClone();
|
||||
var propertyGroups = PropertyGroups.Select(x => (PropertyGroup)x.DeepClone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
//set the property types that are not part of a group
|
||||
clone.PropertyTypes = PropertyTypeCollection
|
||||
.Where(x => x.PropertyGroupId == null)
|
||||
.Select(x => (PropertyType)x.DeepClone()).ToList();
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
|
||||
@@ -331,11 +331,6 @@ namespace Umbraco.Core.Models
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of integer Ids for allowed ContentTypes
|
||||
/// </summary>
|
||||
@@ -356,7 +351,9 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this ContentType
|
||||
/// </summary>
|
||||
/// <remarks>A PropertyGroup corresponds to a Tab in the UI</remarks>
|
||||
/// <remarks>
|
||||
/// A PropertyGroup corresponds to a Tab in the UI
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual PropertyGroupCollection PropertyGroups
|
||||
{
|
||||
@@ -372,7 +369,13 @@ namespace Umbraco.Core.Models
|
||||
/// List of PropertyTypes available on this ContentType.
|
||||
/// This list aggregates PropertyTypes across the PropertyGroups.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
|
||||
/// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
|
||||
/// data. We manually clone this instead.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DoNotClone]
|
||||
public virtual IEnumerable<PropertyType> PropertyTypes
|
||||
{
|
||||
get
|
||||
@@ -388,6 +391,14 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property type collection containing types that are non-groups - used for tests
|
||||
/// </summary>
|
||||
internal IEnumerable<PropertyType> NonGroupedPropertyTypes
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A boolean flag indicating if a property type has been removed from this instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -584,5 +595,19 @@ namespace Umbraco.Core.Models
|
||||
propertyType.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeBase)base.DeepClone();
|
||||
|
||||
//need to manually wire up the event handlers for the property type collections - we've ensured
|
||||
// its ignored from the auto-clone process because its return values are unions, not raw and
|
||||
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
|
||||
|
||||
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
|
||||
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to attribute properties that have a setter and are a reference type
|
||||
/// that should be ignored for cloning when using the DeepCloneHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// This attribute must be used:
|
||||
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
|
||||
///
|
||||
/// This attribute should not be used:
|
||||
/// * when the property is virtual
|
||||
/// * when the setter performs additional required logic other than just setting the underlying field
|
||||
///
|
||||
/// </remarks>
|
||||
internal class DoNotCloneAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class DeepCloneHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to avoid constant reflection (perf)
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
|
||||
/// </summary>
|
||||
@@ -23,14 +49,18 @@ namespace Umbraco.Core.Models
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
var refProperties = inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//reference type but not string
|
||||
x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false);
|
||||
var refProperties = PropCache.GetOrAdd(inputType, type =>
|
||||
inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
x.GetCustomAttribute<DoNotCloneAttribute>() == null
|
||||
//reference type but not string
|
||||
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false)
|
||||
.ToArray());
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
{
|
||||
|
||||
@@ -240,52 +240,6 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
|
||||
//Using data contract serializer - has issues
|
||||
|
||||
//var s = Serialize(this);
|
||||
//var d = Deserialize(s, this.GetType());
|
||||
//return d;
|
||||
|
||||
//Using binary serializer - has issues
|
||||
|
||||
//using (var memoryStream = new MemoryStream(10))
|
||||
//{
|
||||
//IFormatter formatter = new BinaryFormatter();
|
||||
//formatter.Serialize(memoryStream, this);
|
||||
//memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
//return formatter.Deserialize(memoryStream);
|
||||
//}
|
||||
}
|
||||
|
||||
// serialize/deserialize with data contracts:
|
||||
|
||||
//public static string Serialize(object obj)
|
||||
//{
|
||||
// using (var memoryStream = new MemoryStream())
|
||||
// using (var reader = new StreamReader(memoryStream))
|
||||
// {
|
||||
// var serializer = new DataContractSerializer(obj.GetType());
|
||||
// serializer.WriteObject(memoryStream, obj);
|
||||
// memoryStream.Position = 0;
|
||||
// return reader.ReadToEnd();
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static object Deserialize(string xml, Type toType)
|
||||
//{
|
||||
// using (Stream stream = new MemoryStream())
|
||||
// {
|
||||
// using (var writer = new StreamWriter(stream, Encoding.UTF8))
|
||||
// {
|
||||
// writer.Write(xml);
|
||||
// //byte[] data = Encoding.UTF8.GetBytes(xml);
|
||||
// //stream.Write(data, 0, data.Length);
|
||||
// stream.Position = 0;
|
||||
// var deserializer = new DataContractSerializer(toType);
|
||||
// return deserializer.ReadObject(stream);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -23,5 +20,5 @@ namespace Umbraco.Core.Models
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Defines a Property for a Macro
|
||||
/// </summary>
|
||||
public interface IMacroProperty : IValueObject
|
||||
public interface IMacroProperty : IValueObject, IDeepCloneable, IRememberBeingDirty
|
||||
{
|
||||
[DataMember]
|
||||
int Id { get; set; }
|
||||
|
||||
@@ -3,6 +3,9 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Tag, which is composed of a Text, Group and NodeCount property.
|
||||
/// </summary>
|
||||
public interface ITag : IAggregateRoot
|
||||
{
|
||||
[DataMember]
|
||||
@@ -11,6 +14,8 @@ namespace Umbraco.Core.Models
|
||||
[DataMember]
|
||||
string Group { get; set; }
|
||||
|
||||
int NodeCount { get; }
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//int ParentId { get; set; }
|
||||
}
|
||||
|
||||
@@ -138,12 +138,11 @@ namespace Umbraco.Core.Models
|
||||
|
||||
var alias = prop.Alias;
|
||||
|
||||
//remove from the removed/added props (since people could add/remove all they want in one request)
|
||||
_removedProperties.RemoveAll(s => s == alias);
|
||||
_addedProperties.RemoveAll(s => s == alias);
|
||||
|
||||
//add to the added props
|
||||
_addedProperties.Add(alias);
|
||||
if (_addedProperties.Contains(alias) == false)
|
||||
{
|
||||
//add to the added props
|
||||
_addedProperties.Add(alias);
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
@@ -153,12 +152,10 @@ namespace Umbraco.Core.Models
|
||||
|
||||
var alias = prop.Alias;
|
||||
|
||||
//remove from the removed/added props (since people could add/remove all they want in one request)
|
||||
_removedProperties.RemoveAll(s => s == alias);
|
||||
_addedProperties.RemoveAll(s => s == alias);
|
||||
|
||||
//add to the added props
|
||||
_removedProperties.Add(alias);
|
||||
if (_removedProperties.Contains(alias) == false)
|
||||
{
|
||||
_removedProperties.Add(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +401,8 @@ namespace Umbraco.Core.Models
|
||||
|
||||
clone._addedProperties = new List<string>();
|
||||
clone._removedProperties = new List<string>();
|
||||
clone._properties = new MacroPropertyCollection();
|
||||
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
|
||||
//re-assign the event handler
|
||||
clone._properties.CollectionChanged += clone.PropertiesChanged;
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty
|
||||
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty, IDeepCloneable
|
||||
{
|
||||
public MacroProperty()
|
||||
{
|
||||
@@ -176,5 +176,37 @@ namespace Umbraco.Core.Models
|
||||
}, _editorAlias, PropertyTypeSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
//Memberwise clone on MacroProperty will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var clone = (MacroProperty)MemberwiseClone();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(MacroProperty other)
|
||||
{
|
||||
return string.Equals(_alias, other._alias) && _id == other._id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((MacroProperty) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((_alias != null ? _alias.GetHashCode() : 0)*397) ^ _id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,60 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// A macro's property collection
|
||||
/// </summary>
|
||||
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>
|
||||
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>, IDeepCloneable
|
||||
{
|
||||
public MacroPropertyCollection()
|
||||
: base(property => property.Alias)
|
||||
{
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = new MacroPropertyCollection();
|
||||
foreach (var item in this)
|
||||
{
|
||||
clone.Add((IMacroProperty)item.DeepClone());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to update an existing macro property
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="sortOrder"></param>
|
||||
/// <param name="editorAlias"></param>
|
||||
/// <param name="currentAlias">
|
||||
/// The existing property alias
|
||||
/// </param>
|
||||
/// <param name="newAlias"></param>
|
||||
public void UpdateProperty(string currentAlias, string name = null, int? sortOrder = null, string editorAlias = null, string newAlias = null)
|
||||
{
|
||||
var prop = this[currentAlias];
|
||||
if (prop == null)
|
||||
{
|
||||
throw new InvalidOperationException("No property exists with alias " + currentAlias);
|
||||
}
|
||||
|
||||
if (name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
prop.Name = name;
|
||||
}
|
||||
if (sortOrder.HasValue)
|
||||
{
|
||||
prop.SortOrder = sortOrder.Value;
|
||||
}
|
||||
if (name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
prop.EditorAlias = editorAlias;
|
||||
}
|
||||
|
||||
if (newAlias.IsNullOrWhiteSpace() == false && currentAlias != newAlias)
|
||||
{
|
||||
prop.Alias = newAlias;
|
||||
ChangeKey(currentAlias, newAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
using System.Collections;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user -> entity permission
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entity -> user & permission key value pair collection
|
||||
/// </summary>
|
||||
public class EntityPermissionSet
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity id with permissions assigned
|
||||
/// </summary>
|
||||
public int EntityId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key/value pairs of user id & single permission
|
||||
/// </summary>
|
||||
public IEnumerable<UserPermission> UserPermissionsSet { get; private set; }
|
||||
|
||||
public EntityPermissionSet(int entityId, IEnumerable<UserPermission> userPermissionsSet)
|
||||
{
|
||||
EntityId = entityId;
|
||||
UserPermissionsSet = userPermissionsSet;
|
||||
}
|
||||
|
||||
public class UserPermission
|
||||
{
|
||||
public UserPermission(int userId, string permission)
|
||||
{
|
||||
UserId = userId;
|
||||
Permission = permission;
|
||||
}
|
||||
|
||||
public int UserId { get; private set; }
|
||||
public string Permission { get; private set; }
|
||||
|
||||
protected bool Equals(UserPermission other)
|
||||
{
|
||||
return UserId == other.UserId && string.Equals(Permission, other.Permission);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((UserPermission) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (UserId*397) ^ Permission.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the base contract for <see cref="IMember"/> and <see cref="IUser"/>
|
||||
/// </summary>
|
||||
public interface IMembershipUser : IAggregateRoot
|
||||
{
|
||||
object ProviderUserKey { get; set; }
|
||||
|
||||
@@ -1,27 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Type for a Backoffice User
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserType : Entity, IUserType
|
||||
{
|
||||
[DataMember]
|
||||
public string Alias { get; set; }
|
||||
private string _alias;
|
||||
private string _name;
|
||||
private IEnumerable<string> _permissions;
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Name);
|
||||
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Alias);
|
||||
private static readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserType, IEnumerable<string>>(x => x.Permissions);
|
||||
|
||||
[DataMember]
|
||||
public string Name { get; set; }
|
||||
public string Alias
|
||||
{
|
||||
get { return _alias; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The set of default permissions for the user type
|
||||
@@ -30,6 +58,17 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public IEnumerable<string> Permissions { get; set; }
|
||||
public IEnumerable<string> Permissions
|
||||
{
|
||||
get { return _permissions; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_permissions = value;
|
||||
return _permissions;
|
||||
}, _permissions, PermissionsSelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Partial View file
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PartialView : File
|
||||
{
|
||||
private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
|
||||
public PartialView(string path)
|
||||
: base(path)
|
||||
{
|
||||
base.Path = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the file could be validated
|
||||
/// </summary>
|
||||
/// <returns>True if file is valid, otherwise false</returns>
|
||||
public override bool IsValid()
|
||||
{
|
||||
//TODO: Validate using the macro engine
|
||||
//var engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName);
|
||||
//engine.Validate(...)
|
||||
|
||||
var validatePath = IOHelper.ValidateEditPath(IOHelper.MapPath(Path), BasePath);
|
||||
var verifyFileExtension = IOHelper.VerifyFileExtension(Path, new List<string> { "cshtml" });
|
||||
|
||||
return validatePath && verifyFileExtension;
|
||||
}
|
||||
|
||||
public string OldFileName { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public string SnippetName { get; set; }
|
||||
|
||||
public bool CreateMacro { get; set; }
|
||||
|
||||
public string CodeHeader { get; set; }
|
||||
|
||||
public string ParentFolderName { get; set; }
|
||||
|
||||
public string EditViewFile { get; set; }
|
||||
|
||||
public string BasePath { get; set; }
|
||||
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
internal Regex HeaderMatch
|
||||
{
|
||||
get { return _headerMatch; }
|
||||
}
|
||||
|
||||
internal Attempt<string> TryGetSnippetPath(string fileName)
|
||||
{
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(BasePath);
|
||||
var snippetPath = IOHelper.MapPath(string.Format("{0}/PartialViewMacros/Templates/{1}", SystemDirectories.Umbraco, fileName));
|
||||
|
||||
return partialViewsFileSystem.FileExists(snippetPath)
|
||||
? Attempt<string>.Succeed(snippetPath)
|
||||
: Attempt<string>.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Represents a stored pre-value field value
|
||||
/// </summary>
|
||||
public class PreValue
|
||||
public class PreValue : IDeepCloneable
|
||||
{
|
||||
public PreValue(int id, string value, int sortOrder)
|
||||
{
|
||||
@@ -37,5 +37,12 @@
|
||||
/// The sort order stored for the pre-value field value
|
||||
/// </summary>
|
||||
public int SortOrder { get; private set; }
|
||||
|
||||
public virtual object DeepClone()
|
||||
{
|
||||
//Memberwise clone on PreValue will work since it doesn't have any deep elements
|
||||
var clone = (PreValue)MemberwiseClone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
|
||||
/// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format.
|
||||
/// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key.
|
||||
/// </remarks>
|
||||
public class PreValueCollection
|
||||
public class PreValueCollection : IDeepCloneable
|
||||
{
|
||||
private IDictionary<string, PreValue> _preValuesAsDictionary;
|
||||
private IEnumerable<PreValue> _preValuesAsArray;
|
||||
@@ -82,5 +82,21 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (PreValueCollection) MemberwiseClone();
|
||||
if (_preValuesAsArray != null)
|
||||
{
|
||||
clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue)x.DeepClone()).ToArray();
|
||||
}
|
||||
if (_preValuesAsDictionary != null)
|
||||
{
|
||||
clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue)x.Value.DeepClone());
|
||||
}
|
||||
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal enum PropertyTagBehavior
|
||||
{
|
||||
Replace,
|
||||
Remove,
|
||||
Merge
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,4 @@ namespace Umbraco.Core.Models
|
||||
public IEnumerable<Tuple<string, string>> Tags { get; set; }
|
||||
|
||||
}
|
||||
|
||||
internal enum PropertyTagBehavior
|
||||
{
|
||||
Replace,
|
||||
Remove,
|
||||
Merge
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,14 @@ namespace Umbraco.Core.Models
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//check if the item's sort order is already in use
|
||||
if (this.Any(x => x.SortOrder == item.SortOrder))
|
||||
{
|
||||
//make it the next iteration
|
||||
item.SortOrder = this.Max(x => x.SortOrder) + 1;
|
||||
}
|
||||
|
||||
base.Add(item);
|
||||
OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Provides the model creation service.
|
||||
/// </summary>
|
||||
internal interface IPublishedContentModelFactory
|
||||
public interface IPublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly-typed model representing a published content.
|
||||
|
||||
@@ -43,6 +43,11 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
// first unwrap content down to the lowest possible level, ie either the deepest inner
|
||||
// IPublishedContent or the first extended that has added properties. this is to avoid
|
||||
// nesting extended objects as much as possible, so we try to re-extend that lowest
|
||||
// object.
|
||||
|
||||
var wrapped = content as PublishedContentExtended;
|
||||
while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
|
||||
wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
|
||||
@@ -51,14 +56,38 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
// a model, and then that model has to inherit from PublishedContentExtended,
|
||||
// => implements the internal IPublishedContentExtended.
|
||||
|
||||
var model = PublishedContentModelFactory.CreateModel(content);
|
||||
// here we assume that either the factory just created a model that implements
|
||||
// IPublishedContentExtended and therefore does not need to be extended again,
|
||||
// because it can carry the extra property - or that it did *not* create a
|
||||
// model and therefore returned the original content unchanged.
|
||||
|
||||
var model = content.CreateModel();
|
||||
var extended = model == content // == means the factory did not create a model
|
||||
? new PublishedContentExtended(content) // so we have to extend
|
||||
: model; // else we can use what the factory returned
|
||||
|
||||
// so extended should always implement IPublishedContentExtended, however if
|
||||
// by mistake the factory returned a different object that does not implement
|
||||
// IPublishedContentExtended (which would be an error), throw.
|
||||
//
|
||||
// see also PublishedContentExtensionsForModels.CreateModel
|
||||
|
||||
// NOTE
|
||||
// could we lift that constraint and accept that models just be IPublishedContent?
|
||||
// would then mean that we cannot assume a model is IPublishedContentExtended, so
|
||||
// either it is, or we need to wrap it. so instead of having
|
||||
// (Model:IPublishedContentExtended (IPublishedContent))
|
||||
// we'd have
|
||||
// (PublishedContentExtended (Model (IPublishedContent)))
|
||||
// and it is that bad? any other consequences?
|
||||
//
|
||||
// would also allow the factory to cache the model (though that should really
|
||||
// be done by the content cache, not by the factory).
|
||||
|
||||
var extended2 = extended as IPublishedContentExtended;
|
||||
if (extended2 != null) // always true, but keeps Resharper happy
|
||||
extended2.SetContentSet(contentSet);
|
||||
if (extended2 == null)
|
||||
throw new Exception("Extended does not implement IPublishedContentExtended.");
|
||||
extended2.SetContentSet(contentSet);
|
||||
return extended2;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
/// </summary>
|
||||
internal static class PublishedContentExtensionsForModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly typed published content model for an internal published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The internal published content.</param>
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(this IPublishedContent content)
|
||||
{
|
||||
if (content == null)
|
||||
return null;
|
||||
|
||||
if (PublishedContentModelFactoryResolver.Current.HasValue == false)
|
||||
return content;
|
||||
|
||||
// get model
|
||||
// if factory returns nothing, throw
|
||||
// if factory just returns what it got, return
|
||||
var model = PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content);
|
||||
if (model == null)
|
||||
throw new Exception("IPublishedContentFactory returned null.");
|
||||
if (ReferenceEquals(model, content))
|
||||
return content;
|
||||
|
||||
// at the moment, other parts of our code assume that all models will
|
||||
// somehow implement IPublishedContentExtended and not just be IPublishedContent,
|
||||
// so we'd better check this here to fail as soon as we can.
|
||||
//
|
||||
// see also PublishedContentExtended.Extend
|
||||
|
||||
var extended = model as IPublishedContentExtended;
|
||||
if (extended == null)
|
||||
throw new Exception("IPublishedContentFactory created an object that does not implement IPublishedContentModelExtended.");
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// </summary>
|
||||
/// <remarks>Every strongly-typed published content class should inherit from <c>PublishedContentModel</c>
|
||||
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
|
||||
internal abstract class PublishedContentModel : PublishedContentExtended
|
||||
public abstract class PublishedContentModel : PublishedContentExtended
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModel"/> class with
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
|
||||
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class PublishedContentModelAttribute : Attribute
|
||||
public sealed class PublishedContentModelAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute"/> class with a content type alias.
|
||||
|
||||
@@ -1,20 +1,85 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
/// </summary>
|
||||
internal static class PublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly typed published content model for an internal published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The internal published content.</param>
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
return PublishedContentModelFactoryResolver.Current.HasValue
|
||||
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a strongly typed content model factory
|
||||
/// </summary>
|
||||
public class PublishedContentModelFactory : IPublishedContentModelFactory
|
||||
{
|
||||
//private readonly Dictionary<string, ConstructorInfo> _constructors
|
||||
// = new Dictionary<string, ConstructorInfo>();
|
||||
|
||||
private readonly Dictionary<string, Func<IPublishedContent, IPublishedContent>> _constructors;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactory"/> class with types.
|
||||
/// </summary>
|
||||
/// <param name="types">The model types.</param>
|
||||
/// <remarks>
|
||||
/// <para>Types must implement <c>IPublishedContent</c> and have a unique constructor that
|
||||
/// accepts one IPublishedContent as a parameter.</para>
|
||||
/// <para>To activate,</para>
|
||||
/// <code>
|
||||
/// var types = PluginManager.Current.ResolveTypes{PublishedContentModel}();
|
||||
/// var factory = new PublishedContentModelFactoryImpl(types);
|
||||
/// PublishedContentModelFactoryResolver.Current.SetFactory(factory);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public PublishedContentModelFactory(IEnumerable<Type> types)
|
||||
{
|
||||
var ctorArgTypes = new[] { typeof(IPublishedContent) };
|
||||
var constructors = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var constructor = type.GetConstructor(ctorArgTypes);
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
|
||||
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
|
||||
if (constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
|
||||
// should work everywhere, but slow
|
||||
//_constructors[typeName] = constructor;
|
||||
|
||||
// much faster with a dynamic method but potential MediumTrust issues
|
||||
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
|
||||
|
||||
// fast enough and works in MediumTrust
|
||||
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
|
||||
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
|
||||
var exprNew = Expression.New(constructor, exprArg);
|
||||
var expr = Expression.Lambda<Func<IPublishedContent, IPublishedContent>>(exprNew, exprArg);
|
||||
var func = expr.Compile();
|
||||
constructors[typeName] = func;
|
||||
}
|
||||
|
||||
_constructors = constructors.Count > 0 ? constructors : null;
|
||||
}
|
||||
|
||||
public IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
// fail fast
|
||||
if (_constructors == null)
|
||||
return content;
|
||||
|
||||
// be case-insensitive
|
||||
var contentTypeAlias = content.DocumentTypeAlias;
|
||||
|
||||
//ConstructorInfo constructor;
|
||||
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
|
||||
// : content;
|
||||
|
||||
Func<IPublishedContent, IPublishedContent> constructor;
|
||||
return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
? constructor(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a strongly typed content model factory
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory
|
||||
{
|
||||
//private readonly Dictionary<string, ConstructorInfo> _constructors
|
||||
// = new Dictionary<string, ConstructorInfo>();
|
||||
|
||||
private readonly Dictionary<string, Func<IPublishedContent, IPublishedContent>> _constructors
|
||||
= new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
|
||||
|
||||
public PublishedContentModelFactoryImpl()
|
||||
{
|
||||
var types = PluginManager.Current.ResolveTypes<PublishedContentModel>();
|
||||
var ctorArgTypes = new[] { typeof(IPublishedContent) };
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var constructor = type.GetConstructor(ctorArgTypes);
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
|
||||
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
typeName = typeName.ToLowerInvariant();
|
||||
|
||||
if (_constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
|
||||
// should work everywhere, but slow
|
||||
//_constructors[typeName] = constructor;
|
||||
|
||||
// much faster with a dynamic method but potential MediumTrust issues
|
||||
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
|
||||
|
||||
// fast enough and works in MediumTrust
|
||||
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
|
||||
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
|
||||
var exprNew = Expression.New(constructor, exprArg);
|
||||
var expr = Expression.Lambda<Func<IPublishedContent, IPublishedContent>>(exprNew, exprArg);
|
||||
var func = expr.Compile();
|
||||
_constructors[typeName] = func;
|
||||
}
|
||||
}
|
||||
|
||||
public IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
// be case-insensitive
|
||||
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
|
||||
|
||||
//ConstructorInfo constructor;
|
||||
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
|
||||
// : content;
|
||||
|
||||
Func<IPublishedContent, IPublishedContent> constructor;
|
||||
return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
? constructor(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <summary>
|
||||
/// Resolves the IPublishedContentModelFactory object.
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase<PublishedContentModelFactoryResolver, IPublishedContentModelFactory>
|
||||
public class PublishedContentModelFactoryResolver : SingleObjectResolverBase<PublishedContentModelFactoryResolver, IPublishedContentModelFactory>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/>.
|
||||
|
||||
@@ -44,6 +44,11 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
InitializeIndexes();
|
||||
}
|
||||
|
||||
// create detached content type - ie does not match anything in the DB
|
||||
internal PublishedContentType(string alias, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: this (0, alias, propertyTypes)
|
||||
{ }
|
||||
|
||||
private void InitializeIndexes()
|
||||
{
|
||||
for (var i = 0; i < _propertyTypes.Length; i++)
|
||||
|
||||
@@ -222,19 +222,38 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
var converterMeta = _converter as IPropertyValueConverterMeta;
|
||||
|
||||
// get the cache levels, quietely fixing the inconsistencies (no need to throw, really)
|
||||
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
|
||||
if (converterMeta != null)
|
||||
{
|
||||
_sourceCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
|
||||
}
|
||||
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
|
||||
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
|
||||
|
||||
// get the CLR type of the converted value
|
||||
if (_converter != null)
|
||||
{
|
||||
var attr = _converter.GetType().GetCustomAttribute<PropertyValueTypeAttribute>(false);
|
||||
if (attr != null)
|
||||
_clrType = attr.Type;
|
||||
if (converterMeta != null)
|
||||
{
|
||||
_clrType = converterMeta.GetPropertyValueType(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var attr = _converter.GetType().GetCustomAttribute<PropertyValueTypeAttribute>(false);
|
||||
if (attr != null)
|
||||
_clrType = attr.Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
internal class DataTypePreValueDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 5)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 6)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("datatypeNodeId")]
|
||||
|
||||
@@ -78,5 +78,23 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool Equals(PropertyDataDto other)
|
||||
{
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((PropertyDataDto) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(100)]
|
||||
public string Group { get; set; }//NOTE Is set to [varchar] (100) in Sql Server script
|
||||
|
||||
[ResultColumn("NodeCount")]
|
||||
public int NodeCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
public int EntityId { get; private set; }
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<Tag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
public int PropertyTypeId { get; private set; }
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
public IEnumerable<Tag> Tags { get; private set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Tag : Entity, ITag
|
||||
@@ -43,9 +16,15 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public Tag(int id, string text, string @group)
|
||||
{
|
||||
Id = id;
|
||||
Text = text;
|
||||
Group = @group;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public Tag(int id, string text, string @group, int nodeCount)
|
||||
: this(id, text, @group)
|
||||
{
|
||||
NodeCount = nodeCount;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Text);
|
||||
@@ -79,6 +58,8 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public int NodeCount { get; internal set; }
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//public int ParentId { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public enum TagCacheStorageType
|
||||
{
|
||||
Csv,
|
||||
Json
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a tagged entity.
|
||||
/// </summary>
|
||||
/// <remarks>Note that it is the properties of an entity (like Content, Media, Members, etc.) that is tagged,
|
||||
/// which is why this class is composed of a list of tagged properties and an Id reference to the actual entity.</remarks>
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the entity, which is tagged
|
||||
/// </summary>
|
||||
public int EntityId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An enumerable list of tagged properties
|
||||
/// </summary>
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a tagged property on an entity.
|
||||
/// </summary>
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<ITag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the PropertyType, which this tagged property is based on
|
||||
/// </summary>
|
||||
public int PropertyTypeId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Alias of the PropertyType, which this tagged property is based on
|
||||
/// </summary>
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An enumerable list of Tags for the property
|
||||
/// </summary>
|
||||
public IEnumerable<ITag> Tags { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -87,13 +87,13 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(FriendlyNameAttribute),
|
||||
false);
|
||||
|
||||
if (attributes.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
|
||||
var attribute = ((FriendlyNameAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return string.Empty;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an equivalent to the c# lock statement, to be used in a using block.
|
||||
/// </summary>
|
||||
/// <remarks>Ie replace <c>lock (o) {...}</c> by <c>using (new MonitorLock(o)) { ... }</c></remarks>
|
||||
public class MonitorLock : IDisposable
|
||||
{
|
||||
private readonly object _locker;
|
||||
private readonly bool _entered;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MonitorLock"/> class with an object to lock.
|
||||
/// </summary>
|
||||
/// <param name="locker">The object to lock.</param>
|
||||
/// <remarks>Should always be used within a using block.</remarks>
|
||||
public MonitorLock(object locker)
|
||||
{
|
||||
_locker = locker;
|
||||
_entered = false;
|
||||
System.Threading.Monitor.Enter(_locker, ref _entered);
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
if (_entered)
|
||||
System.Threading.Monitor.Exit(_locker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list
|
||||
/// <remarks>If <paramref name="scope"/> is per HttpRequest then there must be a current HttpContext.</remarks>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
|
||||
/// </summary>
|
||||
protected LazyManyObjectsResolverBase(IEnumerable<Lazy<Type>> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(scope)
|
||||
{
|
||||
@@ -108,7 +109,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Gets a value indicating whether the resolver has resolved types to create instances from.
|
||||
/// </summary>
|
||||
/// <remarks>To be used in unit tests.</remarks>
|
||||
internal bool HasResolvedTypes
|
||||
public bool HasResolvedTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -63,6 +63,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
|
||||
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
|
||||
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
|
||||
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
|
||||
/// </summary>
|
||||
|
||||
@@ -135,6 +135,28 @@ namespace Umbraco.Core
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows us to change the key of an item
|
||||
/// </summary>
|
||||
/// <param name="currentKey"></param>
|
||||
/// <param name="newKey"></param>
|
||||
public virtual void ChangeKey(TKey currentKey, TKey newKey)
|
||||
{
|
||||
if (!Indecies.ContainsKey(currentKey))
|
||||
{
|
||||
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
|
||||
}
|
||||
if (ContainsKey(newKey))
|
||||
{
|
||||
throw new DuplicateKeyException(newKey.ToString());
|
||||
}
|
||||
|
||||
var currentIndex = Indecies[currentKey];
|
||||
|
||||
Indecies.Remove(currentKey);
|
||||
Indecies.Add(newKey, currentIndex);
|
||||
}
|
||||
|
||||
internal class DuplicateKeyException : Exception
|
||||
{
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class ConflictingPackageData : IConflictingPackageData
|
||||
{
|
||||
private readonly IMacroService _macroService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public ConflictingPackageData(IMacroService macroService,
|
||||
IFileService fileService)
|
||||
{
|
||||
if (fileService != null) _fileService = fileService;
|
||||
else throw new ArgumentNullException("fileService");
|
||||
if (macroService != null) _macroService = macroService;
|
||||
else throw new ArgumentNullException("macroService");
|
||||
}
|
||||
|
||||
public IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("the root element must be \"" + Constants.Packaging.StylesheetsNodeName + "\"", "stylesheetNotes");
|
||||
}
|
||||
|
||||
return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.NameNodeName);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element",
|
||||
"stylesheetNotes");
|
||||
}
|
||||
|
||||
return _fileService.GetStylesheetByName(xElement.Value) as IFile;
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node",
|
||||
"templateNotes");
|
||||
}
|
||||
|
||||
return templateNotes.Elements(Constants.Packaging.TemplateNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeNameCapital + "\" element",
|
||||
"templateNotes");
|
||||
}
|
||||
|
||||
return _fileService.GetTemplate(xElement.Value);
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
|
||||
public IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.MacrosNodeName, macroNodes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Node must be a \"" + Constants.Packaging.MacrosNodeName + "\" node",
|
||||
"macroNodes");
|
||||
}
|
||||
|
||||
return macroNodes.Elements(Constants.Packaging.MacroNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException(string.Format("missing a \"{0}\" element in {0} element", Constants.Packaging.AliasNodeNameSmall),
|
||||
"macroNodes");
|
||||
}
|
||||
|
||||
return _macroService.GetByAlias(xElement.Value);
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IConflictingPackageData
|
||||
{
|
||||
IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes);
|
||||
IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes);
|
||||
IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to access an umbraco package file
|
||||
/// Remeber that filenames must be unique
|
||||
/// use "FindDubletFileNames" for sanitycheck
|
||||
/// </summary>
|
||||
internal interface IPackageExtraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the content of the file with the given filename
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <param name="fileToRead">filename of the file for wich to get the text content</param>
|
||||
/// <param name="directoryInPackage">this is the relative directory for the location of the file in the package
|
||||
/// I dont know why umbraco packages contains directories in the first place??</param>
|
||||
/// <returns>text content of the file</returns>
|
||||
string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a file from package to given destination
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the ubraco package file</param>
|
||||
/// <param name="fileInPackageName">filename of the file to copy</param>
|
||||
/// <param name="destinationfilePath">destination path (including destination filename)</param>
|
||||
/// <returns>True a file was overwritten</returns>
|
||||
void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a file from package to given destination
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the ubraco package file</param>
|
||||
/// <param name="sourceDestination">Key: Source file in package. Value: Destination path inclusive file name</param>
|
||||
void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination);
|
||||
|
||||
/// <summary>
|
||||
/// Check if given list of files can be found in the package
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <param name="expectedFiles">a list of files you would like to find in the package</param>
|
||||
/// <returns>a subset if any of the files in "expectedFiles" that could not be found in the package</returns>
|
||||
IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sanitycheck - should return en empty collection if package is valid
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <returns>list of files that are found more than ones (accross directories) in the package</returns>
|
||||
IEnumerable<string> FindDubletFileNames(string packageFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the given files from archive and returns them as a collection of byte arrays
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath"></param>
|
||||
/// <param name="filesToGet"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageInstallation
|
||||
{
|
||||
InstallationSummary InstallPackage(string packageFilePath, int userId);
|
||||
MetaData GetMetaData(string packageFilePath);
|
||||
PreInstallWarnings GetPreInstallWarnings(string packageFilePath);
|
||||
XElement GetConfigXmlElement(string packageFilePath);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallAction
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public string RunAt { get; set; }//NOTE Should this default to "install"
|
||||
|
||||
public bool Undo { get; set; }
|
||||
|
||||
public XmlNode XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<int> DataTypesInstalled { get; set; }
|
||||
public IEnumerable<int> LanguagesInstalled { get; set; }
|
||||
public IEnumerable<int> DictionaryItemsInstalled { get; set; }
|
||||
public IEnumerable<int> MacrosInstalled { get; set; }
|
||||
public IEnumerable<KeyValuePair<string, bool>> FilesInstalled { get; set;}
|
||||
public IEnumerable<int> TemplatesInstalled { get; set; }
|
||||
public IEnumerable<int> DocumentTypesInstalled { get; set; }
|
||||
public IEnumerable<int> StylesheetsInstalled { get; set; }
|
||||
public IEnumerable<int> DocumentsInstalled { get; set; }
|
||||
public IEnumerable<InstallAction> InstallActions { get; set; }
|
||||
public IEnumerable<UninstallAction> UninstallActions { get; set; }
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<IDataTypeDefinition> DataTypesInstalled { get; set; }
|
||||
public IEnumerable<ILanguage> LanguagesInstalled { get; set; }
|
||||
public IEnumerable<IDictionaryItem> DictionaryItemsInstalled { get; set; }
|
||||
public IEnumerable<IMacro> MacrosInstalled { get; set; }
|
||||
public IEnumerable<string> FilesInstalled { get; set; }
|
||||
public IEnumerable<ITemplate> TemplatesInstalled { get; set; }
|
||||
public IEnumerable<IContentType> ContentTypesInstalled { get; set; }
|
||||
public IEnumerable<IFile> StylesheetsInstalled { get; set; }
|
||||
public IEnumerable<IContent> ContentInstalled { get; set; }
|
||||
public IEnumerable<PackageAction> Actions { get; set; }
|
||||
public bool PackageInstalled { get; set; }
|
||||
}
|
||||
|
||||
internal static class InstallationSummaryExtentions
|
||||
{
|
||||
public static InstallationSummary InitEmpty(this InstallationSummary summary)
|
||||
{
|
||||
summary.Actions = new List<PackageAction>();
|
||||
summary.ContentInstalled = new List<IContent>();
|
||||
summary.ContentTypesInstalled = new List<IContentType>();
|
||||
summary.DataTypesInstalled = new List<IDataTypeDefinition>();
|
||||
summary.DictionaryItemsInstalled = new List<IDictionaryItem>();
|
||||
summary.FilesInstalled = new List<string>();
|
||||
summary.LanguagesInstalled = new List<ILanguage>();
|
||||
summary.MacrosInstalled = new List<IMacro>();
|
||||
summary.MetaData = new MetaData();
|
||||
summary.TemplatesInstalled = new List<ITemplate>();
|
||||
summary.PackageInstalled = false;
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
internal enum ActionRunAt
|
||||
{
|
||||
Undefined = 0,
|
||||
Install,
|
||||
Uninstall
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PackageAction
|
||||
{
|
||||
private ActionRunAt _runAt;
|
||||
private bool? _undo;
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public ActionRunAt RunAt
|
||||
{
|
||||
get { return _runAt == ActionRunAt.Undefined ? ActionRunAt.Install : _runAt; }
|
||||
set { _runAt = value; }
|
||||
}
|
||||
|
||||
public bool Undo //NOTE: Should thid default to "False"? but the documentation says default "True" (http://our.umbraco.org/wiki/reference/packaging/package-actions)
|
||||
{
|
||||
get { return _undo ?? true; }
|
||||
set { _undo = value; }
|
||||
}
|
||||
|
||||
public XElement XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PreInstallWarnings
|
||||
{
|
||||
public KeyValuePair<string, string>[] UnsecureFiles { get; set; }
|
||||
public KeyValuePair<string, string>[] FilesReplaced { get; set; }
|
||||
public IEnumerable<IMacro> ConflictingMacroAliases { get; set; }
|
||||
public IEnumerable<ITemplate> ConflictingTemplateAliases { get; set; }
|
||||
public IEnumerable<IFile> ConflictingStylesheetNames { get; set; }
|
||||
public IEnumerable<string> AssembliesWithLegacyPropertyEditors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UninstallAction
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public string RunAt { get; set; }//NOTE Should this default to "install"
|
||||
|
||||
public bool Undo { get; set; }//NOTE: Should thid default to "False"?
|
||||
|
||||
public XmlNode XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,47 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
// Note
|
||||
// That class uses ReflectionOnlyLoad which does NOT handle policies (bindingRedirect) and
|
||||
// therefore raised warnings when installing a package, if an exact dependency could not be
|
||||
// found, though it would be found via policies. So we have to explicitely apply policies
|
||||
// where appropriate.
|
||||
|
||||
internal class PackageBinaryInspector : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry point to call from your code
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="assemblys"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Will perform the assembly scan in a separate app domain
|
||||
/// </remarks>
|
||||
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(IEnumerable<byte[]> assemblys, out string[] errorReport)
|
||||
{
|
||||
var appDomain = GetTempAppDomain();
|
||||
var type = typeof(PackageBinaryInspector);
|
||||
try
|
||||
{
|
||||
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
// do NOT turn PerformScan into static (even if ReSharper says so)!
|
||||
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
AppDomain.Unload(appDomain);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry point to call from your code
|
||||
/// </summary>
|
||||
@@ -33,6 +65,7 @@ namespace Umbraco.Core.Packaging
|
||||
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
// do NOT turn PerformScan into static (even if ReSharper says so)!
|
||||
var result = value.PerformScan<T>(dllPath, out errorReport);
|
||||
return result;
|
||||
}
|
||||
@@ -42,6 +75,35 @@ namespace Umbraco.Core.Packaging
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the assembly scanning
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This method is executed in a separate app domain
|
||||
/// </remarks>
|
||||
private IEnumerable<string> PerformScan<T>(IEnumerable<byte[]> assemblies, out string[] errorReport)
|
||||
{
|
||||
//we need this handler to resolve assembly dependencies when loading below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
|
||||
var a = Assembly.ReflectionOnlyLoad(name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
// do NOT apply policy here: we want to scan the dlls that are in the binaries
|
||||
var loaded = assemblies.Select(Assembly.ReflectionOnlyLoad).ToList();
|
||||
|
||||
//scan
|
||||
return PerformScan<T>(loaded, out errorReport);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the assembly scanning
|
||||
/// </summary>
|
||||
@@ -52,33 +114,42 @@ namespace Umbraco.Core.Packaging
|
||||
/// <remarks>
|
||||
/// This method is executed in a separate app domain
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
private IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
{
|
||||
if (Directory.Exists(dllPath) == false)
|
||||
{
|
||||
throw new DirectoryNotFoundException("Could not find directory " + dllPath);
|
||||
}
|
||||
|
||||
//we need this handler to resolve assembly dependencies when loading below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
|
||||
var a = Assembly.ReflectionOnlyLoad(name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
// do NOT apply policy here: we want to scan the dlls that are in the path
|
||||
var files = Directory.GetFiles(dllPath, "*.dll");
|
||||
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
|
||||
|
||||
//scan
|
||||
return PerformScan<T>(loaded, out errorReport);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> PerformScan<T>(IList<Assembly> loaded, out string[] errorReport)
|
||||
{
|
||||
var dllsWithReference = new List<string>();
|
||||
var errors = new List<string>();
|
||||
var assembliesWithErrors = new List<Assembly>();
|
||||
|
||||
//we need this handler to resolve assembly dependencies below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var a = Assembly.ReflectionOnlyLoad(e.Name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
|
||||
|
||||
//load each of the LoadFrom assemblies into the Load context too
|
||||
foreach (var a in loaded)
|
||||
{
|
||||
Assembly.ReflectionOnlyLoad(a.FullName);
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(a.FullName);
|
||||
Assembly.ReflectionOnlyLoad(name);
|
||||
}
|
||||
|
||||
//get the list of assembly names to compare below
|
||||
@@ -94,7 +165,8 @@ namespace Umbraco.Core.Packaging
|
||||
{
|
||||
try
|
||||
{
|
||||
Assembly.ReflectionOnlyLoad(assemblyName.FullName);
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(assemblyName.FullName);
|
||||
Assembly.ReflectionOnlyLoad(name);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@@ -167,7 +239,6 @@ namespace Umbraco.Core.Packaging
|
||||
return dllsWithReference;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// In order to compare types, the types must be in the same context, this method will return the type that
|
||||
/// we are checking against but from the Load context.
|
||||
@@ -176,7 +247,8 @@ namespace Umbraco.Core.Packaging
|
||||
/// <returns></returns>
|
||||
private static Type GetLoadFromContractType<T>()
|
||||
{
|
||||
var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName);
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(typeof(T).Assembly.FullName);
|
||||
var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoad(name);
|
||||
|
||||
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
|
||||
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
|
||||
@@ -216,13 +288,5 @@ namespace Umbraco.Core.Packaging
|
||||
domainSetup,
|
||||
new PermissionSet(PermissionState.Unrestricted));
|
||||
}
|
||||
|
||||
private static string GetAssemblyPath(Assembly a)
|
||||
{
|
||||
var codeBase = a.CodeBase;
|
||||
var uri = new Uri(codeBase);
|
||||
return uri.LocalPath;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Packaging
|
||||
|
||||
internal class PackageBuilding : IPackageBuilding
|
||||
{
|
||||
private readonly PackagingService _packagingService;
|
||||
private readonly IPackagingService _packagingService;
|
||||
|
||||
public PackageBuilding(PackagingService packagingService)
|
||||
public PackageBuilding(IPackagingService packagingService)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,201 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageExtraction
|
||||
{
|
||||
bool Extract(string packageFilePath, string destinationFolder);
|
||||
string ExtractToTemporaryFolder(string packageFilePath);
|
||||
string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml");
|
||||
}
|
||||
|
||||
internal class PackageExtraction : IPackageExtraction
|
||||
{
|
||||
public bool Extract(string packageFilePath, string destinationFolder)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public string ExtractToTemporaryFolder(string packageFilePath)
|
||||
{
|
||||
string tempDir = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), Guid.NewGuid().ToString("D"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
Extract(packageFilePath, tempDir);
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
public string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml")
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class PackageExtraction : IPackageExtraction
|
||||
{
|
||||
public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage)
|
||||
{
|
||||
string retVal = null;
|
||||
bool fileFound = false;
|
||||
string foundDir = null;
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = Path.GetFileName(entry.Name);
|
||||
|
||||
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))
|
||||
{
|
||||
retVal = reader.ReadToEnd();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (fileFound == false)
|
||||
{
|
||||
directoryInPackage = null;
|
||||
throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead);
|
||||
}
|
||||
directoryInPackage = foundDir;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static void CheckPackageExists(string packageFilePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(packageFilePath))
|
||||
{
|
||||
throw new ArgumentNullException("packageFilePath");
|
||||
}
|
||||
|
||||
if (File.Exists(packageFilePath) == false)
|
||||
{
|
||||
if (File.Exists(packageFilePath) == false)
|
||||
throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath));
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(packageFilePath).ToLower();
|
||||
|
||||
var alowedExtension = new[] { ".umb", ".zip" };
|
||||
|
||||
// Check if the file is a valid package
|
||||
if (alowedExtension.All(ae => ae.Equals(extension) == false))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension)));
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath)
|
||||
{
|
||||
CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair<string, string>(fileInPackageName, destinationfilePath) } );
|
||||
}
|
||||
|
||||
public void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
if (fileName == string.Empty) { return true; }
|
||||
|
||||
string destination;
|
||||
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
|
||||
{
|
||||
using (var streamWriter = File.Open(destination, FileMode.Create))
|
||||
{
|
||||
stream.CopyTo(streamWriter);
|
||||
}
|
||||
|
||||
d.Remove(fileName);
|
||||
return d.Any();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (d.Any())
|
||||
{
|
||||
throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles)
|
||||
{
|
||||
var retVal = expectedFiles.ToList();
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (zipEntry, stream) =>
|
||||
{
|
||||
string fileName = Path.GetFileName(zipEntry.Name);
|
||||
|
||||
int index = retVal.FindIndex(f => f.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (index != -1) { retVal.RemoveAt(index); }
|
||||
|
||||
return retVal.Any();
|
||||
});
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<string> FindDubletFileNames(string packageFilePath)
|
||||
{
|
||||
var dictionary = new Dictionary<string, List<string>>();
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
|
||||
List<string> list;
|
||||
if (dictionary.TryGetValue(fileName, out list) == false)
|
||||
{
|
||||
list = new List<string>();
|
||||
dictionary.Add(fileName, list);
|
||||
}
|
||||
|
||||
list.Add(entry.Name);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v);
|
||||
}
|
||||
|
||||
public IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet)
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
var files = new HashSet<string>(filesToGet.Select(f => f.ToLower()));
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
if (zipEntry.IsDirectory) continue;
|
||||
|
||||
if (files.Contains(zipEntry.Name))
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
zipInputStream.CopyTo(memStream);
|
||||
yield return memStream.ToArray();
|
||||
memStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadZipfileEntries(string packageFilePath, Func<ZipEntry, ZipInputStream, bool> entryFunc, bool skipsDirectories = true)
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (zipEntry.IsDirectory && skipsDirectories) continue;
|
||||
if (entryFunc(zipEntry, zipInputStream) == false) break;
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,605 @@
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageInstallation
|
||||
{
|
||||
InstallationSummary InstallPackage(string packageFilePath, int userId = 0);
|
||||
MetaData GetPackageMetaData(string packageFilePath);
|
||||
}
|
||||
|
||||
internal class PackageInstallation : IPackageInstallation
|
||||
{
|
||||
private readonly PackagingService _packagingService;
|
||||
private readonly PackageExtraction _packageExtraction;
|
||||
|
||||
public PackageInstallation(PackagingService packagingService, PackageExtraction packageExtraction)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_packageExtraction = packageExtraction;
|
||||
}
|
||||
|
||||
public InstallationSummary InstallPackage(string packageFilePath, int userId = 0)
|
||||
{
|
||||
var summary = new InstallationSummary();
|
||||
return summary;
|
||||
}
|
||||
|
||||
public MetaData GetPackageMetaData(string packageFilePath)
|
||||
{
|
||||
var metaData = new MetaData();
|
||||
return metaData;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class PackageInstallation : IPackageInstallation
|
||||
{
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IMacroService _macroService;
|
||||
private readonly IPackagingService _packagingService;
|
||||
private IConflictingPackageData _conflictingPackageData;
|
||||
private readonly IPackageExtraction _packageExtraction;
|
||||
private string _fullPathToRoot;
|
||||
|
||||
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
|
||||
IFileService fileService, IPackageExtraction packageExtraction)
|
||||
: this(packagingService, macroService, fileService, packageExtraction, GlobalSettings.FullpathToRoot)
|
||||
{}
|
||||
|
||||
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
|
||||
IFileService fileService, IPackageExtraction packageExtraction, string fullPathToRoot)
|
||||
{
|
||||
if (packageExtraction != null) _packageExtraction = packageExtraction;
|
||||
else throw new ArgumentNullException("packageExtraction");
|
||||
|
||||
if (macroService != null) _macroService = macroService;
|
||||
else throw new ArgumentNullException("macroService");
|
||||
|
||||
if (fileService != null) _fileService = fileService;
|
||||
else throw new ArgumentNullException("fileService");
|
||||
|
||||
if (packagingService != null) _packagingService = packagingService;
|
||||
else throw new ArgumentNullException("packagingService");
|
||||
|
||||
_fullPathToRoot = fullPathToRoot;
|
||||
}
|
||||
|
||||
public IConflictingPackageData ConflictingPackageData
|
||||
{
|
||||
private get
|
||||
{
|
||||
return _conflictingPackageData ??
|
||||
(_conflictingPackageData = new ConflictingPackageData(_macroService, _fileService));
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_conflictingPackageData != null)
|
||||
{
|
||||
throw new PropertyConstraintException("This property already have a value");
|
||||
}
|
||||
_conflictingPackageData = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string FullPathToRoot
|
||||
{
|
||||
private get { return _fullPathToRoot; }
|
||||
set
|
||||
{
|
||||
|
||||
if (_fullPathToRoot != null)
|
||||
{
|
||||
throw new PropertyConstraintException("This property already have a value");
|
||||
}
|
||||
|
||||
_fullPathToRoot = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MetaData GetMetaData(string packageFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
return GetMetaData(rootElement);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public PreInstallWarnings GetPreInstallWarnings(string packageFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
return GetPreInstallWarnings(packageFilePath, rootElement);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public InstallationSummary InstallPackage(string packageFile, int userId)
|
||||
{
|
||||
XElement dataTypes;
|
||||
XElement languages;
|
||||
XElement dictionaryItems;
|
||||
XElement macroes;
|
||||
XElement files;
|
||||
XElement templates;
|
||||
XElement documentTypes;
|
||||
XElement styleSheets;
|
||||
XElement documentSet;
|
||||
XElement documents;
|
||||
XElement actions;
|
||||
MetaData metaData;
|
||||
InstallationSummary installationSummary;
|
||||
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFile);
|
||||
PackageSupportedCheck(rootElement);
|
||||
PackageStructureSanetyCheck(packageFile, rootElement);
|
||||
dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName);
|
||||
languages = rootElement.Element(Constants.Packaging.LanguagesNodeName);
|
||||
dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName);
|
||||
macroes = rootElement.Element(Constants.Packaging.MacrosNodeName);
|
||||
files = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
|
||||
documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName);
|
||||
styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName);
|
||||
documents = rootElement.Element(Constants.Packaging.DocumentsNodeName);
|
||||
actions = rootElement.Element(Constants.Packaging.ActionsNodeName);
|
||||
|
||||
metaData = GetMetaData(rootElement);
|
||||
installationSummary = new InstallationSummary {MetaData = metaData};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFile, e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var dataTypeDefinitions = EmptyEnumerableIfNull<IDataTypeDefinition>(dataTypes) ?? InstallDataTypes(dataTypes, userId);
|
||||
installationSummary.DataTypesInstalled = dataTypeDefinitions;
|
||||
|
||||
var languagesInstalled = EmptyEnumerableIfNull<ILanguage>(languages) ?? InstallLanguages(languages, userId);
|
||||
installationSummary.LanguagesInstalled = languagesInstalled;
|
||||
|
||||
var dictionaryInstalled = EmptyEnumerableIfNull<IDictionaryItem>(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems);
|
||||
installationSummary.DictionaryItemsInstalled = dictionaryInstalled;
|
||||
|
||||
var macros = EmptyEnumerableIfNull<IMacro>(macroes) ?? InstallMacros(macroes, userId);
|
||||
installationSummary.MacrosInstalled = macros;
|
||||
|
||||
var keyValuePairs = EmptyEnumerableIfNull<string>(packageFile) ?? InstallFiles(packageFile, files);
|
||||
installationSummary.FilesInstalled = keyValuePairs;
|
||||
|
||||
var templatesInstalled = EmptyEnumerableIfNull<ITemplate>(templates) ?? InstallTemplats(templates, userId);
|
||||
installationSummary.TemplatesInstalled = templatesInstalled;
|
||||
|
||||
var documentTypesInstalled = EmptyEnumerableIfNull<IContentType>(documentTypes) ?? InstallDocumentTypes(documentTypes, userId);
|
||||
installationSummary.ContentTypesInstalled =documentTypesInstalled;
|
||||
|
||||
var stylesheetsInstalled = EmptyEnumerableIfNull<IFile>(styleSheets) ?? InstallStylesheets(styleSheets, userId);
|
||||
installationSummary.StylesheetsInstalled = stylesheetsInstalled;
|
||||
|
||||
var documentsInstalled = documents != null ? InstallDocuments(documents, userId)
|
||||
: EmptyEnumerableIfNull<IContent>(documentSet)
|
||||
?? InstallDocuments(documentSet, userId);
|
||||
installationSummary.ContentInstalled = documentsInstalled;
|
||||
|
||||
var packageActions = EmptyEnumerableIfNull<PackageAction>(actions) ?? GetPackageActions(actions, metaData.Name);
|
||||
installationSummary.Actions = packageActions;
|
||||
|
||||
installationSummary.PackageInstalled = true;
|
||||
|
||||
return installationSummary;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error installing package " + packageFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temperary check to test that we support stylesheets
|
||||
/// </summary>
|
||||
/// <param name="rootElement"></param>
|
||||
private void PackageSupportedCheck(XElement rootElement)
|
||||
{
|
||||
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
if (styleSheets != null && styleSheets.Elements().Any())
|
||||
throw new NotSupportedException("Stylesheets is not suported in this version of umbraco");
|
||||
}
|
||||
|
||||
private static T[] EmptyArrayIfNull<T>(object obj)
|
||||
{
|
||||
return obj == null ? new T[0] : null;
|
||||
}
|
||||
|
||||
private static IEnumerable<T> EmptyEnumerableIfNull<T>(object obj)
|
||||
{
|
||||
return obj == null ? Enumerable.Empty<T>() : null;
|
||||
}
|
||||
|
||||
private XDocument GetConfigXmlDoc(string packageFilePath)
|
||||
{
|
||||
string filePathInPackage;
|
||||
string configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFilePath,
|
||||
Constants.Packaging.PackageXmlFileName, out filePathInPackage);
|
||||
|
||||
return XDocument.Parse(configXmlContent);
|
||||
}
|
||||
|
||||
public XElement GetConfigXmlElement(string packageFilePath)
|
||||
{
|
||||
XDocument document = GetConfigXmlDoc(packageFilePath);
|
||||
if (document.Root == null ||
|
||||
document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false)
|
||||
{
|
||||
throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath);
|
||||
}
|
||||
return document.Root;
|
||||
}
|
||||
|
||||
internal void PackageStructureSanetyCheck(string packageFilePath)
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
PackageStructureSanetyCheck(packageFilePath, rootElement);
|
||||
}
|
||||
|
||||
private void PackageStructureSanetyCheck(string packageFilePath, XElement rootElement)
|
||||
{
|
||||
XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
if (filesElement != null)
|
||||
{
|
||||
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement).ToArray();
|
||||
|
||||
var missingFiles = _packageExtraction.FindMissingFiles(packageFilePath, sourceDestination.Select(i => i.Key)).ToArray();
|
||||
|
||||
if (missingFiles.Any())
|
||||
{
|
||||
throw new Exception("The following file(s) are missing in the package: " +
|
||||
string.Join(", ", missingFiles.Select(
|
||||
mf =>
|
||||
{
|
||||
var sd = sourceDestination.Single(fi => fi.Key == mf);
|
||||
return string.Format("source: \"{0}\" destination: \"{1}\"",
|
||||
sd.Key, sd.Value);
|
||||
})));
|
||||
}
|
||||
|
||||
IEnumerable<string> dubletFileNames = _packageExtraction.FindDubletFileNames(packageFilePath).ToArray();
|
||||
|
||||
if (dubletFileNames.Any())
|
||||
{
|
||||
throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " +
|
||||
string.Join(", ", dubletFileNames));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<PackageAction> GetPackageActions(XElement actionsElement, string packageName)
|
||||
{
|
||||
if (actionsElement == null) { return new PackageAction[0]; }
|
||||
|
||||
if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root",
|
||||
"actionsElement");
|
||||
}
|
||||
|
||||
return actionsElement.Elements(Constants.Packaging.ActionNodeName)
|
||||
.Select(elemet =>
|
||||
{
|
||||
XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital);
|
||||
if (aliasAttr == null)
|
||||
throw new ArgumentException(
|
||||
"missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element",
|
||||
"actionsElement");
|
||||
|
||||
var packageAction = new PackageAction
|
||||
{
|
||||
XmlData = elemet,
|
||||
Alias = aliasAttr.Value,
|
||||
PackageName = packageName,
|
||||
};
|
||||
|
||||
|
||||
XAttribute attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute);
|
||||
|
||||
ActionRunAt runAt;
|
||||
if (attr != null && Enum.TryParse(attr.Value, true, out runAt)) { packageAction.RunAt = runAt; }
|
||||
|
||||
attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute);
|
||||
|
||||
bool undo;
|
||||
if (attr != null && bool.TryParse(attr.Value, out undo)) { packageAction.Undo = undo; }
|
||||
|
||||
|
||||
return packageAction;
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> InstallDocuments(XElement documentsElement, int userId = 0)
|
||||
{
|
||||
if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false)
|
||||
&& (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false))
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root",
|
||||
"documentsElement");
|
||||
}
|
||||
|
||||
if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName))
|
||||
return _packagingService.ImportContent(documentsElement, -1, userId);
|
||||
|
||||
return
|
||||
documentsElement.Elements(Constants.Packaging.DocumentSetNodeName)
|
||||
.SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IFile> InstallStylesheets(XElement styleSheetsElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root",
|
||||
"styleSheetsElement");
|
||||
}
|
||||
|
||||
// TODO: Call _packagingService when import stylesheets import has been implimentet
|
||||
if (styleSheetsElement.HasElements == false) { return new List<IFile>(); }
|
||||
|
||||
throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets");
|
||||
}
|
||||
|
||||
private IEnumerable<IContentType> InstallDocumentTypes(XElement documentTypes, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false)
|
||||
throw new ArgumentException(
|
||||
"Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes");
|
||||
|
||||
documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes);
|
||||
}
|
||||
|
||||
return _packagingService.ImportContentTypes(documentTypes, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<ITemplate> InstallTemplats(XElement templateElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root",
|
||||
"templateElement");
|
||||
}
|
||||
return _packagingService.ImportTemplates(templateElement, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<string> InstallFiles(string packageFilePath, XElement filesElement)
|
||||
{
|
||||
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement);
|
||||
sourceDestination = AppendRootToDestination(FullPathToRoot, sourceDestination);
|
||||
|
||||
_packageExtraction.CopyFilesFromArchive(packageFilePath, sourceDestination);
|
||||
|
||||
return sourceDestination.Select(sd => sd.Value).ToArray();
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] AppendRootToDestination(string fullpathToRoot, IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
return
|
||||
sourceDestination.Select(
|
||||
sd => new KeyValuePair<string, string>(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IMacro> InstallMacros(XElement macroElements, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root",
|
||||
"macroElements");
|
||||
}
|
||||
return _packagingService.ImportMacros(macroElements, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<IDictionaryItem> InstallDictionaryItems(XElement dictionaryItemsElement)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) ==
|
||||
false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root",
|
||||
"dictionaryItemsElement");
|
||||
}
|
||||
return _packagingService.ImportDictionaryItems(dictionaryItemsElement);
|
||||
}
|
||||
|
||||
private IEnumerable<ILanguage> InstallLanguages(XElement languageElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement");
|
||||
}
|
||||
return _packagingService.ImportLanguages(languageElement, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<IDataTypeDefinition> InstallDataTypes(XElement dataTypeElements, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements");
|
||||
}
|
||||
}
|
||||
return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId);
|
||||
}
|
||||
|
||||
private PreInstallWarnings GetPreInstallWarnings(string packagePath, XElement rootElement)
|
||||
{
|
||||
XElement files = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
|
||||
XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName);
|
||||
|
||||
var sourceDestination = EmptyArrayIfNull<KeyValuePair<string, string>>(files) ?? ExtractSourceDestinationFileInformation(files);
|
||||
|
||||
var installWarnings = new PreInstallWarnings();
|
||||
|
||||
var macroAliases = EmptyEnumerableIfNull<IMacro>(alias) ?? ConflictingPackageData.FindConflictingMacros(alias);
|
||||
installWarnings.ConflictingMacroAliases = macroAliases;
|
||||
|
||||
var templateAliases = EmptyEnumerableIfNull<ITemplate>(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates);
|
||||
installWarnings.ConflictingTemplateAliases = templateAliases;
|
||||
|
||||
var stylesheetNames = EmptyEnumerableIfNull<IFile>(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets);
|
||||
installWarnings.ConflictingStylesheetNames = stylesheetNames;
|
||||
|
||||
installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination);
|
||||
installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination);
|
||||
installWarnings.AssembliesWithLegacyPropertyEditors = FindLegacyPropertyEditors(packagePath, sourceDestination);
|
||||
|
||||
return installWarnings;
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] FindFilesToBeReplaced(IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<string> FindLegacyPropertyEditors(string packagePath, IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
|
||||
{
|
||||
var dlls = sourceDestinationPair.Where(
|
||||
sd => (Path.GetExtension(sd.Value) ?? string.Empty).Equals(".dll", StringComparison.InvariantCultureIgnoreCase)).Select(sd => sd.Key).ToArray();
|
||||
|
||||
if (dlls.Any() == false) { return new List<string>(); }
|
||||
|
||||
// Now we want to see if the DLLs contain any legacy data types since we want to warn people about that
|
||||
string[] assemblyErrors;
|
||||
IEnumerable<byte[]> assemblyesToScan =_packageExtraction.ReadFilesFromArchive(packagePath, dlls);
|
||||
return PackageBinaryInspector.ScanAssembliesForTypeReference<IDataType>(assemblyesToScan, out assemblyErrors).ToArray();
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] FindUnsecureFiles(IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
|
||||
{
|
||||
return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.Value)).ToArray();
|
||||
}
|
||||
|
||||
private bool IsFileDestinationUnsecure(string destination)
|
||||
{
|
||||
var unsecureDirNames = new[] {"bin", "app_code"};
|
||||
if(unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase)))
|
||||
return true;
|
||||
|
||||
string extension = Path.GetExtension(destination);
|
||||
return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] ExtractSourceDestinationFileInformation(XElement filesElement)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("the root element must be \"Files\"", "filesElement");
|
||||
}
|
||||
|
||||
return filesElement.Elements(Constants.Packaging.FileNodeName)
|
||||
.Select(e =>
|
||||
{
|
||||
XElement guidElement = e.Element(Constants.Packaging.GuidNodeName);
|
||||
if (guidElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName);
|
||||
if (orgPathElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName);
|
||||
if (orgNameElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
var fileName = PrepareAsFilePathElement(orgNameElement.Value);
|
||||
var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(orgPathElement.Value));
|
||||
|
||||
var relativePath = Path.Combine(relativeDir, fileName);
|
||||
|
||||
|
||||
return new KeyValuePair<string, string>(guidElement.Value, relativePath);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private static string PrepareAsFilePathElement(string pathElement)
|
||||
{
|
||||
return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\");
|
||||
}
|
||||
|
||||
private MetaData GetMetaData(XElement xRootElement)
|
||||
{
|
||||
XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName);
|
||||
|
||||
if (infoElement == null)
|
||||
{
|
||||
throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element",
|
||||
"xRootElement");
|
||||
}
|
||||
|
||||
var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath);
|
||||
var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath);
|
||||
var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath);
|
||||
var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath);
|
||||
var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath);
|
||||
var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath);
|
||||
var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath);
|
||||
var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath);
|
||||
var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath);
|
||||
var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath);
|
||||
|
||||
XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName);
|
||||
|
||||
return new MetaData
|
||||
{
|
||||
Name = StringValue(nameElement),
|
||||
Version = StringValue(versionElement),
|
||||
Url = StringValue(urlElement),
|
||||
License = StringValue(licenseElement),
|
||||
LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute),
|
||||
AuthorName = StringValue(authorNameElement),
|
||||
AuthorUrl = StringValue(authorUrlElement),
|
||||
Readme = StringValue(readmeElement),
|
||||
Control = StringValue(controlElement),
|
||||
ReqMajor = IntValue(majorElement),
|
||||
ReqMinor = IntValue(minorElement),
|
||||
ReqPatch = IntValue(patchElement)
|
||||
};
|
||||
}
|
||||
|
||||
private static string StringValue(XElement xElement, string defaultValue = "")
|
||||
{
|
||||
return xElement == null ? defaultValue : xElement.Value;
|
||||
}
|
||||
|
||||
private static string StringAttribute(XElement xElement, string attribute, string defaultValue = "")
|
||||
{
|
||||
return xElement == null
|
||||
? defaultValue
|
||||
: xElement.HasAttributes ? xElement.AttributeValue<string>(attribute) : defaultValue;
|
||||
}
|
||||
|
||||
private static int IntValue(XElement xElement, int defaultValue = 0)
|
||||
{
|
||||
int val;
|
||||
return xElement == null ? defaultValue : int.TryParse(xElement.Value, out val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
private static string UpdatePathPlaceholders(string path)
|
||||
{
|
||||
if (path.Contains("[$"))
|
||||
{
|
||||
//this is experimental and undocumented...
|
||||
path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco);
|
||||
path = path.Replace("[$UMBRACOCLIENT]", SystemDirectories.UmbracoClient);
|
||||
path = path.Replace("[$CONFIG]", SystemDirectories.Config);
|
||||
path = path.Replace("[$DATA]", SystemDirectories.Data);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,8 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
public static RuntimeCacheProvider Current { get { return lazy.Value; } }
|
||||
|
||||
private RuntimeCacheProvider()
|
||||
//internal for testing! - though I'm not a huge fan of these being singletons!
|
||||
internal RuntimeCacheProvider()
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
{
|
||||
@@ -128,9 +129,9 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
public void Save(Type type, IEntity entity)
|
||||
{
|
||||
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
|
||||
entity = (IEntity)entity.DeepClone();
|
||||
var clone = (IEntity)entity.DeepClone();
|
||||
|
||||
var key = GetCompositeId(type, entity.Id);
|
||||
var key = GetCompositeId(type, clone.Id);
|
||||
|
||||
_keyTracker.TryAdd(key);
|
||||
|
||||
@@ -139,11 +140,11 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
_memoryCache.Set(key, clone, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
|
||||
HttpRuntime.Cache.Insert(key, clone, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +247,7 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
private string GetCompositeId(Type type, Guid id)
|
||||
{
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToString());
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id);
|
||||
}
|
||||
|
||||
private string GetCompositeId(Type type, int id)
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Persistence
|
||||
connection.Open();
|
||||
connection.Close();
|
||||
}
|
||||
catch (SqlException)
|
||||
catch (DbException)
|
||||
{
|
||||
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