Compare commits
587 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7dad078af4 | |||
| 9a89b6048a | |||
| 8c13b89c79 | |||
| d54e09c40b | |||
| c0d6c511e4 | |||
| 2da3ec27db | |||
| 5f10ed99aa | |||
| 48fa60039b | |||
| 20ceceffe0 | |||
| b73c7fbb16 | |||
| c96b866f1d | |||
| b724a185b7 | |||
| f94d280e27 | |||
| b2399e97cc | |||
| 5580523c08 | |||
| 6670f7036b | |||
| a59dd32dd1 | |||
| ee78e3815b | |||
| 2acfd64706 | |||
| df3d8d3757 | |||
| a6f6a321f9 | |||
| 03b0133b58 | |||
| 25cfceadc0 | |||
| 03969fd687 | |||
| 7788452fb7 | |||
| c7778467e3 | |||
| 0600d5c463 | |||
| 15c36a63d3 | |||
| bcba12abc8 | |||
| c0dd32d1f9 | |||
| 2411271ae0 | |||
| c87d0a6430 | |||
| c0051ba8cd | |||
| 2cf590d9c9 | |||
| 2729da2c08 | |||
| 9046319dc9 | |||
| fa27a37207 | |||
| 80f81ce379 | |||
| 3022293565 | |||
| 34889c147a | |||
| 7d17aef953 | |||
| 8af8a6d411 | |||
| dd736dce3a | |||
| cd4c738c04 | |||
| ed383bdaeb | |||
| 4874a71e72 | |||
| 58b8aacff1 | |||
| d4d5a355e3 | |||
| 24ef0dd961 | |||
| 0efb9b72e7 | |||
| dd680b6fd1 | |||
| 788ed68b5b | |||
| 89606d3a05 | |||
| 77319301c6 | |||
| 227bd1cd7e | |||
| 4790dc59a9 | |||
| de53dbffd1 | |||
| 68ac167ec3 | |||
| c8087d1f68 | |||
| c4c21b8b6e | |||
| a7c134a375 | |||
| 9cdea752e8 | |||
| de3c836ac1 | |||
| 2cdc89297f | |||
| 839a5b13b0 | |||
| 40cc4b90fc | |||
| ba54f96efd | |||
| f4a0bcfc96 | |||
| b5696de71e | |||
| a4d4cbd99f | |||
| 08a2c0d082 | |||
| f037f8541c | |||
| 10990e50e6 | |||
| 9dad4c9573 | |||
| 6318b85449 | |||
| 9d313898d0 | |||
| 0380b04b03 | |||
| f32e47a363 | |||
| 3ea7f8a226 | |||
| 2617016e3b | |||
| 90b3bf59e0 | |||
| aadae6334b | |||
| 0f7b902bba | |||
| b899ab6557 | |||
| 7c3ff7d746 | |||
| 332a95a27a | |||
| 4482879fc6 | |||
| 199bf0ff6e | |||
| 24915163ff | |||
| 1289d3255c | |||
| dc40b73fef | |||
| c1cc8a3824 | |||
| 9695ccea0a | |||
| bbcca7f3e5 | |||
| 7b4a82eb8d | |||
| 8393465e8a | |||
| 9ced66f208 | |||
| 633f56a7aa | |||
| 12d51b292c | |||
| e85ef38218 | |||
| 4775b638a4 | |||
| db3b8a1acd | |||
| 17906b505d | |||
| 7a644b6681 | |||
| 48c127fefd | |||
| 778d8e0afa | |||
| 59aa28bf3b | |||
| cea002fd61 | |||
| f8c1cc684b | |||
| 3663fd9f99 | |||
| f8ed9e60f6 | |||
| 00895ff7a9 | |||
| da778e277f | |||
| 6fcfd49cf0 | |||
| 6457a1a2ca | |||
| 4bbd278007 | |||
| f6677aa19e | |||
| a1bca2eba1 | |||
| 4c5cc93a67 | |||
| 7590bd86dd | |||
| 6b25e5299f | |||
| 76ab09bfc8 | |||
| d6f47366d4 | |||
| dc846e7ff6 | |||
| 322bd4e41e | |||
| 282550f402 | |||
| 699713346c | |||
| d4f28fcd85 | |||
| 11e946d61c | |||
| 341239648d | |||
| f3b23a15c9 | |||
| cc61c34cdf | |||
| 91b599b213 | |||
| ce6eb3d939 | |||
| ec5ca9087e | |||
| 8cfdcbdd05 | |||
| 4a72e8c3df | |||
| d747d16b75 | |||
| 106873732c | |||
| 462467a7d8 | |||
| ea12da99ea | |||
| 97a05008b3 | |||
| 69e1dd1c73 | |||
| 95f68b8bde | |||
| 485621b03a | |||
| 3f1b031789 | |||
| d0f125a51b | |||
| a4f7c49f50 | |||
| a378d0c202 | |||
| d235340ec6 | |||
| 93ca536b14 | |||
| dea78c5d13 | |||
| d06ff5f108 | |||
| d94fe7df68 | |||
| 11b53a5dcc | |||
| d4c0ea9c48 | |||
| 459d9b34e7 | |||
| 2ad86ec83d | |||
| 70ddf646be | |||
| 4de31249fd | |||
| 26c3bb05d6 | |||
| b1fe15ed56 | |||
| 83f1613eb8 | |||
| e8e6f5f807 | |||
| 920084b7c3 | |||
| ff2c71367f | |||
| 428efed7c3 | |||
| 7c59c71261 | |||
| a0c2914ce6 | |||
| dbb70a8646 | |||
| 6a423dd032 | |||
| 73e9ff04e8 | |||
| 201464cb44 | |||
| a68fed42ad | |||
| a427427564 | |||
| 2286c0899c | |||
| 55eea6e44f | |||
| ae257d0f50 | |||
| 28a01d61a6 | |||
| bc776d787e | |||
| 2f74dfe5cb | |||
| 84d2caf5d8 | |||
| 456e7d9fdf | |||
| dae66ad2d0 | |||
| 69d254a198 | |||
| b30555f5a4 | |||
| d5b6428cdc | |||
| 7d5bce76c2 | |||
| d4d77611eb | |||
| 662ed03f0a | |||
| fdac0ede45 | |||
| 83aa30d2c5 | |||
| 34b524aabb | |||
| 2e2ea57ba6 | |||
| 72004768cf | |||
| b8e98ae017 | |||
| 3c01dcb30c | |||
| 938b236f54 | |||
| aa439cacda | |||
| e643f2a0ba | |||
| 5b9a98ad6a | |||
| 3323138232 | |||
| cd0aa5e342 | |||
| f63ec81c47 | |||
| 5b63a8e9d9 | |||
| 71244b9bcd | |||
| fd1e369b2a | |||
| c256266432 | |||
| db087a9e78 | |||
| 4e33552a5c | |||
| da25970f79 | |||
| 0807597a3c | |||
| d564866b07 | |||
| 99a45d26ae | |||
| a32d38f7ea | |||
| 5bf9362c92 | |||
| d9925e3453 | |||
| e1106a9d67 | |||
| a8d7a1d01d | |||
| e62597521e | |||
| fa91321299 | |||
| 2d81691407 | |||
| 21f08ca491 | |||
| e202f21934 | |||
| 2936ff0bf9 | |||
| 55dbc82d75 | |||
| 8e4e575276 | |||
| 84752babc8 | |||
| e1ec49fd3c | |||
| 60a1116acd | |||
| a480d7bbe6 | |||
| 7316c5462a | |||
| 2a9960afb3 | |||
| ed899ec73d | |||
| 8f26f9385d | |||
| 599d367343 | |||
| 45241788b3 | |||
| a446cc224d | |||
| 8bfcd4ce3f | |||
| 07f9a9700d | |||
| 7e7acd0049 | |||
| 57985c26a2 | |||
| 504c1f7254 | |||
| 52ad36a6c2 | |||
| 01670e0832 | |||
| e1a93e9b3f | |||
| 2203db548a | |||
| ff66c90855 | |||
| b0779cb330 | |||
| e3f043794b | |||
| 25c021d0a9 | |||
| 8f35c8fb57 | |||
| 45e3b2acf4 | |||
| 082d116924 | |||
| 85ccea60ab | |||
| 49e5493d52 | |||
| a700258d92 | |||
| 712fb9f11a | |||
| c16a1c727d | |||
| 78bc12eb77 | |||
| 9c10145869 | |||
| 99c96bce86 | |||
| 70424183fa | |||
| da2bc6764b | |||
| d11a377f0f | |||
| d445f7cb0d | |||
| d02235b997 | |||
| 899d325ca1 | |||
| f1107b7a91 | |||
| a3f171e7b3 | |||
| 8b3e80a001 | |||
| 13a2d845c6 | |||
| c20c93e648 | |||
| 2c212cb16a | |||
| 848e8a6248 | |||
| 9dcba6a679 | |||
| accded4dfc | |||
| c8f80453b9 | |||
| d857743bf3 | |||
| 78bee017ca | |||
| f08320bd81 | |||
| a1fcdac896 | |||
| ae2d9e6c55 | |||
| 451be273a5 | |||
| bf7473145f | |||
| 01a309ef42 | |||
| bf4d0a8d01 | |||
| 3a919b67ea | |||
| 77fd2c9444 | |||
| 30042aae6d | |||
| 8bc9fb9242 | |||
| 482e90dbc0 | |||
| 0b61546633 | |||
| ca69e64846 | |||
| 6e60aa17dc | |||
| f60884e796 | |||
| d2dbd91b17 | |||
| 0a7bbfb1ca | |||
| 864994a56e | |||
| 3405c5001b | |||
| 4d8732d925 | |||
| 6751f108f0 | |||
| 0feb65c455 | |||
| 4eb9a54fa5 | |||
| 3b2a27dfb2 | |||
| ad66613011 | |||
| a1de100a93 | |||
| cbcbc128ff | |||
| 788a0241d8 | |||
| 25237995eb | |||
| ed75109829 | |||
| 6738a25bfa | |||
| aa9c753455 | |||
| 0e5f9b92e8 | |||
| 77fd4d383b | |||
| 98a03a0127 | |||
| 464130a102 | |||
| dfb324b003 | |||
| 966b8565ba | |||
| 2b774dbdf2 | |||
| cae600ac2f | |||
| 5e4702a4ab | |||
| 8442b2e8e7 | |||
| 64efd0abbc | |||
| b8f3f93d83 | |||
| ce9e851087 | |||
| 53d9a34996 | |||
| 2f79d4a71d | |||
| 6555fd5791 | |||
| 98d72da622 | |||
| c563629756 | |||
| acb3ecbbf7 | |||
| 2cbe6f5543 | |||
| 9beaaa825e | |||
| bf90edb5be | |||
| 94a506f8e8 | |||
| 4f68941f98 | |||
| 25de56a98b | |||
| b11135a096 | |||
| d4db32c0f2 | |||
| d54f35f5f7 | |||
| f163381399 | |||
| 86cb3e8695 | |||
| dcc50c3b53 | |||
| 0209b71182 | |||
| 6b92e47631 | |||
| 36ffebbb49 | |||
| d0bafc6184 | |||
| 801828df70 | |||
| 953c371580 | |||
| 83d3882b9a | |||
| c37777b289 | |||
| ef6a4139de | |||
| 98ceabaf7e | |||
| 85bcb7ad2b | |||
| adea6dd2f6 | |||
| cad0650223 | |||
| d8909fe09c | |||
| bf5acf6724 | |||
| 9bf33f0eea | |||
| 0155a094b1 | |||
| 08432e8b23 | |||
| 717f67874a | |||
| dc27368397 | |||
| de7900cd2e | |||
| c288c2b9a3 | |||
| 26032e46dd | |||
| caee5c6baf | |||
| ea9c37d500 | |||
| a1c223bc2b | |||
| 6afd9b37a6 | |||
| 1930022f19 | |||
| bece953fa3 | |||
| a41d37b1cf | |||
| 31603f7063 | |||
| 4520c06454 | |||
| 9f0c00827d | |||
| 2a66f95873 | |||
| 3330c617b4 | |||
| 0df0b78b90 | |||
| c6b6685dc7 | |||
| 2db45eaca3 | |||
| b876e34586 | |||
| 935436203e | |||
| 9580c8965f | |||
| 7c3c477f18 | |||
| d7cc626396 | |||
| e2cc342adf | |||
| ea785a6585 | |||
| 46a8e586ce | |||
| fa0f15f5c4 | |||
| 6a15c2603d | |||
| 9d50a4d2a5 | |||
| 60263c9040 | |||
| ba6a5825fa | |||
| 321e2f9de0 | |||
| 4e68380c32 | |||
| 9834352de2 | |||
| da87ba8bc7 | |||
| d34255b1fc | |||
| 9b6ab24890 | |||
| 2341622243 | |||
| 956a51ab67 | |||
| 0fdf69be8b | |||
| ee040eb04f | |||
| e358d3b170 | |||
| 71ca90d74b | |||
| fb42472f31 | |||
| 752ce55cfa | |||
| d1e684cb40 | |||
| e6d6d3dd69 | |||
| 98b41256c1 | |||
| bc446a63fe | |||
| dca14f3eb4 | |||
| 1abdca2432 | |||
| 8a214b5181 | |||
| 6db4fedcad | |||
| b6695b6953 | |||
| 8820fa1fdf | |||
| 3e66235932 | |||
| 91f087141b | |||
| 1ab321c7b1 | |||
| dffcf10032 | |||
| 6265354327 | |||
| 054033c73f | |||
| 4fcd23d8dc | |||
| 86f39aee03 | |||
| 9190930f49 | |||
| 11ec43c5c1 | |||
| 1b83c5c180 | |||
| 8a98b66cfd | |||
| 9638bd8bd2 | |||
| 362717c05d | |||
| 16d9b9f527 | |||
| 3ae57100c8 | |||
| 6d33cf6c69 | |||
| 209ccb9ae7 | |||
| 2850c8aa05 | |||
| 87a9a57e6d | |||
| 3e6273d073 | |||
| 8d98813440 | |||
| d0b409341e | |||
| 84489f612b | |||
| ee6c9a3b05 | |||
| d12162cca5 | |||
| 1117f96597 | |||
| d3f3e161e0 | |||
| 90d78d1b60 | |||
| 5673b1907b | |||
| bc9ebb24f9 | |||
| 084e9e00ce | |||
| 96e4f723b1 | |||
| 163a03986c | |||
| 7007389505 | |||
| 2336905dae | |||
| a2164819f5 | |||
| 6b345bfc75 | |||
| a699f32178 | |||
| e597886d0f | |||
| 10e53de029 | |||
| c9336ba32f | |||
| 55190b7318 | |||
| 0328ba1aec | |||
| 1accc6275a | |||
| ce80c9a244 | |||
| 2d9bd394ae | |||
| b3c291206a | |||
| f3f6504398 | |||
| 52629f829d | |||
| 3496c5aece | |||
| 613f56107d | |||
| c47d59a257 | |||
| 8fe0e4223b | |||
| 9abda14530 | |||
| 9df2979d7d | |||
| f4a81bfc04 | |||
| bff7aba778 | |||
| ade7278724 | |||
| f5835baa27 | |||
| d7f6bd0d20 | |||
| d92e5ca2e3 | |||
| 94465892ed | |||
| 4b59caf18f | |||
| a0994ff105 | |||
| 9b3a7b8abc | |||
| bb93f0a4a2 | |||
| 3c8be919da | |||
| 168893a888 | |||
| eb4076399e | |||
| d61d6008da | |||
| 47a78c1bcb | |||
| dfdf85fc0b | |||
| 8862b99e6a | |||
| a1a1b9ebb5 | |||
| 403f64eeed | |||
| 5a26a52157 | |||
| 5aa29cc150 | |||
| 9be3bdb8a5 | |||
| c0baac2b38 | |||
| 31903c8ead | |||
| e59b14b7e7 | |||
| cb869b9064 | |||
| b20fc22577 | |||
| df0c297f55 | |||
| e36dc368d9 | |||
| 656ba98b1b | |||
| 5c17201006 | |||
| d86a1946a7 | |||
| 18bf4a87b4 | |||
| 91475c7103 | |||
| 88934a3f14 | |||
| 80b5c99111 | |||
| 658a045168 | |||
| ccd438ba26 | |||
| 71863f8ba1 | |||
| 6df6e3bdad | |||
| 9019c49ee8 | |||
| 74ebf67aef | |||
| 33036f80fe | |||
| 0688550143 | |||
| d43b673092 | |||
| 4ec37b9d3a | |||
| a5240a852e | |||
| c74c269b9a | |||
| 0cbdebc9b0 | |||
| 4f655acda5 | |||
| 0cb396825f | |||
| 4eb0981552 | |||
| 4257a89cc1 | |||
| 7300316780 | |||
| 94da555fea | |||
| ed8cc2ad0d | |||
| ae47ad882d | |||
| 10bc7d0c1e | |||
| aa11d3504d | |||
| 6903ebda1f | |||
| 6c6e398c53 | |||
| bccb9655d0 | |||
| dd143681f1 | |||
| b1d4a99510 | |||
| 79d9412b31 | |||
| f6c39362cd | |||
| 48fb2a2fcc | |||
| 4562237a96 | |||
| 3420694cca | |||
| c68067eb09 | |||
| a8c73296ea | |||
| 805c1bf8d0 | |||
| 15119fc8d0 | |||
| e2f8a73561 | |||
| 4d9f6ccd22 | |||
| 9112c05477 | |||
| c0c624d62e | |||
| 306a30265e | |||
| 1ee6fc8514 | |||
| 1003ac42df | |||
| ca951aa56b | |||
| 6ea7b32363 | |||
| db4495cf50 | |||
| c35fa7c975 | |||
| ed8cccdf5a | |||
| 9cd8ce5626 | |||
| 990f271c63 | |||
| adfdbaec46 | |||
| 0f4436a465 | |||
| 8ccbf70b39 | |||
| ddd26f3ad4 | |||
| 316e7531a0 | |||
| 0f068f5fb1 | |||
| 8f6b76f9fc | |||
| 468c281d7d | |||
| 419531f373 | |||
| 3457768646 | |||
| f7cb188210 | |||
| 00518d0de7 | |||
| 892a8d2a8a | |||
| ced6c011aa | |||
| efc2008783 | |||
| acb987a451 | |||
| 4a1ea6e2d8 | |||
| cc23d57803 | |||
| 81fdae3dc4 | |||
| 2859eb560c | |||
| 7c5ad67a9f | |||
| 69c3962f21 | |||
| 6c2750b0cc | |||
| abc672e348 |
@@ -128,3 +128,4 @@ src/*.boltdata/
|
||||
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
|
||||
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
|
||||
src/umbraco.sln.ide/*
|
||||
build/UmbracoCms.*/
|
||||
|
||||
+20
-16
@@ -2,9 +2,18 @@
|
||||
IF NOT EXIST UmbracoVersion.txt (
|
||||
ECHO UmbracoVersion.txt missing!
|
||||
GOTO :showerror
|
||||
)
|
||||
SET /p release=<UmbracoVersion.txt
|
||||
SET comment=beta
|
||||
)
|
||||
|
||||
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%)
|
||||
@@ -14,8 +23,11 @@ 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/ >NUL
|
||||
..\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
|
||||
@@ -32,21 +44,13 @@ CALL InstallGit.cmd
|
||||
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
|
||||
ECHO Setting node_modules folder to hidden to prevent VS13 from crashing on it while loading the websites project
|
||||
attrib +h ..\src\Umbraco.Web.UI.Client\node_modules
|
||||
|
||||
ECHO Adding Web.config transform files to the NuGet package
|
||||
ren .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
|
||||
ren .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
|
||||
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%
|
||||
|
||||
+2
-2
@@ -156,8 +156,8 @@
|
||||
<Target Name="ZipWebApp" DependsOnTargets="ZipUmbracoExaminePDF" >
|
||||
<Message Text="Starting to zip to $(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22" />
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileName)%22 %22$(WebAppFolderAbsolutePath)*%22" />
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22 -x!dotLess.Core.dll" />
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileName)%22 %22$(WebAppFolderAbsolutePath)*%22 -x!dotLess.Core.dll" />
|
||||
|
||||
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
@@ -1,74 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core.AllBinaries</id>
|
||||
<version>6.1.2</version>
|
||||
<title>Umbraco Cms Core All Binaries</title>
|
||||
<authors>Morten Christensen</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\bin\AutoMapper.dll" target="lib\AutoMapper.dll" />
|
||||
<file src="..\_BuildOutput\bin\AutoMapper.Net4.dll" target="lib\AutoMapper.Net4.dll" />
|
||||
<file src="..\_BuildOutput\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\bin\ClientDependency.Core.dll" target="lib\ClientDependency.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\ClientDependency.Core.Mvc.dll" target="lib\ClientDependency.Core.Mvc.dll" />
|
||||
<file src="..\_BuildOutput\bin\cms.dll" target="lib\cms.dll" />
|
||||
<file src="..\_BuildOutput\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\bin\CookComputing.XmlRpcV2.dll" target="lib\CookComputing.XmlRpcV2.dll" />
|
||||
<file src="..\_BuildOutput\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\bin\HtmlAgilityPack.dll" target="lib\HtmlAgilityPack.dll" />
|
||||
<file src="..\_BuildOutput\bin\ICSharpCode.SharpZipLib.dll" target="lib\ICSharpCode.SharpZipLib.dll" />
|
||||
<file src="..\_BuildOutput\bin\ImageProcessor.dll" target="lib\ImageProcessor.dll" />
|
||||
<file src="..\_BuildOutput\bin\ImageProcessor.Web.dll" target="lib\ImageProcessor.Web.dll" />
|
||||
<file src="..\_BuildOutput\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
<file src="..\_BuildOutput\bin\Lucene.Net.dll" target="lib\Lucene.Net.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Threading.Tasks.dll" target="lib\Microsoft.Threading.Tasks.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Threading.Tasks.Extensions.dll" target="lib\Microsoft.Threading.Tasks.Extensions.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Web.Helpers.dll" target="lib\Microsoft.Web.Helpers.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Web.Infrastructure.dll" target="lib\Microsoft.Web.Infrastructure.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Web.Mvc.FixedDisplayModes.dll" target="lib\Microsoft.Web.Mvc.FixedDisplayModes.dll" />
|
||||
<file src="..\_BuildOutput\bin\MiniProfiler.dll" target="lib\MiniProfiler.dll" />
|
||||
<file src="..\_BuildOutput\bin\MySql.Data.dll" target="lib\MySql.Data.dll" />
|
||||
<file src="..\_BuildOutput\bin\Newtonsoft.Json.dll" target="lib\Newtonsoft.Json.dll" />
|
||||
<file src="..\_BuildOutput\bin\NuGet.Core.dll" target="lib\NuGet.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.dll" target="lib\System.Net.Http.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Extensions.dll" target="lib\System.Net.Http.Extensions.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Formatting.dll" target="lib\System.Net.Http.Formatting.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Primitives.dll" target="lib\System.Net.Http.Primitives.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.WebRequest.dll" target="lib\System.Net.Http.WebRequest.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Helpers.dll" target="lib\System.Web.Helpers.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Http.dll" target="lib\System.Web.Http.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Http.WebHost.dll" target="lib\System.Web.Http.WebHost.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Mvc.dll" target="lib\System.Web.Mvc.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Razor.dll" target="lib\System.Web.Razor.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Administration.dll" target="lib\System.Web.WebPages.Administration.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Deployment.dll" target="lib\System.Web.WebPages.Deployment.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.dll" target="lib\System.Web.WebPages.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Razor.dll" target="lib\System.Web.WebPages.Razor.dll" />
|
||||
<file src="..\_BuildOutput\bin\TidyNet.dll" target="lib\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.dll" target="lib\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.providers.dll" target="lib\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Web.UI.dll" target="lib\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
</files>
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core.AllBinaries</id>
|
||||
<version>6.1.2</version>
|
||||
<title>Umbraco Cms Core All Binaries</title>
|
||||
<authors>Morten Christensen</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies and all of the dependant assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\bin\AutoMapper.dll" target="lib\AutoMapper.dll" />
|
||||
<file src="..\_BuildOutput\bin\AutoMapper.Net4.dll" target="lib\AutoMapper.Net4.dll" />
|
||||
<file src="..\_BuildOutput\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\bin\ClientDependency.Core.dll" target="lib\ClientDependency.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\ClientDependency.Core.Mvc.dll" target="lib\ClientDependency.Core.Mvc.dll" />
|
||||
<file src="..\_BuildOutput\bin\cms.dll" target="lib\cms.dll" />
|
||||
<file src="..\_BuildOutput\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\bin\CookComputing.XmlRpcV2.dll" target="lib\CookComputing.XmlRpcV2.dll" />
|
||||
<file src="..\_BuildOutput\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\bin\HtmlAgilityPack.dll" target="lib\HtmlAgilityPack.dll" />
|
||||
<file src="..\_BuildOutput\bin\ICSharpCode.SharpZipLib.dll" target="lib\ICSharpCode.SharpZipLib.dll" />
|
||||
<file src="..\_BuildOutput\bin\ImageProcessor.dll" target="lib\ImageProcessor.dll" />
|
||||
<file src="..\_BuildOutput\bin\ImageProcessor.Web.dll" target="lib\ImageProcessor.Web.dll" />
|
||||
<file src="..\_BuildOutput\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
<file src="..\_BuildOutput\bin\Lucene.Net.dll" target="lib\Lucene.Net.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Threading.Tasks.dll" target="lib\Microsoft.Threading.Tasks.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Threading.Tasks.Extensions.dll" target="lib\Microsoft.Threading.Tasks.Extensions.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Web.Infrastructure.dll" target="lib\Microsoft.Web.Infrastructure.dll" />
|
||||
<file src="..\_BuildOutput\bin\Microsoft.Web.Mvc.FixedDisplayModes.dll" target="lib\Microsoft.Web.Mvc.FixedDisplayModes.dll" />
|
||||
<file src="..\_BuildOutput\bin\MiniProfiler.dll" target="lib\MiniProfiler.dll" />
|
||||
<file src="..\_BuildOutput\bin\MySql.Data.dll" target="lib\MySql.Data.dll" />
|
||||
<file src="..\_BuildOutput\bin\Newtonsoft.Json.dll" target="lib\Newtonsoft.Json.dll" />
|
||||
<file src="..\_BuildOutput\bin\NuGet.Core.dll" target="lib\NuGet.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.dll" target="lib\System.Net.Http.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Extensions.dll" target="lib\System.Net.Http.Extensions.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Formatting.dll" target="lib\System.Net.Http.Formatting.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.Primitives.dll" target="lib\System.Net.Http.Primitives.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Net.Http.WebRequest.dll" target="lib\System.Net.Http.WebRequest.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Helpers.dll" target="lib\System.Web.Helpers.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Http.dll" target="lib\System.Web.Http.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Http.WebHost.dll" target="lib\System.Web.Http.WebHost.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Mvc.dll" target="lib\System.Web.Mvc.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.Razor.dll" target="lib\System.Web.Razor.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Administration.dll" target="lib\System.Web.WebPages.Administration.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Deployment.dll" target="lib\System.Web.WebPages.Deployment.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.dll" target="lib\System.Web.WebPages.dll" />
|
||||
<file src="..\_BuildOutput\bin\System.Web.WebPages.Razor.dll" target="lib\System.Web.WebPages.Razor.dll" />
|
||||
<file src="..\_BuildOutput\bin\TidyNet.dll" target="lib\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.dll" target="lib\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\bin\umbraco.providers.dll" target="lib\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\bin\Umbraco.Web.UI.dll" target="lib\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,71 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\..\src\SQLCE4Umbraco\obj\Debug\SQLCE4Umbraco.dll" target="lib" />
|
||||
<file src="..\..\src\SQLCE4Umbraco\obj\Debug\SQLCE4Umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\SQLCE4Umbraco\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\SQLCE4Umbraco" />
|
||||
|
||||
<file src="..\..\src\umbraco.businesslogic\obj\Debug\businesslogic.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.businesslogic\obj\Debug\businesslogic.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.businesslogic\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.businesslogic" />
|
||||
|
||||
<file src="..\..\src\umbraco.cms\obj\Debug\cms.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.cms\obj\Debug\cms.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.cms\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.cms" />
|
||||
|
||||
<file src="..\..\src\umbraco.controls\obj\Debug\controls.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.controls\obj\Debug\controls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.controls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.controls" />
|
||||
|
||||
<file src="..\..\src\umbraco.interfaces\obj\Debug\interfaces.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.interfaces\obj\Debug\interfaces.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.interfaces\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.interfaces" />
|
||||
|
||||
<file src="..\..\src\Umbraco.Core\obj\Debug\Umbraco.Core.dll" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Core\obj\Debug\Umbraco.Core.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Core\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
|
||||
|
||||
<file src="..\..\src\umbraco.datalayer\obj\Debug\umbraco.DataLayer.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.datalayer\obj\Debug\umbraco.DataLayer.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.datalayer\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.datalayer" />
|
||||
|
||||
<file src="..\..\src\umbraco.editorControls\obj\Debug\umbraco.editorControls.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.editorControls\obj\Debug\umbraco.editorControls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.editorControls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.editorControls" />
|
||||
|
||||
<file src="..\..\src\umbraco.MacroEngines\obj\Debug\umbraco.MacroEngines.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.MacroEngines\obj\Debug\umbraco.MacroEngines.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.MacroEngines\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.MacroEngines" />
|
||||
|
||||
<file src="..\..\src\umbraco.providers\obj\Debug\umbraco.providers.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.providers\obj\Debug\umbraco.providers.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.providers\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.providers" />
|
||||
|
||||
<file src="..\..\src\Umbraco.Web\obj\Debug\umbraco.dll" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web\obj\Debug\umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
|
||||
|
||||
<file src="..\..\src\bin\Debug\Umbraco.Web.UI.dll" target="lib" />
|
||||
<file src="..\..\src\bin\Debug\Umbraco.Web.UI.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web.UI\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web.UI" />
|
||||
|
||||
<file src="..\..\src\UmbracoExamine\obj\Debug\UmbracoExamine.dll" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\obj\Debug\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
|
||||
</files>
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\..\src\SQLCE4Umbraco\obj\Debug\SQLCE4Umbraco.dll" target="lib" />
|
||||
<file src="..\..\src\SQLCE4Umbraco\obj\Debug\SQLCE4Umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\SQLCE4Umbraco\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\SQLCE4Umbraco" />
|
||||
|
||||
<file src="..\..\src\umbraco.businesslogic\obj\Debug\businesslogic.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.businesslogic\obj\Debug\businesslogic.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.businesslogic\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.businesslogic" />
|
||||
|
||||
<file src="..\..\src\umbraco.cms\obj\Debug\cms.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.cms\obj\Debug\cms.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.cms\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.cms" />
|
||||
|
||||
<file src="..\..\src\umbraco.controls\obj\Debug\controls.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.controls\obj\Debug\controls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.controls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.controls" />
|
||||
|
||||
<file src="..\..\src\umbraco.interfaces\obj\Debug\interfaces.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.interfaces\obj\Debug\interfaces.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.interfaces\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.interfaces" />
|
||||
|
||||
<file src="..\..\src\Umbraco.Core\obj\Debug\Umbraco.Core.dll" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Core\obj\Debug\Umbraco.Core.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Core\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
|
||||
|
||||
<file src="..\..\src\umbraco.datalayer\obj\Debug\umbraco.DataLayer.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.datalayer\obj\Debug\umbraco.DataLayer.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.datalayer\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.datalayer" />
|
||||
|
||||
<file src="..\..\src\umbraco.editorControls\obj\Debug\umbraco.editorControls.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.editorControls\obj\Debug\umbraco.editorControls.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.editorControls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.editorControls" />
|
||||
|
||||
<file src="..\..\src\umbraco.MacroEngines\obj\Debug\umbraco.MacroEngines.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.MacroEngines\obj\Debug\umbraco.MacroEngines.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.MacroEngines\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.MacroEngines" />
|
||||
|
||||
<file src="..\..\src\umbraco.providers\obj\Debug\umbraco.providers.dll" target="lib" />
|
||||
<file src="..\..\src\umbraco.providers\obj\Debug\umbraco.providers.pdb" target="lib" />
|
||||
<file src="..\..\src\umbraco.providers\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.providers" />
|
||||
|
||||
<file src="..\..\src\Umbraco.Web\obj\Debug\umbraco.dll" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web\obj\Debug\umbraco.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
|
||||
|
||||
<file src="..\..\src\bin\Debug\Umbraco.Web.UI.dll" target="lib" />
|
||||
<file src="..\..\src\bin\Debug\Umbraco.Web.UI.pdb" target="lib" />
|
||||
<file src="..\..\src\Umbraco.Web.UI\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web.UI" />
|
||||
|
||||
<file src="..\..\src\UmbracoExamine\obj\Debug\UmbracoExamine.dll" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\obj\Debug\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,73 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.40804.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.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)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.6.5]" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.8.0, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[1.9.5, 2.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[3.3.1, 4.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\cms.dll" />
|
||||
<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\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" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.Web.Helpers.dll" target="lib\Microsoft.Web.Helpers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\umbraco.MacroEngines.xml" />
|
||||
<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\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>
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 6.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)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.6.5]" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.8.2.1, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.60, 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>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\cms.dll" />
|
||||
<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\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" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\umbraco.MacroEngines.xml" />
|
||||
<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\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,43 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata minClientVersion="2.8">
|
||||
<id>UmbracoCms</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<metadata minClientVersion="2.8">
|
||||
<id>UmbracoCms</id>
|
||||
<version>7.0.0</version>
|
||||
<title>Umbraco Cms</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\Configs\**" target="Content\config" exclude="..\_BuildOutput\Configs\Web.config.transform" />
|
||||
<file src="..\_BuildOutput\WebApp\css\**" target="Content\css" />
|
||||
<file src="..\_BuildOutput\WebApp\masterpages\**" target="Content\masterpages" />
|
||||
<file src="..\_BuildOutput\WebApp\media\**" target="Content\media" />
|
||||
<file src="..\_BuildOutput\WebApp\scripts\**" target="Content\scripts" />
|
||||
<file src="..\_BuildOutput\WebApp\usercontrols\**" target="Content\usercontrols" />
|
||||
<file src="..\_BuildOutput\WebApp\Views\**" target="Content\Views" exclude="..\_BuildOutput\WebApp\Views\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\xslt\**" target="Content\xslt" />
|
||||
<file src="..\_BuildOutput\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Plugins\**" target="UmbracoFiles\App_Plugins" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\config\splashes" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="..\_BuildOutput\Configs\**" target="Content\Config" exclude="..\_BuildOutput\Configs\Web.config.transform" />
|
||||
<file src="..\_BuildOutput\WebApp\Views\**" target="Content\Views" exclude="..\_BuildOutput\WebApp\Views\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Plugins\**" target="UmbracoFiles\App_Plugins" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="tools\install.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\Readme.txt" target="tools\Readme.txt" />
|
||||
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoExamine.PDF</id>
|
||||
<version>0.7.0</version>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>UmbracoExmine.PDF</description>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[7.0.0, 8.0.0)" />
|
||||
<dependency id="iTextSharp" version="[5.5.3, 6.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\UmbracoExamine.PDF\UmbracoExamine.PDF.dll" target="lib\UmbracoExamine.PDF.dll" />
|
||||
<file src="..\_BuildOutput\UmbracoExamine.PDF\UmbracoExamine.PDF.XML" target="lib\UmbracoExamine.PDF.XML" />
|
||||
</files>
|
||||
<metadata>
|
||||
<id>UmbracoExamine.PDF</id>
|
||||
<version>0.7.0</version>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>UmbracoExmine.PDF</description>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[7.0.0, 8.0.0)" />
|
||||
<dependency id="iTextSharp" version="[5.5.3, 6.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\UmbracoExamine.PDF\UmbracoExamine.PDF.dll" target="lib\UmbracoExamine.PDF.dll" />
|
||||
<file src="..\_BuildOutput\UmbracoExamine.PDF\UmbracoExamine.PDF.XML" target="lib\UmbracoExamine.PDF.XML" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -26,11 +26,15 @@
|
||||
views/dashboard/developer/developerdashboardvideos.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control>
|
||||
views/dashboard/developer/examinemanagement.html
|
||||
</control>
|
||||
</tab>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)">
|
||||
@@ -53,6 +57,7 @@
|
||||
views/dashboard/default/startupdashboardintro.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Change password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Change Password" xdt:Transform="Insert">
|
||||
<control showOnce="false" addPanel="false" panelCaption="">
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
|
||||
_ _ __ __ ____ _____ _____ ____
|
||||
| | | | \/ | _ \| __ \ /\ / ____/ __ \
|
||||
| | | | \ / | |_) | |__) | / \ | | | | | |
|
||||
| | | | |\/| | _ <| _ / / /\ \| | | | | |
|
||||
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
|
||||
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
A note about running Umbraco from Visual Studio.
|
||||
|
||||
Don't forget to build!
|
||||
|
||||
BETA Notice: We've done our best to transform your config files, we are testing this in 7.2 beta and would love to
|
||||
hear back if it didn't work so we can improve this feature. We'd love to see your before and after config files
|
||||
(make sure to delete sensitive data like passwords and API keys) in the issue tracker at http://issues.umbraco.org/
|
||||
Remember, we backed up your files in App_Data\NuGetBackup so you can find the original file before it was transformed.
|
||||
|
||||
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
|
||||
file (and config files in the config folder).
|
||||
|
||||
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
|
||||
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or a package
|
||||
might have added. Only the existing files were overwritten. If you customized anything then make sure to do a compare
|
||||
and merge with the NuGetBackup folder.
|
||||
We've done our best to transform your configuration files but in case something is not quite right: remember we
|
||||
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
|
||||
|
||||
This nuget package includes build targets that extends the creation of a deploy package, which is generated by
|
||||
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
|
||||
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
|
||||
a package might have added. Only the existing files were overwritten. If you customized anything then make
|
||||
sure to do a compare and merge with the NuGetBackup folder.
|
||||
|
||||
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
|
||||
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
|
||||
Publish this won't affect you.
|
||||
These things will now be automatically included when creating a deploy package or publishing to the file system:
|
||||
umbraco, umbraco_client, config\splashes and global.asax.
|
||||
The following items will now be automatically included when creating a deploy package or publishing to the file
|
||||
system: umbraco, umbraco_client, config\splashes and global.asax.
|
||||
|
||||
Please read the release notes on our.umbraco.org:
|
||||
http://our.umbraco.org/contribute/releases
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<section name="ExamineLuceneIndexSets" type="Examine.LuceneEngine.Config.IndexSets, Examine" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type,requirePermission)" />
|
||||
|
||||
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)">
|
||||
<section name="umbraco.presentation.Properties.Settings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
</sectionGroup>
|
||||
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
|
||||
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
@@ -52,8 +54,6 @@
|
||||
<compilation>
|
||||
<assemblies xdt:Transform="InsertIfMissing" />
|
||||
<assemblies>
|
||||
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
|
||||
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
|
||||
<add assembly="System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
<add assembly="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
|
||||
@@ -71,6 +71,13 @@
|
||||
</assemblies>
|
||||
</compilation>
|
||||
|
||||
<compilation>
|
||||
<assemblies>
|
||||
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
|
||||
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
|
||||
</assemblies>
|
||||
</compilation>
|
||||
|
||||
<xhtmlConformance xdt:Transform="Remove" />
|
||||
|
||||
<httpModules xdt:Transform="InsertIfMissing" />
|
||||
@@ -126,38 +133,48 @@
|
||||
|
||||
<runtime xdt:Transform="InsertIfMissing" />
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" xdt:Transform="SetAttributes()">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="InsertIfMissing">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="InsertIfMissing">
|
||||
<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 xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="InsertIfMissing">
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.1" />
|
||||
</dependentAssembly>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" xdt:Transform="InsertIfMissing" />
|
||||
</runtime>
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Replace">
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Replace">
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<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 xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Replace">
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.1" />
|
||||
</dependentAssembly>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
<system.web.webPages.razor xdt:Transform="Remove" />
|
||||
|
||||
<location path="umbraco" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
|
||||
<location path="umbraco" xdt:Locator="Match(path)">
|
||||
<system.webServer xdt:Transform="InsertIfMissing">
|
||||
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
|
||||
</system.webServer>
|
||||
</location>
|
||||
|
||||
<location path="App_Plugins" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
|
||||
<location path="App_Plugins" xdt:Locator="Match(path)">
|
||||
<system.webServer xdt:Transform="InsertIfMissing">
|
||||
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
|
||||
</system.webServer>
|
||||
</location>
|
||||
|
||||
</configuration>
|
||||
@@ -27,7 +27,6 @@ if ($project) {
|
||||
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 }
|
||||
|
||||
@@ -49,98 +49,79 @@ if ($project) {
|
||||
}
|
||||
|
||||
$copyWebconfig = $false
|
||||
try
|
||||
|
||||
# SJ - What can I say: big up for James Newton King for teaching us a hack for detecting if this is a new install vs. an upgrade!
|
||||
# https://github.com/JamesNK/Newtonsoft.Json/pull/387 - would never have seen this without the controversial pull request..
|
||||
Try
|
||||
{
|
||||
# SJ - What can I say: big up for James Newton King for teaching us a hack for detecting if this is a new install vs. an upgrade!
|
||||
# https://github.com/JamesNK/Newtonsoft.Json/pull/387 - would never have seen this without the controversial pull request..
|
||||
$dte2 = Get-Interface $dte ([EnvDTE80.DTE2])
|
||||
|
||||
if ($dte2.ActiveWindow.Caption -eq "Package Manager Console")
|
||||
{
|
||||
# user is installing from VS NuGet console
|
||||
# see if user is installing from VS NuGet console
|
||||
# get reference to the window, the console host and the input history
|
||||
# copy web.config if "install-package UmbracoCms" was last input
|
||||
|
||||
# this is in a try-catch as they might be using the regular NuGet dialog
|
||||
# instead of package manager console
|
||||
|
||||
$dte2 = Get-Interface $dte ([EnvDTE80.DTE2])
|
||||
|
||||
$consoleWindow = $(Get-VSComponentModel).GetService([NuGetConsole.IPowerConsoleWindow])
|
||||
|
||||
$props = $consoleWindow.GetType().GetProperties([System.Reflection.BindingFlags]::Instance -bor `
|
||||
[System.Reflection.BindingFlags]::NonPublic)
|
||||
$props = $consoleWindow.GetType().GetProperties([System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
|
||||
|
||||
$prop = $props | ? { $_.Name -eq "ActiveHostInfo" } | select -first 1
|
||||
if ($prop -eq $null) { return }
|
||||
$prop = $props | ? { $_.Name -eq "ActiveHostInfo" } | select -first 1
|
||||
|
||||
$hostInfo = $prop.GetValue($consoleWindow)
|
||||
if ($hostInfo -eq $null) { return }
|
||||
$hostInfo = $prop.GetValue($consoleWindow)
|
||||
|
||||
$history = $hostInfo.WpfConsole.InputHistory.History
|
||||
|
||||
$lastCommand = $history | select -last 1
|
||||
$lastCommand = $history | select -last 1
|
||||
|
||||
if ($lastCommand)
|
||||
{
|
||||
$lastCommand = $lastCommand.Trim().ToLower()
|
||||
if ($lastCommand.StartsWith("install-package") -and $lastCommand.Contains("umbracocms"))
|
||||
{
|
||||
$copyWebconfig = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$lastCommand = $lastCommand.Trim().ToLower()
|
||||
if ($lastCommand.StartsWith("install-package") -and $lastCommand.Contains("umbracocms"))
|
||||
{
|
||||
$copyWebconfig = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
|
||||
Try
|
||||
{
|
||||
# user is installing from VS NuGet dialog
|
||||
# get reference to the window, then smart output console provider
|
||||
# copy web.config if messages in buffered console contains "installing...UmbracoCms" in last operation
|
||||
|
||||
$instanceField = [NuGet.Dialog.PackageManagerWindow].GetField("CurrentInstance", [System.Reflection.BindingFlags]::Static -bor `
|
||||
[System.Reflection.BindingFlags]::NonPublic)
|
||||
$consoleField = [NuGet.Dialog.PackageManagerWindow].GetField("_smartOutputConsoleProvider", [System.Reflection.BindingFlags]::Instance -bor `
|
||||
[System.Reflection.BindingFlags]::NonPublic)
|
||||
if ($instanceField -eq $null -or $consoleField -eq $null) { return }
|
||||
|
||||
$instanceField = [NuGet.Dialog.PackageManagerWindow].GetField("CurrentInstance", [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::NonPublic)
|
||||
$consoleField = [NuGet.Dialog.PackageManagerWindow].GetField("_smartOutputConsoleProvider", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
|
||||
|
||||
$instance = $instanceField.GetValue($null)
|
||||
if ($instance -eq $null) { return }
|
||||
|
||||
|
||||
$consoleProvider = $consoleField.GetValue($instance)
|
||||
if ($consoleProvider -eq $null) { return }
|
||||
|
||||
$console = $consoleProvider.CreateOutputConsole($false)
|
||||
|
||||
$messagesField = $console.GetType().GetField("_messages", [System.Reflection.BindingFlags]::Instance -bor `
|
||||
[System.Reflection.BindingFlags]::NonPublic)
|
||||
if ($messagesField -eq $null) { return }
|
||||
|
||||
$messagesField = $console.GetType().GetField("_messages", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
|
||||
|
||||
$messages = $messagesField.GetValue($console)
|
||||
if ($messages -eq $null) { return }
|
||||
|
||||
|
||||
$operations = $messages -split "=============================="
|
||||
|
||||
$lastOperation = $operations | select -last 1
|
||||
|
||||
if ($lastOperation)
|
||||
{
|
||||
$lastOperation = $lastOperation.ToLower()
|
||||
$lastOperation = $lastOperation.ToLower()
|
||||
$lines = $lastOperation -split "`r`n"
|
||||
$installMatch = $lines | ? { $_.Contains("...umbracocms ") } | select -first 1
|
||||
|
||||
$lines = $lastOperation -split "`r`n"
|
||||
|
||||
$installMatch = $lines | ? { $_.StartsWith("------- installing...umbracocms ") } | select -first 1
|
||||
|
||||
if ($installMatch)
|
||||
{
|
||||
$copyWebconfig = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
# stop potential errors from bubbling up
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
$FailedItem = $_.Exception.ItemName
|
||||
$installLogFile = Join-Path $projectDestinationPath "NuGetInstallError.log"
|
||||
$text = "Error occurred: " + $ErrorMessage + " failure: " + $FailedItem
|
||||
$text | Out-File $installLogFile
|
||||
}
|
||||
|
||||
if ($installMatch)
|
||||
{
|
||||
$copyWebconfig = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
|
||||
if($copyWebconfig -eq $true)
|
||||
{
|
||||
$packageWebConfigSource = Join-Path $rootPath "UmbracoFiles\Web.config"
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
7.2.0
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.2.2
|
||||
@@ -229,5 +229,17 @@ 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
|
||||
<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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
public interface IScheduledTasksSection : IUmbracoConfigurationSection
|
||||
{
|
||||
IEnumerable<IScheduledTask> Tasks { get; }
|
||||
|
||||
string BaseUrl { 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"]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.2.0");
|
||||
private static readonly Version Version = new Version("7.2.2");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "beta"; } }
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
@@ -34,38 +35,42 @@ namespace Umbraco.Core.Dynamics
|
||||
FoundExtensionMethod
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to invoke a member based on the dynamic instance
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="thisObject">The object instance to invoke the extension method for</param>
|
||||
/// <param name="binder"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// First tries to find a property with the binder name, if that fails it will try to find a static or instance method
|
||||
/// on the object that matches the binder name
|
||||
/// </remarks>
|
||||
public static Attempt<TryInvokeMemberResult> TryInvokeMember<T>(T thisObject, InvokeMemberBinder binder, object[] args)
|
||||
/// <summary>
|
||||
/// Attempts to invoke a member based on the dynamic instance
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="runtimeCache"></param>
|
||||
/// <param name="thisObject">The object instance to invoke the extension method for</param>
|
||||
/// <param name="binder"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// First tries to find a property with the binder name, if that fails it will try to find a static or instance method
|
||||
/// on the object that matches the binder name
|
||||
/// </remarks>
|
||||
public static Attempt<TryInvokeMemberResult> TryInvokeMember<T>(IRuntimeCacheProvider runtimeCache, T thisObject, InvokeMemberBinder binder, object[] args)
|
||||
{
|
||||
return TryInvokeMember<T>(thisObject, binder, args, null);
|
||||
return TryInvokeMember<T>(runtimeCache, thisObject, binder, args, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to invoke a member based on the dynamic instance
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="thisObject">The object instance to invoke the extension method for</param>
|
||||
/// <param name="binder"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="findExtensionMethodsOnTypes">The types to scan for extension methods </param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// First tries to find a property with the binder name, if that fails it will try to find a static or instance method
|
||||
/// on the object that matches the binder name, if that fails it will then attempt to invoke an extension method
|
||||
/// based on the binder name and the extension method types to scan.
|
||||
/// </remarks>
|
||||
public static Attempt<TryInvokeMemberResult> TryInvokeMember<T>(T thisObject,
|
||||
/// <summary>
|
||||
/// Attempts to invoke a member based on the dynamic instance
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="runtimeCache"></param>
|
||||
/// <param name="thisObject">The object instance to invoke the extension method for</param>
|
||||
/// <param name="binder"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="findExtensionMethodsOnTypes">The types to scan for extension methods </param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// First tries to find a property with the binder name, if that fails it will try to find a static or instance method
|
||||
/// on the object that matches the binder name, if that fails it will then attempt to invoke an extension method
|
||||
/// based on the binder name and the extension method types to scan.
|
||||
/// </remarks>
|
||||
public static Attempt<TryInvokeMemberResult> TryInvokeMember<T>(
|
||||
IRuntimeCacheProvider runtimeCache,
|
||||
T thisObject,
|
||||
InvokeMemberBinder binder,
|
||||
object[] args,
|
||||
IEnumerable<Type> findExtensionMethodsOnTypes)
|
||||
@@ -76,9 +81,9 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
//Property?
|
||||
result = typeof(T).InvokeMember(binder.Name,
|
||||
System.Reflection.BindingFlags.Instance |
|
||||
System.Reflection.BindingFlags.Public |
|
||||
System.Reflection.BindingFlags.GetProperty,
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.GetProperty,
|
||||
null,
|
||||
thisObject,
|
||||
args);
|
||||
@@ -90,10 +95,10 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
//Static or Instance Method?
|
||||
result = typeof(T).InvokeMember(binder.Name,
|
||||
System.Reflection.BindingFlags.Instance |
|
||||
System.Reflection.BindingFlags.Public |
|
||||
System.Reflection.BindingFlags.Static |
|
||||
System.Reflection.BindingFlags.InvokeMethod,
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.InvokeMethod,
|
||||
null,
|
||||
thisObject,
|
||||
args);
|
||||
@@ -105,7 +110,7 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
try
|
||||
{
|
||||
result = FindAndExecuteExtensionMethod(thisObject, args, binder.Name, findExtensionMethodsOnTypes);
|
||||
result = FindAndExecuteExtensionMethod(runtimeCache, thisObject, args, binder.Name, findExtensionMethodsOnTypes);
|
||||
return Attempt.Succeed(new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundExtensionMethod));
|
||||
}
|
||||
catch (TargetInvocationException ext)
|
||||
@@ -138,16 +143,19 @@ namespace Umbraco.Core.Dynamics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find an extension method that matches the name and arguments based on scanning the Type's passed in
|
||||
/// to the findMethodsOnTypes parameter
|
||||
/// </summary>
|
||||
/// <param name="thisObject">The instance object to execute the extension method for</param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="findMethodsOnTypes"></param>
|
||||
/// <returns></returns>
|
||||
internal static object FindAndExecuteExtensionMethod<T>(T thisObject,
|
||||
/// <summary>
|
||||
/// Attempts to find an extension method that matches the name and arguments based on scanning the Type's passed in
|
||||
/// to the findMethodsOnTypes parameter
|
||||
/// </summary>
|
||||
/// <param name="runtimeCache"></param>
|
||||
/// <param name="thisObject">The instance object to execute the extension method for</param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="findMethodsOnTypes"></param>
|
||||
/// <returns></returns>
|
||||
internal static object FindAndExecuteExtensionMethod<T>(
|
||||
IRuntimeCacheProvider runtimeCache,
|
||||
T thisObject,
|
||||
object[] args,
|
||||
string name,
|
||||
IEnumerable<Type> findMethodsOnTypes)
|
||||
@@ -158,7 +166,7 @@ namespace Umbraco.Core.Dynamics
|
||||
MethodInfo toExecute = null;
|
||||
foreach (var t in findMethodsOnTypes)
|
||||
{
|
||||
toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false);
|
||||
toExecute = ExtensionMethodFinder.FindExtensionMethod(runtimeCache, t, args, name, false);
|
||||
if (toExecute != null)
|
||||
break;
|
||||
}
|
||||
@@ -166,6 +174,13 @@ namespace Umbraco.Core.Dynamics
|
||||
if (toExecute != null)
|
||||
{
|
||||
var genericArgs = (new[] { (object)thisObject }).Concat(args);
|
||||
|
||||
// else we'd get an exception w/ message "Late bound operations cannot
|
||||
// be performed on types or methods for which ContainsGenericParameters is true."
|
||||
// because MakeGenericMethod must be used to obtain an actual method that can run
|
||||
if (toExecute.ContainsGenericParameters)
|
||||
throw new InvalidOperationException("Method contains generic parameters, something's wrong.");
|
||||
|
||||
result = toExecute.Invoke(null, genericArgs.ToArray());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Collections;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
@@ -162,8 +163,10 @@ namespace Umbraco.Core.Dynamics
|
||||
return true; //anyway
|
||||
}
|
||||
|
||||
var runtimeCache = ApplicationContext.Current != null ? ApplicationContext.Current.ApplicationCache.RuntimeCache : new NullCacheProvider();
|
||||
|
||||
//ok, now lets try to match by member, property, extensino method
|
||||
var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[]
|
||||
var attempt = DynamicInstanceHelper.TryInvokeMember(runtimeCache, this, binder, args, new[]
|
||||
{
|
||||
typeof (IEnumerable<DynamicXml>),
|
||||
typeof (IEnumerable<XElement>),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web.Compilation;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections;
|
||||
using System.Linq.Expressions;
|
||||
using System.Web.Services.Description;
|
||||
using Umbraco.Core.Cache;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
@@ -14,279 +15,152 @@ namespace Umbraco.Core.Dynamics
|
||||
/// </summary>
|
||||
internal static class ExtensionMethodFinder
|
||||
{
|
||||
private static readonly MethodInfo[] AllExtensionMethods;
|
||||
/// <summary>
|
||||
/// The static cache for extension methods found that match the criteria that we are looking for
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]> MethodCache = new ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]>();
|
||||
|
||||
static ExtensionMethodFinder()
|
||||
{
|
||||
AllExtensionMethods = TypeFinder.GetAssembliesWithKnownExclusions()
|
||||
/// <summary>
|
||||
/// Returns the enumerable of all extension method info's in the app domain = USE SPARINGLY!!!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We cache this as a sliding 5 minute exiration, in unit tests there's over 1100 methods found, surely that will eat up a bit of memory so we want
|
||||
/// to make sure we give it back.
|
||||
/// </remarks>
|
||||
private static IEnumerable<MethodInfo> GetAllExtensionMethodsInAppDomain(IRuntimeCacheProvider runtimeCacheProvider)
|
||||
{
|
||||
if (runtimeCacheProvider == null) throw new ArgumentNullException("runtimeCacheProvider");
|
||||
|
||||
return runtimeCacheProvider.GetCacheItem<MethodInfo[]>(typeof (ExtensionMethodFinder).Name, () => TypeFinder.GetAssembliesWithKnownExclusions()
|
||||
// assemblies that contain extension methods
|
||||
.Where(a => a.IsDefined(typeof(ExtensionAttribute), false))
|
||||
.Where(a => a.IsDefined(typeof (ExtensionAttribute), false))
|
||||
// types that contain extension methods
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.Where(t => t.IsDefined(typeof(ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
|
||||
.Where(t => t.IsDefined(typeof (ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
|
||||
// actual extension methods
|
||||
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.Where(m => m.IsDefined(typeof(ExtensionAttribute), false)))
|
||||
.Where(m => m.IsDefined(typeof (ExtensionAttribute), false)))
|
||||
// and also IEnumerable<T> extension methods - because the assembly is excluded
|
||||
.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
.ToArray();
|
||||
}
|
||||
.Concat(typeof (Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
//If we don't do this then we'll be scanning all assemblies each time!
|
||||
.ToArray(),
|
||||
|
||||
//only cache for 5 minutes
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
|
||||
// ORIGINAL CODE IS NOT COMPLETE, DOES NOT HANDLE GENERICS, ETC...
|
||||
|
||||
// so this is an attempt at fixing things, but it's not done yet
|
||||
// and do we really want to do this? extension methods are not supported on dynamics, period
|
||||
// we should use strongly typed content instead of dynamics.
|
||||
|
||||
/*
|
||||
|
||||
// get all extension methods for type thisType, with name name,
|
||||
// accepting argsCount arguments (not counting the instance of thisType).
|
||||
private static IEnumerable<MethodInfo> GetExtensionMethods(Type thisType, string name, int argsCount)
|
||||
{
|
||||
var key = string.Format("{0}.{1}::{2}", thisType.FullName, name, argsCount);
|
||||
|
||||
var types = thisType.GetBaseTypes(true); // either do this OR have MatchFirstParameter handle the stuff... F*XME
|
||||
|
||||
var methods = AllExtensionMethods
|
||||
.Where(m => m.Name == name)
|
||||
.Where(m => m.GetParameters().Length == argsCount)
|
||||
.Where(m => MatchFirstParameter(thisType, m.GetParameters()[0].ParameterType));
|
||||
|
||||
// f*xme - is this what we should cache?
|
||||
return methods;
|
||||
//each time this is accessed it will be for 5 minutes longer
|
||||
isSliding:true);
|
||||
}
|
||||
|
||||
// find out whether the first parameter is a match for thisType
|
||||
private static bool MatchFirstParameter(Type thisType, Type firstParameterType)
|
||||
{
|
||||
return MethodArgZeroHasCorrectTargetType(null, firstParameterType, thisType);
|
||||
}
|
||||
|
||||
// get the single extension method for type thisType, with name name,
|
||||
// that accepts the arguments in args (which does not contain the instance of thisType).
|
||||
public static MethodInfo GetExtensionMethod(Type thisType, string name, object[] args)
|
||||
{
|
||||
MethodInfo result = null;
|
||||
foreach (var method in GetExtensionMethods(thisType, name, args.Length).Where(m => MatchParameters(m, args)))
|
||||
{
|
||||
if (result == null)
|
||||
result = method;
|
||||
else
|
||||
throw new AmbiguousMatchException("More than one matching extension method was found.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// find out whether the method can accept the arguments
|
||||
private static bool MatchParameters(MethodInfo method, IList<object> args)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
var i = 0;
|
||||
for (; i < parameters.Length; ++i)
|
||||
{
|
||||
if (MatchParameter(parameters[i].ParameterType, args[i].GetType()) == false)
|
||||
break;
|
||||
}
|
||||
return (i == parameters.Length);
|
||||
}
|
||||
|
||||
internal static bool MatchParameter(Type parameterType, Type argumentType)
|
||||
{
|
||||
// public static int DoSomething<T>(Foo foo, T t1, T t2)
|
||||
// DoSomething(foo, t1, t2) => how can we match?!
|
||||
return parameterType == argumentType; // f*xme of course!
|
||||
}
|
||||
*
|
||||
*/
|
||||
|
||||
// BELOW IS THE ORIGINAL CODE...
|
||||
|
||||
/// <summary>
|
||||
/// Returns all extension methods found matching the definition
|
||||
/// </summary>
|
||||
/// <param name="thisType"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="argumentCount"></param>
|
||||
/// <param name="argsContainsThis"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// TODO: NOTE: This will be an intensive method to call!! Results should be cached!
|
||||
/// </remarks>
|
||||
private static IEnumerable<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
|
||||
{
|
||||
// at *least* we can cache the extension methods discovery
|
||||
var candidates = AllExtensionMethods;
|
||||
|
||||
/*
|
||||
//only scan assemblies we know to contain extension methods (user assemblies)
|
||||
var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
|
||||
|
||||
//get extension methods from runtime
|
||||
var candidates = (
|
||||
from assembly in assembliesToScan
|
||||
where assembly.IsDefined(typeof(ExtensionAttribute), false)
|
||||
from type in assembly.GetTypes()
|
||||
where (type.IsDefined(typeof(ExtensionAttribute), false)
|
||||
&& type.IsSealed && !type.IsGenericType && !type.IsNested)
|
||||
from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
// this filters extension methods
|
||||
where method.IsDefined(typeof(ExtensionAttribute), false)
|
||||
select method
|
||||
);
|
||||
|
||||
//add the extension methods defined in IEnumerable
|
||||
candidates = candidates.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
|
||||
*/
|
||||
|
||||
//filter by name
|
||||
var methodsByName = candidates.Where(m => m.Name == name);
|
||||
|
||||
var isGenericAndRightParamCount = methodsByName.Where(m => m.GetParameters().Length == argumentCount + (argsContainsThis ? 0 : 1));
|
||||
|
||||
//find the right overload that can take genericParameterType
|
||||
//which will be either DynamicNodeList or List<DynamicNode> which is IEnumerable`
|
||||
|
||||
var withGenericParameterType = isGenericAndRightParamCount.Select(m => new { m, t = FirstParameterType(m) });
|
||||
|
||||
var methodsWhereArgZeroIsTargetType = (from method in withGenericParameterType
|
||||
where
|
||||
method.t != null && MethodArgZeroHasCorrectTargetType(method.m, method.t, thisType)
|
||||
select method);
|
||||
|
||||
return methodsWhereArgZeroIsTargetType.Select(mt => mt.m);
|
||||
}
|
||||
|
||||
private static bool MethodArgZeroHasCorrectTargetType(MethodInfo method, Type firstArgumentType, Type thisType)
|
||||
{
|
||||
//This is done with seperate method calls because you can't debug/watch lamdas - if you're trying to figure
|
||||
//out why the wrong method is returned, it helps to be able to see each boolean result
|
||||
|
||||
return
|
||||
|
||||
// is it defined on me?
|
||||
MethodArgZeroHasCorrectTargetTypeTypeMatchesExactly(method, firstArgumentType, thisType) ||
|
||||
|
||||
// or on any of my interfaces?
|
||||
MethodArgZeroHasCorrectTargetTypeAnInterfaceMatches(method, firstArgumentType, thisType) ||
|
||||
|
||||
// or on any of my base types?
|
||||
MethodArgZeroHasCorrectTargetTypeIsASubclassOf(method, firstArgumentType, thisType) ||
|
||||
|
||||
//share a common interface (e.g. IEnumerable)
|
||||
MethodArgZeroHasCorrectTargetTypeShareACommonInterface(method, firstArgumentType, thisType);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static bool MethodArgZeroHasCorrectTargetTypeShareACommonInterface(MethodInfo method, Type firstArgumentType, Type thisType)
|
||||
{
|
||||
Type[] interfaces = firstArgumentType.GetInterfaces();
|
||||
if (interfaces.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool result = interfaces.All(i => thisType.GetInterfaces().Contains(i));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool MethodArgZeroHasCorrectTargetTypeIsASubclassOf(MethodInfo method, Type firstArgumentType, Type thisType)
|
||||
{
|
||||
bool result = thisType.IsSubclassOf(firstArgumentType);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool MethodArgZeroHasCorrectTargetTypeAnInterfaceMatches(MethodInfo method, Type firstArgumentType, Type thisType)
|
||||
{
|
||||
bool result = thisType.GetInterfaces().Contains(firstArgumentType);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool MethodArgZeroHasCorrectTargetTypeTypeMatchesExactly(MethodInfo method, Type firstArgumentType, Type thisType)
|
||||
{
|
||||
bool result = (thisType == firstArgumentType);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Type FirstParameterType(MethodInfo m)
|
||||
{
|
||||
ParameterInfo[] p = m.GetParameters();
|
||||
if (p.Any())
|
||||
{
|
||||
return p.First().ParameterType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MethodInfo DetermineMethodFromParams(IEnumerable<MethodInfo> methods, Type genericType, IEnumerable<object> args)
|
||||
/// <summary>
|
||||
/// Returns all extension methods found matching the definition
|
||||
/// </summary>
|
||||
/// <param name="runtimeCache">
|
||||
/// The runtime cache is used to temporarily cache all extension methods found in the app domain so that
|
||||
/// while we search for individual extension methods, the process will be reasonably 'quick'. We then statically
|
||||
/// cache the MethodInfo's that we are looking for and then the runtime cache will expire and give back all that memory.
|
||||
/// </param>
|
||||
/// <param name="thisType"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="argumentCount">
|
||||
/// The arguments EXCLUDING the 'this' argument in an extension method
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// NOTE: This will be an intensive method to call! Results will be cached based on the key (args) of this method
|
||||
/// </remarks>
|
||||
internal static IEnumerable<MethodInfo> GetAllExtensionMethods(IRuntimeCacheProvider runtimeCache, Type thisType, string name, int argumentCount)
|
||||
{
|
||||
var key = new Tuple<Type, string, int>(thisType, name, argumentCount);
|
||||
|
||||
return MethodCache.GetOrAdd(key, tuple =>
|
||||
{
|
||||
var candidates = GetAllExtensionMethodsInAppDomain(runtimeCache);
|
||||
|
||||
// filter by name
|
||||
var filtr1 = candidates.Where(m => m.Name == name);
|
||||
|
||||
// filter by args count
|
||||
// ensure we add + 1 to the arg count because the 'this' arg is not included in the count above!
|
||||
var filtr2 = filtr1.Where(m => m.GetParameters().Length == argumentCount + 1);
|
||||
|
||||
// filter by first parameter type (target of the extension method)
|
||||
// ie find the right overload that can take genericParameterType
|
||||
// (which will be either DynamicNodeList or List<DynamicNode> which is IEnumerable)
|
||||
var filtr3 = filtr2.Select(x =>
|
||||
{
|
||||
var t = x.GetParameters()[0].ParameterType; // exists because of +1 above
|
||||
var bindings = new Dictionary<string, Type>();
|
||||
if (TypeHelper.MatchType(thisType, t, bindings) == false) return null;
|
||||
|
||||
// create the generic method if necessary
|
||||
if (x.ContainsGenericParameters == false) return x;
|
||||
var targs = t.GetGenericArguments().Select(y => bindings[y.Name]).ToArray();
|
||||
return x.MakeGenericMethod(targs);
|
||||
}).Where(x => x != null);
|
||||
|
||||
return filtr3.ToArray();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static MethodInfo DetermineMethodFromParams(IEnumerable<MethodInfo> methods, Type genericType, IEnumerable<object> args)
|
||||
{
|
||||
if (!methods.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
MethodInfo methodToExecute = null;
|
||||
if (methods.Count() > 1)
|
||||
{
|
||||
//Given the args, lets get the types and compare the type sequence to try and find the correct overload
|
||||
var argTypes = args.ToList().ConvertAll(o =>
|
||||
{
|
||||
var oe = (o as Expression);
|
||||
if (oe != null)
|
||||
{
|
||||
return oe.Type.FullName;
|
||||
}
|
||||
return o.GetType().FullName;
|
||||
});
|
||||
var methodsWithArgTypes = methods.Select(method => new { method, types = method.GetParameters().Select(pi => pi.ParameterType.FullName) });
|
||||
var firstMatchingOverload = methodsWithArgTypes.FirstOrDefault(m => m.types.SequenceEqual(argTypes));
|
||||
if (firstMatchingOverload != null)
|
||||
{
|
||||
methodToExecute = firstMatchingOverload.method;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodToExecute == null)
|
||||
{
|
||||
var firstMethod = methods.FirstOrDefault();
|
||||
// NH: this is to ensure that it's always the correct one being chosen when using the LINQ extension methods
|
||||
if (methods.Count() > 1)
|
||||
{
|
||||
var firstGenericMethod = methods.FirstOrDefault(x => x.IsGenericMethodDefinition);
|
||||
if (firstGenericMethod != null)
|
||||
{
|
||||
firstMethod = firstGenericMethod;
|
||||
}
|
||||
}
|
||||
//Given the args, lets get the types and compare the type sequence to try and find the correct overload
|
||||
var argTypes = args.Select(o =>
|
||||
{
|
||||
var oe = (o as Expression);
|
||||
return oe != null ? oe.Type : o.GetType();
|
||||
});
|
||||
|
||||
var methodsWithArgTypes = methods.Select(method => new
|
||||
{
|
||||
method,
|
||||
//skip the first arg because that is the extension method type ('this') that we are looking for
|
||||
types = method.GetParameters().Select(pi => pi.ParameterType).Skip(1)
|
||||
});
|
||||
|
||||
//This type comparer will check
|
||||
var typeComparer = new DelegateEqualityComparer<Type>(
|
||||
//Checks if the argument type passed in can be assigned from the parameter type in the method. For
|
||||
// example, if the argument type is HtmlHelper<MyModel> but the method parameter type is HtmlHelper then
|
||||
// it will match because the argument is assignable to that parameter type and will be able to execute
|
||||
TypeHelper.IsTypeAssignableFrom,
|
||||
//This will not ever execute but if it does we need to get the hash code of the string because the hash
|
||||
// code of a type is random
|
||||
type => type.FullName.GetHashCode());
|
||||
|
||||
var firstMatchingOverload = methodsWithArgTypes
|
||||
.FirstOrDefault(m => m.types.SequenceEqual(argTypes, typeComparer));
|
||||
|
||||
if (firstMatchingOverload != null)
|
||||
{
|
||||
methodToExecute = firstMatchingOverload.method;
|
||||
}
|
||||
|
||||
if (firstMethod != null)
|
||||
{
|
||||
if (firstMethod.IsGenericMethodDefinition)
|
||||
{
|
||||
if (genericType != null)
|
||||
{
|
||||
methodToExecute = firstMethod.MakeGenericMethod(genericType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
methodToExecute = firstMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
return methodToExecute;
|
||||
}
|
||||
|
||||
public static MethodInfo FindExtensionMethod(Type thisType, object[] args, string name, bool argsContainsThis)
|
||||
public static MethodInfo FindExtensionMethod(IRuntimeCacheProvider runtimeCache, Type thisType, object[] args, string name, bool argsContainsThis)
|
||||
{
|
||||
Type genericType = null;
|
||||
if (thisType.IsGenericType)
|
||||
{
|
||||
genericType = thisType.GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
|
||||
var methods = GetAllExtensionMethods(thisType, name, args.Length, argsContainsThis).ToArray();
|
||||
return DetermineMethodFromParams(methods, genericType, args);
|
||||
args = args
|
||||
//if the args contains 'this', remove the first one since that is 'this' and we don't want to use
|
||||
//that in the method searching
|
||||
.Skip(argsContainsThis ? 1 : 0)
|
||||
.ToArray();
|
||||
|
||||
var methods = GetAllExtensionMethods(runtimeCache, thisType, name, args.Length).ToArray();
|
||||
|
||||
return DetermineMethodFromParams(methods, genericType, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core
|
||||
/// <remarks>
|
||||
/// Taken from http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-5
|
||||
/// </remarks>
|
||||
internal static class Enum<T> where T : struct
|
||||
public static class Enum<T> where T : struct
|
||||
{
|
||||
private static readonly IEnumerable<T> All = Enum.GetValues(typeof(T)).Cast<T>();
|
||||
private static readonly Dictionary<string, T> InsensitiveNames = All.ToDictionary(k => Enum.GetName(typeof(T), k).ToLowerInvariant());
|
||||
|
||||
@@ -1,18 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
{
|
||||
public static class FileSystemExtensions
|
||||
{
|
||||
public static long GetSize(this IFileSystem fs, string path)
|
||||
{
|
||||
using (var s = fs.OpenFile(path))
|
||||
{
|
||||
var size = s.Length;
|
||||
s.Close();
|
||||
|
||||
return size;
|
||||
/// <summary>
|
||||
/// Attempts to open the file at <code>filePath</code> up to <code>maxRetries</code> times,
|
||||
/// with a thread sleep time of <code>sleepPerRetryInMilliseconds</code> between retries.
|
||||
/// </summary>
|
||||
public static FileStream OpenReadWithRetry(this FileInfo file, int maxRetries = 5, int sleepPerRetryInMilliseconds = 50)
|
||||
{
|
||||
var retries = maxRetries;
|
||||
|
||||
while (retries > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.OpenRead(file.FullName);
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
retries--;
|
||||
|
||||
if (retries == 0)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Thread.Sleep(sleepPerRetryInMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("Retries must be greater than zero");
|
||||
}
|
||||
|
||||
public static long GetSize(this IFileSystem fs, string path)
|
||||
{
|
||||
using (var file = fs.OpenFile(path))
|
||||
{
|
||||
using (var sr = new StreamReader(file))
|
||||
{
|
||||
var str = sr.ReadToEnd();
|
||||
return str.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +58,14 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
|
||||
public static string GetExtension(this IFileSystem fs, string path)
|
||||
{
|
||||
return Path.GetExtension(fs.GetFullPath(path));
|
||||
}
|
||||
{
|
||||
return Path.GetExtension(fs.GetFullPath(path));
|
||||
}
|
||||
|
||||
public static string GetFileName(this IFileSystem fs, string path)
|
||||
{
|
||||
return Path.GetFileName(fs.GetFullPath(path));
|
||||
}
|
||||
{
|
||||
return Path.GetFileName(fs.GetFullPath(path));
|
||||
}
|
||||
|
||||
//TODO: Currently this is the only way to do this
|
||||
internal static void CreateFolder(this IFileSystem fs, string folderPath)
|
||||
|
||||
@@ -154,12 +154,8 @@ namespace Umbraco.Core.IO
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
_size = ImageHelper.GetDimensions(fs);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Helper class for mandating values, for example on method parameters.
|
||||
/// </summary>
|
||||
internal static class Mandate
|
||||
public static class Mandate
|
||||
{
|
||||
/// <summary>
|
||||
/// Mandates that the specified parameter is not null.
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Manifest
|
||||
public IEnumerable<PackageManifest> GetManifests()
|
||||
{
|
||||
//get all Manifest.js files in the appropriate folders
|
||||
var manifestFileContents = GetAllManfifestFileContents(_pluginsDir);
|
||||
var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
|
||||
return CreateManifests(manifestFileContents.ToArray());
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
/// <param name="currDir"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<string> GetAllManfifestFileContents(DirectoryInfo currDir)
|
||||
private IEnumerable<string> GetAllManifestFileContents(DirectoryInfo currDir)
|
||||
{
|
||||
var depth = FolderDepth(_pluginsDir, currDir);
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Manifest
|
||||
var result = new List<string>();
|
||||
foreach (var d in dirs)
|
||||
{
|
||||
result.AddRange(GetAllManfifestFileContents(d));
|
||||
result.AddRange(GetAllManifestFileContents(d));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// An endian-aware converter for converting between base data types
|
||||
/// and an array of bytes.
|
||||
/// </summary>
|
||||
internal class BitConverterEx
|
||||
{
|
||||
#region Public Enums
|
||||
/// <summary>
|
||||
/// Represents the byte order.
|
||||
/// </summary>
|
||||
public enum ByteOrder
|
||||
{
|
||||
LittleEndian = 1,
|
||||
BigEndian = 2,
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Member Variables
|
||||
private ByteOrder mFrom, mTo;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public BitConverterEx(ByteOrder from, ByteOrder to)
|
||||
{
|
||||
mFrom = from;
|
||||
mTo = to;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Indicates the byte order in which data is stored in this platform.
|
||||
/// </summary>
|
||||
public static ByteOrder SystemByteOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return (BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Predefined Values
|
||||
/// <summary>
|
||||
/// Returns a bit converter that converts between little-endian and system byte-order.
|
||||
/// </summary>
|
||||
public static BitConverterEx LittleEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(ByteOrder.LittleEndian, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bit converter that converts between big-endian and system byte-order.
|
||||
/// </summary>
|
||||
public static BitConverterEx BigEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(ByteOrder.BigEndian, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a bit converter that does not do any byte-order conversion.
|
||||
/// </summary>
|
||||
public static BitConverterEx SystemEndian
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BitConverterEx(BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a Unicode character.
|
||||
/// </summary>
|
||||
public static char ToChar(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToChar(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static ushort ToUInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToUInt16(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static uint ToUInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToUInt32(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
public static ulong ToUInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToUInt64(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit signed integer.
|
||||
/// </summary>
|
||||
public static short ToInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 2, from, to);
|
||||
return BitConverter.ToInt16(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit signed integer.
|
||||
/// </summary>
|
||||
public static int ToInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToInt32(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit signed integer.
|
||||
/// </summary>
|
||||
public static long ToInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToInt64(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a single precision floating number.
|
||||
/// </summary>
|
||||
public static float ToSingle(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 4, from, to);
|
||||
return BitConverter.ToSingle(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a double precision floating number.
|
||||
/// </summary>
|
||||
public static double ToDouble(byte[] value, long startIndex, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = CheckData(value, startIndex, 8, from, to);
|
||||
return BitConverter.ToDouble(data, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ushort value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(uint value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ulong value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(short value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(int value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(long value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given single precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(float value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given double precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(double value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
data = CheckData(data, from, to);
|
||||
return data;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public char ToChar(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToChar(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit unsigned integer.
|
||||
/// </summary>
|
||||
public ushort ToUInt16(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt16(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit unsigned integer.
|
||||
/// </summary>
|
||||
public uint ToUInt32(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt32(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
public ulong ToUInt64(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToUInt64(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 16-bit signed integer.
|
||||
/// </summary>
|
||||
public short ToInt16(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt16(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 32-bit signed integer.
|
||||
/// </summary>
|
||||
public int ToInt32(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt32(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a 64-bit signed integer.
|
||||
/// </summary>
|
||||
public long ToInt64(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToInt64(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a single precision floating number.
|
||||
/// </summary>
|
||||
public float ToSingle(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToSingle(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of bytes to a double precision floating number.
|
||||
/// </summary>
|
||||
public double ToDouble(byte[] value, long startIndex)
|
||||
{
|
||||
return BitConverterEx.ToDouble(value, startIndex, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(ushort value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(uint value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit unsigned integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(ulong value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 16-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(short value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 32-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(int value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given 64-bit signed integer to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(long value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given single precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(float value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given double precision floating-point number to an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] GetBytes(double value)
|
||||
{
|
||||
return BitConverterEx.GetBytes(value, mFrom, mTo);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
/// <summary>
|
||||
/// Reverse the array of bytes as needed.
|
||||
/// </summary>
|
||||
private static byte[] CheckData(byte[] value, long startIndex, long length, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
byte[] data = new byte[length];
|
||||
Array.Copy(value, startIndex, data, 0, length);
|
||||
if (from != to)
|
||||
Array.Reverse(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse the array of bytes as needed.
|
||||
/// </summary>
|
||||
private static byte[] CheckData(byte[] value, ByteOrder from, ByteOrder to)
|
||||
{
|
||||
return CheckData(value, 0, value.Length, from, to);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts between exif data types and array of bytes.
|
||||
/// </summary>
|
||||
internal class ExifBitConverter : BitConverterEx
|
||||
{
|
||||
#region Constructors
|
||||
public ExifBitConverter(ByteOrder from, ByteOrder to)
|
||||
: base(from, to)
|
||||
{
|
||||
;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Returns an ASCII string converted from the given byte array.
|
||||
/// </summary>
|
||||
public static string ToAscii(byte[] data, bool endatfirstnull, Encoding encoding)
|
||||
{
|
||||
int len = data.Length;
|
||||
if (endatfirstnull)
|
||||
{
|
||||
len = Array.IndexOf(data, (byte)0);
|
||||
if (len == -1) len = data.Length;
|
||||
}
|
||||
return encoding.GetString(data, 0, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ASCII string converted from the given byte array.
|
||||
/// </summary>
|
||||
public static string ToAscii(byte[] data, Encoding encoding)
|
||||
{
|
||||
return ToAscii(data, true, encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string converted from the given byte array.
|
||||
/// from the numeric value of each byte.
|
||||
/// </summary>
|
||||
public static string ToString(byte[] data)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in data)
|
||||
sb.Append(b);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a DateTime object converted from the given byte array.
|
||||
/// </summary>
|
||||
public static DateTime ToDateTime(byte[] data, bool hastime)
|
||||
{
|
||||
string str = ToAscii(data, Encoding.ASCII);
|
||||
string[] parts = str.Split(new char[] { ':', ' ' });
|
||||
try
|
||||
{
|
||||
if (hastime && parts.Length == 6)
|
||||
{
|
||||
// yyyy:MM:dd HH:mm:ss
|
||||
// This is the expected format though some cameras
|
||||
// can use single digits. See Issue 21.
|
||||
return new DateTime(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]), int.Parse(parts[4]), int.Parse(parts[5]));
|
||||
}
|
||||
else if (!hastime && parts.Length == 3)
|
||||
{
|
||||
// yyyy:MM:dd
|
||||
return new DateTime(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a DateTime object converted from the given byte array.
|
||||
/// </summary>
|
||||
public static DateTime ToDateTime(byte[] data)
|
||||
{
|
||||
return ToDateTime(data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an unsigned rational number converted from the first
|
||||
/// eight bytes of the given byte array. The first four bytes are
|
||||
/// assumed to be the numerator and the next four bytes are the
|
||||
/// denumerator.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.UFraction32 ToURational(byte[] data, ByteOrder frombyteorder)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, 0, num, 0, 4);
|
||||
Array.Copy(data, 4, den, 0, 4);
|
||||
return new MathEx.UFraction32(ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToUInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a signed rational number converted from the first
|
||||
/// eight bytes of the given byte array. The first four bytes are
|
||||
/// assumed to be the numerator and the next four bytes are the
|
||||
/// denumerator.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.Fraction32 ToSRational(byte[] data, ByteOrder frombyteorder)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, 0, num, 0, 4);
|
||||
Array.Copy(data, 4, den, 0, 4);
|
||||
return new MathEx.Fraction32(ToInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 16-bit unsigned integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static ushort[] ToUShortArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
ushort[] numbers = new ushort[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[2];
|
||||
Array.Copy(data, i * 2, num, 0, 2);
|
||||
numbers[i] = ToUInt16(num, 0, frombyteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 32-bit unsigned integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static uint[] ToUIntArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
uint[] numbers = new uint[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
Array.Copy(data, i * 4, num, 0, 4);
|
||||
numbers[i] = ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of 32-bit signed integers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static int[] ToSIntArray(byte[] data, int count, ByteOrder byteorder)
|
||||
{
|
||||
int[] numbers = new int[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
Array.Copy(data, i * 4, num, 0, 4);
|
||||
numbers[i] = ToInt32(num, 0, byteorder, BitConverterEx.SystemByteOrder);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of unsigned rational numbers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.UFraction32[] ToURationalArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
MathEx.UFraction32[] numbers = new MathEx.UFraction32[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, i * 8, num, 0, 4);
|
||||
Array.Copy(data, i * 8 + 4, den, 0, 4);
|
||||
numbers[i].Set(ToUInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToUInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of signed rational numbers converted from
|
||||
/// the given byte array.
|
||||
/// Numbers are converted from the given byte-order to platform byte-order.
|
||||
/// </summary>
|
||||
public static MathEx.Fraction32[] ToSRationalArray(byte[] data, int count, ByteOrder frombyteorder)
|
||||
{
|
||||
MathEx.Fraction32[] numbers = new MathEx.Fraction32[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] num = new byte[4];
|
||||
byte[] den = new byte[4];
|
||||
Array.Copy(data, i * 8, num, 0, 4);
|
||||
Array.Copy(data, i * 8 + 4, den, 0, 4);
|
||||
numbers[i].Set(ToInt32(num, 0, frombyteorder, BitConverterEx.SystemByteOrder), ToInt32(den, 0, frombyteorder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given ascii string to an array of bytes optionally adding a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(string value, bool addnull, Encoding encoding)
|
||||
{
|
||||
if (addnull) value += '\0';
|
||||
return encoding.GetBytes(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given ascii string to an array of bytes without adding a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(string value, Encoding encoding)
|
||||
{
|
||||
return GetBytes(value, false, encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given datetime to an array of bytes with a null terminator.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(DateTime value, bool hastime)
|
||||
{
|
||||
string str = "";
|
||||
if (hastime)
|
||||
str = value.ToString("yyyy:MM:dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
|
||||
else
|
||||
str = value.ToString("yyyy:MM:dd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
return GetBytes(str, true, Encoding.ASCII);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given unsigned rational number to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.UFraction32 value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] num = GetBytes(value.Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value.Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] data = new byte[8];
|
||||
Array.Copy(num, 0, data, 0, 4);
|
||||
Array.Copy(den, 0, data, 4, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given signed rational number to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.Fraction32 value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] num = GetBytes(value.Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value.Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] data = new byte[8];
|
||||
Array.Copy(num, 0, data, 0, 4);
|
||||
Array.Copy(den, 0, data, 4, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 16-bit unsigned integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(ushort[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[2 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 2, 2);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 32-bit unsigned integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(uint[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[4 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of 32-bit signed integers to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(int[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[4 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i], BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of unsigned rationals to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.UFraction32[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[8 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i].Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value[i].Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 8, 4);
|
||||
Array.Copy(den, 0, data, i * 8 + 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array of signed rationals to an array of bytes.
|
||||
/// Numbers are converted from the platform byte-order to the given byte-order.
|
||||
/// </summary>
|
||||
public static byte[] GetBytes(MathEx.Fraction32[] value, ByteOrder tobyteorder)
|
||||
{
|
||||
byte[] data = new byte[8 * value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] num = GetBytes(value[i].Numerator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
byte[] den = GetBytes(value[i].Denominator, BitConverterEx.SystemByteOrder, tobyteorder);
|
||||
Array.Copy(num, 0, data, i * 8, 4);
|
||||
Array.Copy(den, 0, data, i * 8 + 4, 4);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
internal enum Compression : ushort
|
||||
{
|
||||
Uncompressed = 1,
|
||||
CCITT1D = 2,
|
||||
Group3Fax = 3,
|
||||
Group4Fax = 4,
|
||||
LZW = 5,
|
||||
JPEG = 6,
|
||||
PackBits = 32773,
|
||||
}
|
||||
|
||||
internal enum PhotometricInterpretation : ushort
|
||||
{
|
||||
WhiteIsZero = 0,
|
||||
BlackIsZero = 1,
|
||||
RGB = 2,
|
||||
RGBPalette = 3,
|
||||
TransparencyMask = 4,
|
||||
CMYK = 5,
|
||||
YCbCr = 6,
|
||||
CIELab = 8,
|
||||
}
|
||||
|
||||
internal enum Orientation : ushort
|
||||
{
|
||||
Normal = 1,
|
||||
MirroredVertically = 2,
|
||||
Rotated180 = 3,
|
||||
MirroredHorizontally = 4,
|
||||
RotatedLeftAndMirroredVertically = 5,
|
||||
RotatedRight = 6,
|
||||
RotatedLeft = 7,
|
||||
RotatedRightAndMirroredVertically = 8,
|
||||
}
|
||||
|
||||
internal enum PlanarConfiguration : ushort
|
||||
{
|
||||
ChunkyFormat = 1,
|
||||
PlanarFormat = 2,
|
||||
}
|
||||
|
||||
internal enum YCbCrPositioning : ushort
|
||||
{
|
||||
Centered = 1,
|
||||
CoSited = 2,
|
||||
}
|
||||
|
||||
internal enum ResolutionUnit : ushort
|
||||
{
|
||||
Inches = 2,
|
||||
Centimeters = 3,
|
||||
}
|
||||
|
||||
internal enum ColorSpace : ushort
|
||||
{
|
||||
sRGB = 1,
|
||||
Uncalibrated = 0xfff,
|
||||
}
|
||||
|
||||
internal enum ExposureProgram : ushort
|
||||
{
|
||||
NotDefined = 0,
|
||||
Manual = 1,
|
||||
Normal = 2,
|
||||
AperturePriority = 3,
|
||||
ShutterPriority = 4,
|
||||
/// <summary>
|
||||
/// Biased toward depth of field.
|
||||
/// </summary>
|
||||
Creative = 5,
|
||||
/// <summary>
|
||||
/// Biased toward fast shutter speed.
|
||||
/// </summary>
|
||||
Action = 6,
|
||||
/// <summary>
|
||||
/// For closeup photos with the background out of focus.
|
||||
/// </summary>
|
||||
Portrait = 7,
|
||||
/// <summary>
|
||||
/// For landscape photos with the background in focus.
|
||||
/// </summary>
|
||||
Landscape = 8,
|
||||
}
|
||||
|
||||
internal enum MeteringMode : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Average = 1,
|
||||
CenterWeightedAverage = 2,
|
||||
Spot = 3,
|
||||
MultiSpot = 4,
|
||||
Pattern = 5,
|
||||
Partial = 6,
|
||||
Other = 255,
|
||||
}
|
||||
|
||||
internal enum LightSource : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Daylight = 1,
|
||||
Fluorescent = 2,
|
||||
Tungsten = 3,
|
||||
Flash = 4,
|
||||
FineWeather = 9,
|
||||
CloudyWeather = 10,
|
||||
Shade = 11,
|
||||
/// <summary>
|
||||
/// D 5700 – 7100K
|
||||
/// </summary>
|
||||
DaylightFluorescent = 12,
|
||||
/// <summary>
|
||||
/// N 4600 – 5400K
|
||||
/// </summary>
|
||||
DayWhiteFluorescent = 13,
|
||||
/// <summary>
|
||||
/// W 3900 – 4500K
|
||||
/// </summary>
|
||||
CoolWhiteFluorescent = 14,
|
||||
/// <summary>
|
||||
/// WW 3200 – 3700K
|
||||
/// </summary>
|
||||
WhiteFluorescent = 15,
|
||||
StandardLightA = 17,
|
||||
StandardLightB = 18,
|
||||
StandardLightC = 19,
|
||||
D55 = 20,
|
||||
D65 = 21,
|
||||
D75 = 22,
|
||||
D50 = 23,
|
||||
ISOStudioTungsten = 24,
|
||||
OtherLightSource = 255,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum Flash : ushort
|
||||
{
|
||||
FlashDidNotFire = 0,
|
||||
StrobeReturnLightNotDetected = 4,
|
||||
StrobeReturnLightDetected = 2,
|
||||
FlashFired = 1,
|
||||
CompulsoryFlashMode = 8,
|
||||
AutoMode = 16,
|
||||
NoFlashFunction = 32,
|
||||
RedEyeReductionMode = 64,
|
||||
}
|
||||
|
||||
internal enum SensingMethod : ushort
|
||||
{
|
||||
NotDefined = 1,
|
||||
OneChipColorAreaSensor = 2,
|
||||
TwoChipColorAreaSensor = 3,
|
||||
ThreeChipColorAreaSensor = 4,
|
||||
ColorSequentialAreaSensor = 5,
|
||||
TriLinearSensor = 7,
|
||||
ColorSequentialLinearSensor = 8,
|
||||
}
|
||||
|
||||
internal enum FileSource : byte // UNDEFINED
|
||||
{
|
||||
DSC = 3,
|
||||
}
|
||||
|
||||
internal enum SceneType : byte // UNDEFINED
|
||||
{
|
||||
DirectlyPhotographedImage = 1,
|
||||
}
|
||||
|
||||
internal enum CustomRendered : ushort
|
||||
{
|
||||
NormalProcess = 0,
|
||||
CustomProcess = 1,
|
||||
}
|
||||
|
||||
internal enum ExposureMode : ushort
|
||||
{
|
||||
Auto = 0,
|
||||
Manual = 1,
|
||||
AutoBracket = 2,
|
||||
}
|
||||
|
||||
internal enum WhiteBalance : ushort
|
||||
{
|
||||
Auto = 0,
|
||||
Manual = 1,
|
||||
}
|
||||
|
||||
internal enum SceneCaptureType : ushort
|
||||
{
|
||||
Standard = 0,
|
||||
Landscape = 1,
|
||||
Portrait = 2,
|
||||
NightScene = 3,
|
||||
}
|
||||
|
||||
internal enum GainControl : ushort
|
||||
{
|
||||
None = 0,
|
||||
LowGainUp = 1,
|
||||
HighGainUp = 2,
|
||||
LowGainDown = 3,
|
||||
HighGainDown = 4,
|
||||
}
|
||||
|
||||
internal enum Contrast : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Soft = 1,
|
||||
Hard = 2,
|
||||
}
|
||||
|
||||
internal enum Saturation : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Low = 1,
|
||||
High = 2,
|
||||
}
|
||||
|
||||
internal enum Sharpness : ushort
|
||||
{
|
||||
Normal = 0,
|
||||
Soft = 1,
|
||||
Hard = 2,
|
||||
}
|
||||
|
||||
internal enum SubjectDistanceRange : ushort
|
||||
{
|
||||
Unknown = 0,
|
||||
Macro = 1,
|
||||
CloseView = 2,
|
||||
DistantView = 3,
|
||||
}
|
||||
|
||||
internal enum GPSLatitudeRef : byte // ASCII
|
||||
{
|
||||
North = 78, // 'N'
|
||||
South = 83, // 'S'
|
||||
}
|
||||
|
||||
internal enum GPSLongitudeRef : byte // ASCII
|
||||
{
|
||||
West = 87, // 'W'
|
||||
East = 69, // 'E'
|
||||
}
|
||||
|
||||
internal enum GPSAltitudeRef : byte
|
||||
{
|
||||
AboveSeaLevel = 0,
|
||||
BelowSeaLevel = 1,
|
||||
}
|
||||
|
||||
internal enum GPSStatus : byte // ASCII
|
||||
{
|
||||
MeasurementInProgress = 65, // 'A'
|
||||
MeasurementInteroperability = 86, // 'V'
|
||||
}
|
||||
|
||||
internal enum GPSMeasureMode : byte // ASCII
|
||||
{
|
||||
TwoDimensional = 50, // '2'
|
||||
ThreeDimensional = 51, // '3'
|
||||
}
|
||||
|
||||
internal enum GPSSpeedRef : byte // ASCII
|
||||
{
|
||||
KilometersPerHour = 75, // 'K'
|
||||
MilesPerHour = 77, // 'M'
|
||||
Knots = 78, // 'N'
|
||||
}
|
||||
|
||||
internal enum GPSDirectionRef : byte // ASCII
|
||||
{
|
||||
TrueDirection = 84, // 'T'
|
||||
MagneticDirection = 77, // 'M'
|
||||
}
|
||||
|
||||
internal enum GPSDistanceRef : byte // ASCII
|
||||
{
|
||||
Kilometers = 75, // 'K'
|
||||
Miles = 77, // 'M'
|
||||
Knots = 78, // 'N'
|
||||
}
|
||||
|
||||
internal enum GPSDifferential : ushort
|
||||
{
|
||||
MeasurementWithoutDifferentialCorrection = 0,
|
||||
DifferentialCorrectionApplied = 1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG/Exif file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidExifFileException : Exception
|
||||
{
|
||||
public NotValidExifFileException()
|
||||
: base("Not a valid JPEG/Exif file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidExifFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the IFD section ID could not be understood.
|
||||
/// </summary>
|
||||
internal class UnknownIFDSectionException : Exception
|
||||
{
|
||||
public UnknownIFDSectionException()
|
||||
: base("Unknown IFD section.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public UnknownIFDSectionException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when an invalid enum type is given to an
|
||||
/// ExifEnumProperty.
|
||||
/// </summary>
|
||||
internal class UnknownEnumTypeException : Exception
|
||||
{
|
||||
public UnknownEnumTypeException()
|
||||
: base("Unknown enum type.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public UnknownEnumTypeException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the 0th IFD section does not contain any fields.
|
||||
/// </summary>
|
||||
internal class IFD0IsEmptyException : Exception
|
||||
{
|
||||
public IFD0IsEmptyException()
|
||||
: base("0th IFD section cannot be empty.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public IFD0IsEmptyException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an enumerated value.
|
||||
/// </summary>
|
||||
internal class ExifEnumProperty<T> : ExifProperty
|
||||
{
|
||||
protected T mValue;
|
||||
protected bool mIsBitField;
|
||||
protected override object _Value { get { return Value; } set { Value = (T)value; } }
|
||||
public new T Value { get { return mValue; } set { mValue = value; } }
|
||||
public bool IsBitField { get { return mIsBitField; } }
|
||||
|
||||
static public implicit operator T(ExifEnumProperty<T> obj) { return (T)obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifEnumProperty(ExifTag tag, T value, bool isbitfield)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
mIsBitField = isbitfield;
|
||||
}
|
||||
|
||||
public ExifEnumProperty(ExifTag tag, T value)
|
||||
: this(tag, value, false)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort tagid = ExifTagFactory.GetTagID(mTag);
|
||||
|
||||
Type type = typeof(T);
|
||||
Type basetype = Enum.GetUnderlyingType(type);
|
||||
|
||||
if (type == typeof(FileSource) || type == typeof(SceneType))
|
||||
{
|
||||
// UNDEFINED
|
||||
return new ExifInterOperability(tagid, 7, 1, new byte[] { (byte)((object)mValue) });
|
||||
}
|
||||
else if (type == typeof(GPSLatitudeRef) || type == typeof(GPSLongitudeRef) ||
|
||||
type == typeof(GPSStatus) || type == typeof(GPSMeasureMode) ||
|
||||
type == typeof(GPSSpeedRef) || type == typeof(GPSDirectionRef) ||
|
||||
type == typeof(GPSDistanceRef))
|
||||
{
|
||||
// ASCII
|
||||
return new ExifInterOperability(tagid, 2, 2, new byte[] { (byte)((object)mValue), 0 });
|
||||
}
|
||||
else if (basetype == typeof(byte))
|
||||
{
|
||||
// BYTE
|
||||
return new ExifInterOperability(tagid, 1, 1, new byte[] { (byte)((object)mValue) });
|
||||
}
|
||||
else if (basetype == typeof(ushort))
|
||||
{
|
||||
// SHORT
|
||||
return new ExifInterOperability(tagid, 3, 1, ExifBitConverter.GetBytes((ushort)((object)mValue), BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
else
|
||||
throw new UnknownEnumTypeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: UNDEFINED) Used for the UserComment field.
|
||||
/// </summary>
|
||||
internal class ExifEncodedString : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
private Encoding mEncoding;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
public Encoding Encoding { get { return mEncoding; } set { mEncoding = value; } }
|
||||
|
||||
static public implicit operator string(ExifEncodedString obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public ExifEncodedString(ExifTag tag, string value, Encoding encoding)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
string enc = "";
|
||||
if (mEncoding == null)
|
||||
enc = "\0\0\0\0\0\0\0\0";
|
||||
else if (mEncoding.EncodingName == "US-ASCII")
|
||||
enc = "ASCII\0\0\0";
|
||||
else if (mEncoding.EncodingName == "Japanese (JIS 0208-1990 and 0212-1990)")
|
||||
enc = "JIS\0\0\0\0\0";
|
||||
else if (mEncoding.EncodingName == "Unicode")
|
||||
enc = "Unicode\0";
|
||||
else
|
||||
enc = "\0\0\0\0\0\0\0\0";
|
||||
|
||||
byte[] benc = Encoding.ASCII.GetBytes(enc);
|
||||
byte[] bstr = (mEncoding == null ? Encoding.ASCII.GetBytes(mValue) : mEncoding.GetBytes(mValue));
|
||||
byte[] data = new byte[benc.Length + bstr.Length];
|
||||
Array.Copy(benc, 0, data, 0, benc.Length);
|
||||
Array.Copy(bstr, 0, data, benc.Length, bstr.Length);
|
||||
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)data.Length, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string formatted as DateTime. (EXIF Specification: ASCII) Used for the date time fields.
|
||||
/// </summary>
|
||||
internal class ExifDateTime : ExifProperty
|
||||
{
|
||||
protected DateTime mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (DateTime)value; } }
|
||||
public new DateTime Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator DateTime(ExifDateTime obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString("yyyy.MM.dd HH:mm:ss"); }
|
||||
|
||||
public ExifDateTime(ExifTag tag, DateTime value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 2, (uint)20, ExifBitConverter.GetBytes(mValue, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the exif version as a 4 byte ASCII string. (EXIF Specification: UNDEFINED)
|
||||
/// Used for the ExifVersion, FlashpixVersion, InteroperabilityVersion and GPSVersionID fields.
|
||||
/// </summary>
|
||||
internal class ExifVersion : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value.Substring(0, 4); } }
|
||||
|
||||
public ExifVersion(ExifTag tag, string value)
|
||||
: base(tag)
|
||||
{
|
||||
if (value.Length > 4)
|
||||
mValue = value.Substring(0, 4);
|
||||
else if (value.Length < 4)
|
||||
mValue = value + new string(' ', 4 - value.Length);
|
||||
else
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mTag == ExifTag.ExifVersion || mTag == ExifTag.FlashpixVersion || mTag == ExifTag.InteroperabilityVersion)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, Encoding.ASCII.GetBytes(mValue));
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
data[i] = byte.Parse(mValue[0].ToString());
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 2xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifPointSubjectArea : ExifUShortArray
|
||||
{
|
||||
protected new ushort[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public ushort X { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public ushort Y { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d})", mValue[0], mValue[1]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifPointSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y)
|
||||
: base(tag, new ushort[] { x, y })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 3xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifCircularSubjectArea : ExifPointSubjectArea
|
||||
{
|
||||
public ushort Diamater { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d}) {2:d}", mValue[0], mValue[1], mValue[2]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifCircularSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d)
|
||||
: base(tag, new ushort[] { x, y, d })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the location and area of the subject (EXIF Specification: 4xSHORT)
|
||||
/// The coordinate values, width, and height are expressed in relation to the
|
||||
/// upper left as origin, prior to rotation processing as per the Rotation tag.
|
||||
/// </summary>
|
||||
internal class ExifRectangularSubjectArea : ExifPointSubjectArea
|
||||
{
|
||||
public ushort Width { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
public ushort Height { get { return mValue[3]; } set { mValue[3] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("({0:d}, {1:d}) ({2:d} x {3:d})", mValue[0], mValue[1], mValue[2], mValue[3]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifRectangularSubjectArea(ExifTag tag, ushort[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h)
|
||||
: base(tag, new ushort[] { x, y, w, h })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents GPS latitudes and longitudes (EXIF Specification: 3xRATIONAL)
|
||||
/// </summary>
|
||||
internal class GPSLatitudeLongitude : ExifURationalArray
|
||||
{
|
||||
protected new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public MathEx.UFraction32 Degrees { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public MathEx.UFraction32 Minutes { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
public MathEx.UFraction32 Seconds { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public static explicit operator float(GPSLatitudeLongitude obj) { return obj.ToFloat(); }
|
||||
public float ToFloat()
|
||||
{
|
||||
return (float)Degrees + ((float)Minutes) / 60.0f + ((float)Seconds) / 3600.0f;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:F2}°{1:F2}'{2:F2}\"", (float)Degrees, (float)Minutes, (float)Seconds);
|
||||
}
|
||||
|
||||
public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s)
|
||||
: base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a GPS time stamp as UTC (EXIF Specification: 3xRATIONAL)
|
||||
/// </summary>
|
||||
internal class GPSTimeStamp : ExifURationalArray
|
||||
{
|
||||
protected new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
public MathEx.UFraction32 Hour { get { return mValue[0]; } set { mValue[0] = value; } }
|
||||
public MathEx.UFraction32 Minute { get { return mValue[1]; } set { mValue[1] = value; } }
|
||||
public MathEx.UFraction32 Second { get { return mValue[2]; } set { mValue[2] = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:F2}:{1:F2}:{2:F2}\"", (float)Hour, (float)Minute, (float)Second);
|
||||
}
|
||||
|
||||
public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public GPSTimeStamp(ExifTag tag, float h, float m, float s)
|
||||
: base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: BYTE)
|
||||
/// Used by Windows XP.
|
||||
/// </summary>
|
||||
internal class WindowsByteString : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator string(WindowsByteString obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public WindowsByteString(ExifTag tag, string value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
byte[] data = Encoding.Unicode.GetBytes(mValue);
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a custom type descriptor for an ExifFile instance.
|
||||
/// </summary>
|
||||
internal sealed class ExifFileTypeDescriptionProvider : TypeDescriptionProvider
|
||||
{
|
||||
public ExifFileTypeDescriptionProvider()
|
||||
: this(TypeDescriptor.GetProvider(typeof(ImageFile)))
|
||||
{
|
||||
}
|
||||
|
||||
public ExifFileTypeDescriptionProvider(TypeDescriptionProvider parent)
|
||||
: base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a custom type descriptor for the given type and object.
|
||||
/// </summary>
|
||||
/// <param name="objectType">The type of object for which to retrieve the type descriptor.</param>
|
||||
/// <param name="instance">An instance of the type. Can be null if no instance was passed to the <see cref="T:System.ComponentModel.TypeDescriptor"/>.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.ComponentModel.ICustomTypeDescriptor"/> that can provide metadata for the type.
|
||||
/// </returns>
|
||||
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
|
||||
{
|
||||
return new ExifFileTypeDescriptor(base.GetTypeDescriptor(objectType, instance), instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands ExifProperty objects contained in an ExifFile as seperate properties.
|
||||
/// </summary>
|
||||
internal sealed class ExifFileTypeDescriptor : CustomTypeDescriptor
|
||||
{
|
||||
ImageFile owner;
|
||||
|
||||
public ExifFileTypeDescriptor(ICustomTypeDescriptor parent, object instance)
|
||||
: base(parent)
|
||||
{
|
||||
owner = (ImageFile)instance;
|
||||
}
|
||||
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
|
||||
{
|
||||
return GetProperties();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a collection of property descriptors for the object represented by this type descriptor.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>.
|
||||
/// </returns>
|
||||
public override PropertyDescriptorCollection GetProperties()
|
||||
{
|
||||
// Enumerate the original set of properties and create our new set with it
|
||||
List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
|
||||
|
||||
foreach (ExifProperty prop in owner.Properties)
|
||||
{
|
||||
ExifPropertyDescriptor pd = new ExifPropertyDescriptor(prop);
|
||||
properties.Add(pd);
|
||||
}
|
||||
|
||||
// Finally return the list
|
||||
return new PropertyDescriptorCollection(properties.ToArray(), true);
|
||||
}
|
||||
}
|
||||
internal sealed class ExifPropertyDescriptor : PropertyDescriptor
|
||||
{
|
||||
object originalValue;
|
||||
ExifProperty linkedProperty;
|
||||
|
||||
public ExifPropertyDescriptor(ExifProperty property)
|
||||
: base(property.Name, new Attribute[] { new BrowsableAttribute(true) })
|
||||
{
|
||||
linkedProperty = property;
|
||||
originalValue = property.Value;
|
||||
}
|
||||
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Type ComponentType
|
||||
{
|
||||
get { return typeof(JPEGFile); }
|
||||
}
|
||||
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
return linkedProperty.Value;
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override Type PropertyType
|
||||
{
|
||||
get { return linkedProperty.Value.GetType(); }
|
||||
}
|
||||
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
linkedProperty.Value = originalValue;
|
||||
}
|
||||
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
linkedProperty.Value = value;
|
||||
}
|
||||
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents interoperability data for an exif tag in the platform byte order.
|
||||
/// </summary>
|
||||
internal struct ExifInterOperability
|
||||
{
|
||||
private ushort mTagID;
|
||||
private ushort mTypeID;
|
||||
private uint mCount;
|
||||
private byte[] mData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag ID defined in the Exif standard.
|
||||
/// </summary>
|
||||
public ushort TagID { get { return mTagID; } }
|
||||
/// <summary>
|
||||
/// Gets the type code defined in the Exif standard.
|
||||
/// <list type="bullet">
|
||||
/// <item>1 = BYTE (byte)</item>
|
||||
/// <item>2 = ASCII (byte array)</item>
|
||||
/// <item>3 = SHORT (ushort)</item>
|
||||
/// <item>4 = LONG (uint)</item>
|
||||
/// <item>5 = RATIONAL (2 x uint: numerator, denominator)</item>
|
||||
/// <item>6 = BYTE (sbyte)</item>
|
||||
/// <item>7 = UNDEFINED (byte array)</item>
|
||||
/// <item>8 = SSHORT (short)</item>
|
||||
/// <item>9 = SLONG (int)</item>
|
||||
/// <item>10 = SRATIONAL (2 x int: numerator, denominator)</item>
|
||||
/// <item>11 = FLOAT (float)</item>
|
||||
/// <item>12 = DOUBLE (double)</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public ushort TypeID { get { return mTypeID; } }
|
||||
/// <summary>
|
||||
/// Gets the byte count or number of components.
|
||||
/// </summary>
|
||||
public uint Count { get { return mCount; } }
|
||||
/// <summary>
|
||||
/// Gets the field value as an array of bytes.
|
||||
/// </summary>
|
||||
public byte[] Data { get { return mData; } }
|
||||
/// <summary>
|
||||
/// Returns the string representation of this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Tag: {0}, Type: {1}, Count: {2}, Data Length: {3}", mTagID, mTypeID, mCount, mData.Length);
|
||||
}
|
||||
|
||||
public ExifInterOperability(ushort tagid, ushort typeid, uint count, byte[] data)
|
||||
{
|
||||
mTagID = tagid;
|
||||
mTypeID = typeid;
|
||||
mCount = count;
|
||||
mData = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the abstract base class for an Exif property.
|
||||
/// </summary>
|
||||
internal abstract class ExifProperty
|
||||
{
|
||||
protected ExifTag mTag;
|
||||
protected IFD mIFD;
|
||||
protected string mName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Exif tag associated with this property.
|
||||
/// </summary>
|
||||
public ExifTag Tag { get { return mTag; } }
|
||||
/// <summary>
|
||||
/// Gets the IFD section contaning this property.
|
||||
/// </summary>
|
||||
public IFD IFD { get { return mIFD; } }
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this property.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mName == null || mName.Length == 0)
|
||||
return ExifTagFactory.GetTagName(mTag);
|
||||
else
|
||||
return mName;
|
||||
}
|
||||
set
|
||||
{
|
||||
mName = value;
|
||||
}
|
||||
}
|
||||
protected abstract object _Value { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value of this property.
|
||||
/// </summary>
|
||||
public object Value { get { return _Value; } set { _Value = value; } }
|
||||
/// <summary>
|
||||
/// Gets interoperability data for this property.
|
||||
/// </summary>
|
||||
public abstract ExifInterOperability Interoperability { get; }
|
||||
|
||||
public ExifProperty(ExifTag tag)
|
||||
{
|
||||
mTag = tag;
|
||||
mIFD = ExifTagFactory.GetTagIFD(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an 8-bit unsigned integer. (EXIF Specification: BYTE)
|
||||
/// </summary>
|
||||
internal class ExifByte : ExifProperty
|
||||
{
|
||||
protected byte mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToByte(value); } }
|
||||
public new byte Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte(ExifByte obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifByte(ExifTag tag, byte value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, 1, new byte[] { mValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 8-bit unsigned integers. (EXIF Specification: BYTE with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifByteArray : ExifProperty
|
||||
{
|
||||
protected byte[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (byte[])value; } }
|
||||
public new byte[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte[](ExifByteArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (byte b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifByteArray(ExifTag tag, byte[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.Length, mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ASCII string. (EXIF Specification: ASCII)
|
||||
/// </summary>
|
||||
internal class ExifAscii : ExifProperty
|
||||
{
|
||||
protected string mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (string)value; } }
|
||||
public new string Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public Encoding Encoding { get; private set; }
|
||||
|
||||
static public implicit operator string(ExifAscii obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue; }
|
||||
|
||||
public ExifAscii(ExifTag tag, string value, Encoding encoding)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
Encoding = encoding;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 2, (uint)mValue.Length + 1, ExifBitConverter.GetBytes(mValue, true, Encoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 16-bit unsigned integer. (EXIF Specification: SHORT)
|
||||
/// </summary>
|
||||
internal class ExifUShort : ExifProperty
|
||||
{
|
||||
protected ushort mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToUInt16(value); } }
|
||||
public new ushort Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator ushort(ExifUShort obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifUShort(ExifTag tag, ushort value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 16-bit unsigned integers.
|
||||
/// (EXIF Specification: SHORT with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifUShortArray : ExifProperty
|
||||
{
|
||||
protected ushort[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (ushort[])value; } }
|
||||
public new ushort[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator ushort[](ExifUShortArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (ushort b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUShortArray(ExifTag tag, ushort[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 32-bit unsigned integer. (EXIF Specification: LONG)
|
||||
/// </summary>
|
||||
internal class ExifUInt : ExifProperty
|
||||
{
|
||||
protected uint mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToUInt32(value); } }
|
||||
public new uint Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator uint(ExifUInt obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
public ExifUInt(ExifTag tag, uint value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 4, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 16-bit unsigned integers.
|
||||
/// (EXIF Specification: LONG with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifUIntArray : ExifProperty
|
||||
{
|
||||
protected uint[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (uint[])value; } }
|
||||
public new uint[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator uint[](ExifUIntArray obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (uint b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUIntArray(ExifTag tag, uint[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rational number defined with a 32-bit unsigned numerator
|
||||
/// and denominator. (EXIF Specification: RATIONAL)
|
||||
/// </summary>
|
||||
internal class ExifURational : ExifProperty
|
||||
{
|
||||
protected MathEx.UFraction32 mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.UFraction32)value; } }
|
||||
public new MathEx.UFraction32 Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
public float ToFloat() { return (float)mValue; }
|
||||
|
||||
static public explicit operator float(ExifURational obj) { return (float)obj.mValue; }
|
||||
|
||||
public uint[] ToArray()
|
||||
{
|
||||
return new uint[] { mValue.Numerator, mValue.Denominator };
|
||||
}
|
||||
|
||||
public ExifURational(ExifTag tag, uint numerator, uint denominator)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = new MathEx.UFraction32(numerator, denominator);
|
||||
}
|
||||
|
||||
public ExifURational(ExifTag tag, MathEx.UFraction32 value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 5, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of unsigned rational numbers.
|
||||
/// (EXIF Specification: RATIONAL with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifURationalArray : ExifProperty
|
||||
{
|
||||
protected MathEx.UFraction32[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.UFraction32[])value; } }
|
||||
public new MathEx.UFraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public explicit operator float[](ExifURationalArray obj)
|
||||
{
|
||||
float[] result = new float[obj.mValue.Length];
|
||||
for (int i = 0; i < obj.mValue.Length; i++)
|
||||
result[i] = (float)obj.mValue[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (MathEx.UFraction32 b in mValue)
|
||||
{
|
||||
sb.Append(b.ToString());
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifURationalArray(ExifTag tag, MathEx.UFraction32[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 5, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a byte array that can take any value. (EXIF Specification: UNDEFINED)
|
||||
/// </summary>
|
||||
internal class ExifUndefined : ExifProperty
|
||||
{
|
||||
protected byte[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (byte[])value; } }
|
||||
public new byte[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public implicit operator byte[](ExifUndefined obj) { return obj.mValue; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (byte b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifUndefined(ExifTag tag, byte[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)mValue.Length, mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 32-bit signed integer. (EXIF Specification: SLONG)
|
||||
/// </summary>
|
||||
internal class ExifSInt : ExifProperty
|
||||
{
|
||||
protected int mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = Convert.ToInt32(value); } }
|
||||
public new int Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
|
||||
static public implicit operator int(ExifSInt obj) { return obj.mValue; }
|
||||
|
||||
public ExifSInt(ExifTag tag, int value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 9, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of 32-bit signed integers.
|
||||
/// (EXIF Specification: SLONG with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifSIntArray : ExifProperty
|
||||
{
|
||||
protected int[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (int[])value; } }
|
||||
public new int[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (int b in mValue)
|
||||
{
|
||||
sb.Append(b);
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static public implicit operator int[](ExifSIntArray obj) { return obj.mValue; }
|
||||
|
||||
public ExifSIntArray(ExifTag tag, int[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 9, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rational number defined with a 32-bit signed numerator
|
||||
/// and denominator. (EXIF Specification: SRATIONAL)
|
||||
/// </summary>
|
||||
internal class ExifSRational : ExifProperty
|
||||
{
|
||||
protected MathEx.Fraction32 mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.Fraction32)value; } }
|
||||
public new MathEx.Fraction32 Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.ToString(); }
|
||||
public float ToFloat() { return (float)mValue; }
|
||||
|
||||
static public explicit operator float(ExifSRational obj) { return (float)obj.mValue; }
|
||||
|
||||
public int[] ToArray()
|
||||
{
|
||||
return new int[] { mValue.Numerator, mValue.Denominator };
|
||||
}
|
||||
|
||||
public ExifSRational(ExifTag tag, int numerator, int denominator)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = new MathEx.Fraction32(numerator, denominator);
|
||||
}
|
||||
|
||||
public ExifSRational(ExifTag tag, MathEx.Fraction32 value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 10, 1, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array of signed rational numbers.
|
||||
/// (EXIF Specification: SRATIONAL with count > 1)
|
||||
/// </summary>
|
||||
internal class ExifSRationalArray : ExifProperty
|
||||
{
|
||||
protected MathEx.Fraction32[] mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (MathEx.Fraction32[])value; } }
|
||||
public new MathEx.Fraction32[] Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
static public explicit operator float[](ExifSRationalArray obj)
|
||||
{
|
||||
float[] result = new float[obj.mValue.Length];
|
||||
for (int i = 0; i < obj.mValue.Length; i++)
|
||||
result[i] = (float)obj.mValue[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
foreach (MathEx.Fraction32 b in mValue)
|
||||
{
|
||||
sb.Append(b.ToString());
|
||||
sb.Append(' ');
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public ExifSRationalArray(ExifTag tag, MathEx.Fraction32[] value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 10, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection of <see cref="ExifProperty"/> objects.
|
||||
/// </summary>
|
||||
internal class ExifPropertyCollection : IDictionary<ExifTag, ExifProperty>
|
||||
{
|
||||
#region Member Variables
|
||||
private ImageFile parent;
|
||||
private Dictionary<ExifTag, ExifProperty> items;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
internal ExifPropertyCollection (ImageFile parentFile)
|
||||
{
|
||||
parent = parentFile;
|
||||
items = new Dictionary<ExifTag, ExifProperty> ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the number of elements contained in the collection.
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return items.Count; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a collection containing the keys in this collection.
|
||||
/// </summary>
|
||||
public ICollection<ExifTag> Keys {
|
||||
get { return items.Keys; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a collection containing the values in this collection.
|
||||
/// </summary>
|
||||
public ICollection<ExifProperty> Values {
|
||||
get { return items.Values; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
public ExifProperty this[ExifTag key] {
|
||||
get { return items[key]; }
|
||||
set {
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, value);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ExifProperty Collection Setters
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, byte value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifByte (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, string value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
items.Add (key, new WindowsByteString (key, value));
|
||||
} else {
|
||||
items.Add (key, new ExifAscii (key, value, parent.Encoding));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, ushort value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifUShort (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, int value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifSInt (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, uint value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifUInt (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, float value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURational (key, new MathEx.UFraction32 (value)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, double value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURational (key, new MathEx.UFraction32 (value)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, object value)
|
||||
{
|
||||
Type type = value.GetType ();
|
||||
if (type.IsEnum) {
|
||||
Type etype = typeof(ExifEnumProperty<>).MakeGenericType (new Type[] { type });
|
||||
object prop = Activator.CreateInstance (etype, new object[] { key, value });
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, (ExifProperty)prop);
|
||||
} else
|
||||
throw new ArgumentException ("No exif property exists for this tag.", "value");
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
/// <param name="encoding">String encoding.</param>
|
||||
public void Set (ExifTag key, string value, Encoding encoding)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifEncodedString (key, value, encoding));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="value">The value of tag.</param>
|
||||
public void Set (ExifTag key, DateTime value)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifDateTime (key, value));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExifProperty"/> with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The tag to set.</param>
|
||||
/// <param name="d">Angular degrees (or clock hours for a timestamp).</param>
|
||||
/// <param name="m">Angular minutes (or clock minutes for a timestamp).</param>
|
||||
/// <param name="s">Angular seconds (or clock seconds for a timestamp).</param>
|
||||
public void Set (ExifTag key, float d, float m, float s)
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
items.Add (key, new ExifURationalArray (key, new MathEx.UFraction32[] { new MathEx.UFraction32 (d), new MathEx.UFraction32 (m), new MathEx.UFraction32 (s) }));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The <see cref="ExifProperty"/> to add to the collection.</param>
|
||||
public void Add (ExifProperty item)
|
||||
{
|
||||
ExifProperty oldItem = null;
|
||||
if (items.TryGetValue (item.Tag, out oldItem))
|
||||
items[item.Tag] = item;
|
||||
else
|
||||
items.Add (item.Tag, item);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all items from the collection.
|
||||
/// </summary>
|
||||
public void Clear ()
|
||||
{
|
||||
items.Clear ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the collection contains an element with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to locate in the collection.</param>
|
||||
/// <returns>
|
||||
/// true if the collection contains an element with the key; otherwise, false.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool ContainsKey (ExifTag key)
|
||||
{
|
||||
return items.ContainsKey (key);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the element with the specified key from the collection.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to remove.</param>
|
||||
/// <returns>
|
||||
/// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key"/> was not found in the original collection.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool Remove (ExifTag key)
|
||||
{
|
||||
return items.Remove (key);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all items with the given IFD from the collection.
|
||||
/// </summary>
|
||||
/// <param name="ifd">The IFD section to remove.</param>
|
||||
public void Remove (IFD ifd)
|
||||
{
|
||||
List<ExifTag> toRemove = new List<ExifTag> ();
|
||||
foreach (KeyValuePair<ExifTag, ExifProperty> item in items) {
|
||||
if (item.Value.IFD == ifd)
|
||||
toRemove.Add (item.Key);
|
||||
}
|
||||
foreach (ExifTag tag in toRemove)
|
||||
items.Remove (tag);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key whose value to get.</param>
|
||||
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
|
||||
/// <returns>
|
||||
/// true if the collection contains an element with the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
public bool TryGetValue (ExifTag key, out ExifProperty value)
|
||||
{
|
||||
return items.TryGetValue (key, out value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through a collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
public IEnumerator<ExifProperty> GetEnumerator ()
|
||||
{
|
||||
return Values.GetEnumerator ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hidden Interface
|
||||
/// <summary>
|
||||
/// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The object to use as the key of the element to add.</param>
|
||||
/// <param name="value">The object to use as the value of the element to add.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="key"/> is null.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</exception>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
|
||||
void IDictionary<ExifTag, ExifProperty>.Add (ExifTag key, ExifProperty value)
|
||||
{
|
||||
Add (value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
void ICollection<KeyValuePair<ExifTag, ExifProperty>>.Add (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
Add (item.Value);
|
||||
}
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.Contains (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
|
||||
/// </summary>
|
||||
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
|
||||
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="array"/> is null.</exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="arrayIndex"/> is less than 0.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">
|
||||
/// <paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
|
||||
void ICollection<KeyValuePair<ExifTag, ExifProperty>>.CopyTo (KeyValuePair<ExifTag, ExifProperty>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException ("array");
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException ("arrayIndex");
|
||||
if (array.Rank > 1)
|
||||
throw new ArgumentException ("Destination array is multidimensional.", "array");
|
||||
if (arrayIndex >= array.Length)
|
||||
throw new ArgumentException ("arrayIndex is equal to or greater than the length of destination array", "array");
|
||||
if (arrayIndex + items.Count > array.Length)
|
||||
throw new ArgumentException ("There is not enough space in destination array.", "array");
|
||||
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<ExifTag, ExifProperty> item in items) {
|
||||
if (i >= arrayIndex) {
|
||||
array[i] = item;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
|
||||
/// </summary>
|
||||
/// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.</returns>
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.IsReadOnly {
|
||||
get { return false; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
|
||||
/// <returns>
|
||||
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
|
||||
bool ICollection<KeyValuePair<ExifTag, ExifProperty>>.Remove (KeyValuePair<ExifTag, ExifProperty> item)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through a collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return GetEnumerator ();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
IEnumerator<KeyValuePair<ExifTag, ExifProperty>> IEnumerable<KeyValuePair<ExifTag, ExifProperty>>.GetEnumerator ()
|
||||
{
|
||||
return items.GetEnumerator ();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates exif properties from interoperability parameters.
|
||||
/// </summary>
|
||||
internal static class ExifPropertyFactory
|
||||
{
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Creates an ExifProperty from the given interoperability parameters.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag id of the exif property.</param>
|
||||
/// <param name="type">The type id of the exif property.</param>
|
||||
/// <param name="count">Byte or component count.</param>
|
||||
/// <param name="value">Field data as an array of bytes.</param>
|
||||
/// <param name="byteOrder">Byte order of value.</param>
|
||||
/// <param name="ifd">IFD section containing this propery.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
|
||||
public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
|
||||
{
|
||||
BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
|
||||
// Find the exif tag corresponding to given tag id
|
||||
ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);
|
||||
|
||||
if (ifd == IFD.Zeroth)
|
||||
{
|
||||
if (tag == 0x103) // Compression
|
||||
return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x106) // PhotometricInterpretation
|
||||
return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x112) // Orientation
|
||||
return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x11c) // PlanarConfiguration
|
||||
return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x213) // YCbCrPositioning
|
||||
return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x128) // ResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x132) // DateTime
|
||||
return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x9c9b || tag == 0x9c9c || // Windows tags
|
||||
tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
|
||||
return new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0'));
|
||||
}
|
||||
else if (ifd == IFD.EXIF)
|
||||
{
|
||||
if (tag == 0x9000) // ExifVersion
|
||||
return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
else if (tag == 0xa000) // FlashpixVersion
|
||||
return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
else if (tag == 0xa001) // ColorSpace
|
||||
return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9286) // UserComment
|
||||
{
|
||||
// Default to ASCII
|
||||
Encoding enc = Encoding.ASCII;
|
||||
bool hasenc;
|
||||
if (value.Length < 8)
|
||||
hasenc = false;
|
||||
else
|
||||
{
|
||||
hasenc = true;
|
||||
string encstr = enc.GetString(value, 0, 8);
|
||||
if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.ASCII;
|
||||
else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
|
||||
else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
enc = Encoding.Unicode;
|
||||
else
|
||||
hasenc = false;
|
||||
}
|
||||
|
||||
string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');
|
||||
|
||||
return new ExifEncodedString(ExifTag.UserComment, val, enc);
|
||||
}
|
||||
else if (tag == 0x9003) // DateTimeOriginal
|
||||
return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x9004) // DateTimeDigitized
|
||||
return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
|
||||
else if (tag == 0x8822) // ExposureProgram
|
||||
return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9207) // MeteringMode
|
||||
return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9208) // LightSource
|
||||
return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x9209) // Flash
|
||||
return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0x9214) // SubjectArea
|
||||
{
|
||||
if (count == 3)
|
||||
return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else if (count == 4)
|
||||
return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else // count == 2
|
||||
return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (tag == 0xa210) // FocalPlaneResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa214) // SubjectLocation
|
||||
return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
else if (tag == 0xa217) // SensingMethod
|
||||
return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa300) // FileSource
|
||||
return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa301) // SceneType
|
||||
return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa401) // CustomRendered
|
||||
return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa402) // ExposureMode
|
||||
return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa403) // WhiteBalance
|
||||
return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa406) // SceneCaptureType
|
||||
return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa407) // GainControl
|
||||
return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa408) // Contrast
|
||||
return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa409) // Saturation
|
||||
return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa40a) // Sharpness
|
||||
return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
|
||||
else if (tag == 0xa40c) // SubjectDistanceRange
|
||||
return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
|
||||
}
|
||||
else if (ifd == IFD.GPS)
|
||||
{
|
||||
if (tag == 0) // GPSVersionID
|
||||
return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
|
||||
else if (tag == 1) // GPSLatitudeRef
|
||||
return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
|
||||
else if (tag == 2) // GPSLatitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 3) // GPSLongitudeRef
|
||||
return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
|
||||
else if (tag == 4) // GPSLongitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 5) // GPSAltitudeRef
|
||||
return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
|
||||
else if (tag == 7) // GPSTimeStamp
|
||||
return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 9) // GPSStatus
|
||||
return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
|
||||
else if (tag == 10) // GPSMeasureMode
|
||||
return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
|
||||
else if (tag == 12) // GPSSpeedRef
|
||||
return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
|
||||
else if (tag == 14) // GPSTrackRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 16) // GPSImgDirectionRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 19) // GPSDestLatitudeRef
|
||||
return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
|
||||
else if (tag == 20) // GPSDestLatitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 21) // GPSDestLongitudeRef
|
||||
return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
|
||||
else if (tag == 22) // GPSDestLongitude
|
||||
return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
else if (tag == 23) // GPSDestBearingRef
|
||||
return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
|
||||
else if (tag == 25) // GPSDestDistanceRef
|
||||
return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
|
||||
else if (tag == 29) // GPSDate
|
||||
return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
|
||||
else if (tag == 30) // GPSDifferential
|
||||
return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
|
||||
}
|
||||
else if (ifd == IFD.Interop)
|
||||
{
|
||||
if (tag == 1) // InteroperabilityIndex
|
||||
return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII);
|
||||
else if (tag == 2) // InteroperabilityVersion
|
||||
return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
|
||||
}
|
||||
else if (ifd == IFD.First)
|
||||
{
|
||||
if (tag == 0x103) // Compression
|
||||
return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x106) // PhotometricInterpretation
|
||||
return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x112) // Orientation
|
||||
return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x11c) // PlanarConfiguration
|
||||
return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x213) // YCbCrPositioning
|
||||
return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x128) // ResolutionUnit
|
||||
return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
|
||||
else if (tag == 0x132) // DateTime
|
||||
return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
|
||||
}
|
||||
|
||||
if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifByte(etag, value[0]);
|
||||
else
|
||||
return new ExifByteArray(etag, value);
|
||||
}
|
||||
else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
|
||||
{
|
||||
return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding);
|
||||
}
|
||||
else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifUShort(etag, conv.ToUInt16(value, 0));
|
||||
else
|
||||
return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifUInt(etag, conv.ToUInt32(value, 0));
|
||||
else
|
||||
return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
|
||||
else
|
||||
return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
|
||||
return new ExifUndefined(etag, value);
|
||||
else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifSInt(etag, conv.ToInt32(value, 0));
|
||||
else
|
||||
return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
|
||||
{
|
||||
if (count == 1)
|
||||
return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
|
||||
else
|
||||
return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("Unknown property type.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the tags associated with exif fields.
|
||||
/// </summary>
|
||||
internal enum ExifTag : int
|
||||
{
|
||||
// ****************************
|
||||
// Zeroth IFD
|
||||
// ****************************
|
||||
NewSubfileType = IFD.Zeroth + 254,
|
||||
SubfileType = IFD.Zeroth + 255,
|
||||
ImageWidth = IFD.Zeroth + 256,
|
||||
ImageLength = IFD.Zeroth + 257,
|
||||
BitsPerSample = IFD.Zeroth + 258,
|
||||
Compression = IFD.Zeroth + 259,
|
||||
PhotometricInterpretation = IFD.Zeroth + 262,
|
||||
Threshholding = IFD.Zeroth + 263,
|
||||
CellWidth = IFD.Zeroth + 264,
|
||||
CellLength = IFD.Zeroth + 265,
|
||||
FillOrder = IFD.Zeroth + 266,
|
||||
DocumentName = IFD.Zeroth + 269,
|
||||
ImageDescription = IFD.Zeroth + 270,
|
||||
Make = IFD.Zeroth + 271,
|
||||
Model = IFD.Zeroth + 272,
|
||||
StripOffsets = IFD.Zeroth + 273,
|
||||
Orientation = IFD.Zeroth + 274,
|
||||
SamplesPerPixel = IFD.Zeroth + 277,
|
||||
RowsPerStrip = IFD.Zeroth + 278,
|
||||
StripByteCounts = IFD.Zeroth + 279,
|
||||
MinSampleValue = IFD.Zeroth + 280,
|
||||
MaxSampleValue = IFD.Zeroth + 281,
|
||||
XResolution = IFD.Zeroth + 282,
|
||||
YResolution = IFD.Zeroth + 283,
|
||||
PlanarConfiguration = IFD.Zeroth + 284,
|
||||
PageName = IFD.Zeroth + 285,
|
||||
XPosition = IFD.Zeroth + 286,
|
||||
YPosition = IFD.Zeroth + 287,
|
||||
FreeOffsets = IFD.Zeroth + 288,
|
||||
FreeByteCounts = IFD.Zeroth + 289,
|
||||
GrayResponseUnit = IFD.Zeroth + 290,
|
||||
GrayResponseCurve = IFD.Zeroth + 291,
|
||||
T4Options = IFD.Zeroth + 292,
|
||||
T6Options = IFD.Zeroth + 293,
|
||||
ResolutionUnit = IFD.Zeroth + 296,
|
||||
PageNumber = IFD.Zeroth + 297,
|
||||
TransferFunction = IFD.Zeroth + 301,
|
||||
Software = IFD.Zeroth + 305,
|
||||
DateTime = IFD.Zeroth + 306,
|
||||
Artist = IFD.Zeroth + 315,
|
||||
HostComputer = IFD.Zeroth + 316,
|
||||
Predictor = IFD.Zeroth + 317,
|
||||
WhitePoint = IFD.Zeroth + 318,
|
||||
PrimaryChromaticities = IFD.Zeroth + 319,
|
||||
ColorMap = IFD.Zeroth + 320,
|
||||
HalftoneHints = IFD.Zeroth + 321,
|
||||
TileWidth = IFD.Zeroth + 322,
|
||||
TileLength = IFD.Zeroth + 323,
|
||||
TileOffsets = IFD.Zeroth + 324,
|
||||
TileByteCounts = IFD.Zeroth + 325,
|
||||
InkSet = IFD.Zeroth + 332,
|
||||
InkNames = IFD.Zeroth + 333,
|
||||
NumberOfInks = IFD.Zeroth + 334,
|
||||
DotRange = IFD.Zeroth + 336,
|
||||
TargetPrinter = IFD.Zeroth + 337,
|
||||
ExtraSamples = IFD.Zeroth + 338,
|
||||
SampleFormat = IFD.Zeroth + 339,
|
||||
SMinSampleValue = IFD.Zeroth + 340,
|
||||
SMaxSampleValue = IFD.Zeroth + 341,
|
||||
TransferRange = IFD.Zeroth + 342,
|
||||
JPEGProc = IFD.Zeroth + 512,
|
||||
JPEGInterchangeFormat = IFD.Zeroth + 513,
|
||||
JPEGInterchangeFormatLength = IFD.Zeroth + 514,
|
||||
JPEGRestartInterval = IFD.Zeroth + 515,
|
||||
JPEGLosslessPredictors = IFD.Zeroth + 517,
|
||||
JPEGPointTransforms = IFD.Zeroth + 518,
|
||||
JPEGQTables = IFD.Zeroth + 519,
|
||||
JPEGDCTables = IFD.Zeroth + 520,
|
||||
JPEGACTables = IFD.Zeroth + 521,
|
||||
YCbCrCoefficients = IFD.Zeroth + 529,
|
||||
YCbCrSubSampling = IFD.Zeroth + 530,
|
||||
YCbCrPositioning = IFD.Zeroth + 531,
|
||||
ReferenceBlackWhite = IFD.Zeroth + 532,
|
||||
Copyright = IFD.Zeroth + 33432,
|
||||
// Pointers to other IFDs
|
||||
EXIFIFDPointer = IFD.Zeroth + 34665,
|
||||
GPSIFDPointer = IFD.Zeroth + 34853,
|
||||
// Windows Tags
|
||||
WindowsTitle = IFD.Zeroth + 0x9c9b,
|
||||
WindowsComment = IFD.Zeroth + 0x9c9c,
|
||||
WindowsAuthor = IFD.Zeroth + 0x9c9d,
|
||||
WindowsKeywords = IFD.Zeroth + 0x9c9e,
|
||||
WindowsSubject = IFD.Zeroth + 0x9c9f,
|
||||
// Rating
|
||||
Rating = IFD.Zeroth + 0x4746,
|
||||
RatingPercent = IFD.Zeroth + 0x4749,
|
||||
// Microsoft specifing padding and offset tags
|
||||
ZerothIFDPadding = IFD.Zeroth + 0xea1c,
|
||||
// ****************************
|
||||
// EXIF Tags
|
||||
// ****************************
|
||||
ExifVersion = IFD.EXIF + 36864,
|
||||
FlashpixVersion = IFD.EXIF + 40960,
|
||||
ColorSpace = IFD.EXIF + 40961,
|
||||
ComponentsConfiguration = IFD.EXIF + 37121,
|
||||
CompressedBitsPerPixel = IFD.EXIF + 37122,
|
||||
PixelXDimension = IFD.EXIF + 40962,
|
||||
PixelYDimension = IFD.EXIF + 40963,
|
||||
MakerNote = IFD.EXIF + 37500,
|
||||
UserComment = IFD.EXIF + 37510,
|
||||
RelatedSoundFile = IFD.EXIF + 40964,
|
||||
DateTimeOriginal = IFD.EXIF + 36867,
|
||||
DateTimeDigitized = IFD.EXIF + 36868,
|
||||
SubSecTime = IFD.EXIF + 37520,
|
||||
SubSecTimeOriginal = IFD.EXIF + 37521,
|
||||
SubSecTimeDigitized = IFD.EXIF + 37522,
|
||||
ExposureTime = IFD.EXIF + 33434,
|
||||
FNumber = IFD.EXIF + 33437,
|
||||
ExposureProgram = IFD.EXIF + 34850,
|
||||
SpectralSensitivity = IFD.EXIF + 34852,
|
||||
ISOSpeedRatings = IFD.EXIF + 34855,
|
||||
OECF = IFD.EXIF + 34856,
|
||||
ShutterSpeedValue = IFD.EXIF + 37377,
|
||||
ApertureValue = IFD.EXIF + 37378,
|
||||
BrightnessValue = IFD.EXIF + 37379,
|
||||
ExposureBiasValue = IFD.EXIF + 37380,
|
||||
MaxApertureValue = IFD.EXIF + 37381,
|
||||
SubjectDistance = IFD.EXIF + 37382,
|
||||
MeteringMode = IFD.EXIF + 37383,
|
||||
LightSource = IFD.EXIF + 37384,
|
||||
Flash = IFD.EXIF + 37385,
|
||||
FocalLength = IFD.EXIF + 37386,
|
||||
SubjectArea = IFD.EXIF + 37396,
|
||||
FlashEnergy = IFD.EXIF + 41483,
|
||||
SpatialFrequencyResponse = IFD.EXIF + 41484,
|
||||
FocalPlaneXResolution = IFD.EXIF + 41486,
|
||||
FocalPlaneYResolution = IFD.EXIF + 41487,
|
||||
FocalPlaneResolutionUnit = IFD.EXIF + 41488,
|
||||
SubjectLocation = IFD.EXIF + 41492,
|
||||
ExposureIndex = IFD.EXIF + 41493,
|
||||
SensingMethod = IFD.EXIF + 41495,
|
||||
FileSource = IFD.EXIF + 41728,
|
||||
SceneType = IFD.EXIF + 41729,
|
||||
CFAPattern = IFD.EXIF + 41730,
|
||||
CustomRendered = IFD.EXIF + 41985,
|
||||
ExposureMode = IFD.EXIF + 41986,
|
||||
WhiteBalance = IFD.EXIF + 41987,
|
||||
DigitalZoomRatio = IFD.EXIF + 41988,
|
||||
FocalLengthIn35mmFilm = IFD.EXIF + 41989,
|
||||
SceneCaptureType = IFD.EXIF + 41990,
|
||||
GainControl = IFD.EXIF + 41991,
|
||||
Contrast = IFD.EXIF + 41992,
|
||||
Saturation = IFD.EXIF + 41993,
|
||||
Sharpness = IFD.EXIF + 41994,
|
||||
DeviceSettingDescription = IFD.EXIF + 41995,
|
||||
SubjectDistanceRange = IFD.EXIF + 41996,
|
||||
ImageUniqueID = IFD.EXIF + 42016,
|
||||
InteroperabilityIFDPointer = IFD.EXIF + 40965,
|
||||
// Microsoft specifing padding and offset tags
|
||||
ExifIFDPadding = IFD.EXIF + 0xea1c,
|
||||
OffsetSchema = IFD.EXIF + 0xea1d,
|
||||
// ****************************
|
||||
// GPS Tags
|
||||
// ****************************
|
||||
GPSVersionID = IFD.GPS + 0,
|
||||
GPSLatitudeRef = IFD.GPS + 1,
|
||||
GPSLatitude = IFD.GPS + 2,
|
||||
GPSLongitudeRef = IFD.GPS + 3,
|
||||
GPSLongitude = IFD.GPS + 4,
|
||||
GPSAltitudeRef = IFD.GPS + 5,
|
||||
GPSAltitude = IFD.GPS + 6,
|
||||
GPSTimeStamp = IFD.GPS + 7,
|
||||
GPSSatellites = IFD.GPS + 8,
|
||||
GPSStatus = IFD.GPS + 9,
|
||||
GPSMeasureMode = IFD.GPS + 10,
|
||||
GPSDOP = IFD.GPS + 11,
|
||||
GPSSpeedRef = IFD.GPS + 12,
|
||||
GPSSpeed = IFD.GPS + 13,
|
||||
GPSTrackRef = IFD.GPS + 14,
|
||||
GPSTrack = IFD.GPS + 15,
|
||||
GPSImgDirectionRef = IFD.GPS + 16,
|
||||
GPSImgDirection = IFD.GPS + 17,
|
||||
GPSMapDatum = IFD.GPS + 18,
|
||||
GPSDestLatitudeRef = IFD.GPS + 19,
|
||||
GPSDestLatitude = IFD.GPS + 20,
|
||||
GPSDestLongitudeRef = IFD.GPS + 21,
|
||||
GPSDestLongitude = IFD.GPS + 22,
|
||||
GPSDestBearingRef = IFD.GPS + 23,
|
||||
GPSDestBearing = IFD.GPS + 24,
|
||||
GPSDestDistanceRef = IFD.GPS + 25,
|
||||
GPSDestDistance = IFD.GPS + 26,
|
||||
GPSProcessingMethod = IFD.GPS + 27,
|
||||
GPSAreaInformation = IFD.GPS + 28,
|
||||
GPSDateStamp = IFD.GPS + 29,
|
||||
GPSDifferential = IFD.GPS + 30,
|
||||
// ****************************
|
||||
// InterOp Tags
|
||||
// ****************************
|
||||
InteroperabilityIndex = IFD.Interop + 1,
|
||||
InteroperabilityVersion = IFD.Interop + 2,
|
||||
RelatedImageWidth = IFD.Interop + 0x1001,
|
||||
RelatedImageHeight = IFD.Interop + 0x1002,
|
||||
// ****************************
|
||||
// First IFD TIFF Tags
|
||||
// ****************************
|
||||
ThumbnailImageWidth = IFD.First + 256,
|
||||
ThumbnailImageLength = IFD.First + 257,
|
||||
ThumbnailBitsPerSample = IFD.First + 258,
|
||||
ThumbnailCompression = IFD.First + 259,
|
||||
ThumbnailPhotometricInterpretation = IFD.First + 262,
|
||||
ThumbnailOrientation = IFD.First + 274,
|
||||
ThumbnailSamplesPerPixel = IFD.First + 277,
|
||||
ThumbnailPlanarConfiguration = IFD.First + 284,
|
||||
ThumbnailYCbCrSubSampling = IFD.First + 530,
|
||||
ThumbnailYCbCrPositioning = IFD.First + 531,
|
||||
ThumbnailXResolution = IFD.First + 282,
|
||||
ThumbnailYResolution = IFD.First + 283,
|
||||
ThumbnailResolutionUnit = IFD.First + 296,
|
||||
ThumbnailStripOffsets = IFD.First + 273,
|
||||
ThumbnailRowsPerStrip = IFD.First + 278,
|
||||
ThumbnailStripByteCounts = IFD.First + 279,
|
||||
ThumbnailJPEGInterchangeFormat = IFD.First + 513,
|
||||
ThumbnailJPEGInterchangeFormatLength = IFD.First + 514,
|
||||
ThumbnailTransferFunction = IFD.First + 301,
|
||||
ThumbnailWhitePoint = IFD.First + 318,
|
||||
ThumbnailPrimaryChromaticities = IFD.First + 319,
|
||||
ThumbnailYCbCrCoefficients = IFD.First + 529,
|
||||
ThumbnailReferenceBlackWhite = IFD.First + 532,
|
||||
ThumbnailDateTime = IFD.First + 306,
|
||||
ThumbnailImageDescription = IFD.First + 270,
|
||||
ThumbnailMake = IFD.First + 271,
|
||||
ThumbnailModel = IFD.First + 272,
|
||||
ThumbnailSoftware = IFD.First + 305,
|
||||
ThumbnailArtist = IFD.First + 315,
|
||||
ThumbnailCopyright = IFD.First + 33432,
|
||||
// ****************************
|
||||
// JFIF Tags
|
||||
// ****************************
|
||||
/// <summary>
|
||||
/// Represents the JFIF version.
|
||||
/// </summary>
|
||||
JFIFVersion = IFD.JFIF + 1,
|
||||
/// <summary>
|
||||
/// Represents units for X and Y densities.
|
||||
/// </summary>
|
||||
JFIFUnits = IFD.JFIF + 101,
|
||||
/// <summary>
|
||||
/// Horizontal pixel density.
|
||||
/// </summary>
|
||||
XDensity = IFD.JFIF + 102,
|
||||
/// <summary>
|
||||
/// Vertical pixel density
|
||||
/// </summary>
|
||||
YDensity = IFD.JFIF + 103,
|
||||
/// <summary>
|
||||
/// Thumbnail horizontal pixel count.
|
||||
/// </summary>
|
||||
JFIFXThumbnail = IFD.JFIF + 201,
|
||||
/// <summary>
|
||||
/// Thumbnail vertical pixel count.
|
||||
/// </summary>
|
||||
JFIFYThumbnail = IFD.JFIF + 202,
|
||||
/// <summary>
|
||||
/// JFIF JPEG thumbnail.
|
||||
/// </summary>
|
||||
JFIFThumbnail = IFD.JFIF + 203,
|
||||
/// <summary>
|
||||
/// Code which identifies the JFIF extension.
|
||||
/// </summary>
|
||||
JFXXExtensionCode = IFD.JFXX + 1,
|
||||
/// <summary>
|
||||
/// Thumbnail horizontal pixel count.
|
||||
/// </summary>
|
||||
JFXXXThumbnail = IFD.JFXX + 101,
|
||||
/// <summary>
|
||||
/// Thumbnail vertical pixel count.
|
||||
/// </summary>
|
||||
JFXXYThumbnail = IFD.JFXX + 102,
|
||||
/// <summary>
|
||||
/// The 256-Color RGB palette.
|
||||
/// </summary>
|
||||
JFXXPalette = IFD.JFXX + 201,
|
||||
/// <summary>
|
||||
/// JFIF thumbnail. The thumbnail will be either a JPEG,
|
||||
/// a 256 color palette bitmap, or a 24-bit RGB bitmap.
|
||||
/// </summary>
|
||||
JFXXThumbnail = IFD.JFXX + 202,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
internal static class ExifTagFactory
|
||||
{
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Returns the ExifTag corresponding to the given tag id.
|
||||
/// </summary>
|
||||
public static ExifTag GetExifTag(IFD ifd, ushort tagid)
|
||||
{
|
||||
return (ExifTag)(ifd + tagid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tag id corresponding to the given ExifTag.
|
||||
/// </summary>
|
||||
public static ushort GetTagID(ExifTag exiftag)
|
||||
{
|
||||
IFD ifd = GetTagIFD(exiftag);
|
||||
return (ushort)((int)exiftag - (int)ifd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IFD section containing the given tag.
|
||||
/// </summary>
|
||||
public static IFD GetTagIFD(ExifTag tag)
|
||||
{
|
||||
return (IFD)(((int)tag / 100000) * 100000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given exif tag.
|
||||
/// </summary>
|
||||
public static string GetTagName(ExifTag tag)
|
||||
{
|
||||
string name = Enum.GetName(typeof(ExifTag), tag);
|
||||
if (name == null)
|
||||
return "Unknown";
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given tag id.
|
||||
/// </summary>
|
||||
public static string GetTagName(IFD ifd, ushort tagid)
|
||||
{
|
||||
return GetTagName(GetExifTag(ifd, tagid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string representation for the given exif tag including
|
||||
/// IFD section and tag id.
|
||||
/// </summary>
|
||||
public static string GetTagLongName(ExifTag tag)
|
||||
{
|
||||
string ifdname = Enum.GetName(typeof(IFD), GetTagIFD(tag));
|
||||
string name = Enum.GetName(typeof(ExifTag), tag);
|
||||
if (name == null)
|
||||
name = "Unknown";
|
||||
string tagidname = GetTagID(tag).ToString();
|
||||
return ifdname + ": " + name + " (" + tagidname + ")";
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the IFD section containing tags.
|
||||
/// </summary>
|
||||
internal enum IFD : int
|
||||
{
|
||||
Unknown = 0,
|
||||
Zeroth = 100000,
|
||||
EXIF = 200000,
|
||||
GPS = 300000,
|
||||
Interop = 400000,
|
||||
First = 500000,
|
||||
MakerNote = 600000,
|
||||
JFIF = 700000,
|
||||
JFXX = 800000,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the base class for image files.
|
||||
/// </summary>
|
||||
[TypeDescriptionProvider(typeof(ExifFileTypeDescriptionProvider))]
|
||||
internal abstract class ImageFile
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFile"/> class.
|
||||
/// </summary>
|
||||
protected ImageFile ()
|
||||
{
|
||||
Format = ImageFileFormat.Unknown;
|
||||
Properties = new ExifPropertyCollection (this);
|
||||
Encoding = Encoding.Default;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns the format of the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public ImageFileFormat Format { get; protected set; }
|
||||
/// <summary>
|
||||
/// Gets the collection of Exif properties contained in the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public ExifPropertyCollection Properties { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the embedded thumbnail image.
|
||||
/// </summary>
|
||||
public ImageFile Thumbnail { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Exif property with the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The Exif tag associated with the Exif property.</param>
|
||||
public ExifProperty this[ExifTag key] {
|
||||
get { return Properties[key]; }
|
||||
set { Properties[key] = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the encoding used for text metadata when the source encoding is unknown.
|
||||
/// </summary>
|
||||
public Encoding Encoding { get; protected set; }
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ImageFile"/> to a <see cref="System.Drawing.Image"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="System.Drawing.Image"/> containing image data.</returns>
|
||||
public abstract Image ToImage ();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
public virtual void Save (string filename)
|
||||
{
|
||||
using (FileStream stream = new FileStream (filename, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||
Save (stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> to save image data to.</param>
|
||||
public abstract void Save (Stream stream);
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromFile (string filename)
|
||||
{
|
||||
return FromFile(filename, Encoding.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">A string that contains the name of the file.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromFile(string filename, Encoding encoding)
|
||||
{
|
||||
using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return FromStream(stream, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromStream(Stream stream)
|
||||
{
|
||||
return FromStream(stream, Encoding.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ImageFile"/> from the specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
/// <returns>The <see cref="ImageFile"/> created from the file.</returns>
|
||||
public static ImageFile FromStream(Stream stream, Encoding encoding)
|
||||
{
|
||||
stream.Seek (0, SeekOrigin.Begin);
|
||||
byte[] header = new byte[8];
|
||||
stream.Seek (0, SeekOrigin.Begin);
|
||||
if (stream.Read (header, 0, header.Length) != header.Length)
|
||||
throw new NotValidImageFileException ();
|
||||
|
||||
// JPEG
|
||||
if (header[0] == 0xFF && header[1] == 0xD8)
|
||||
return new JPEGFile (stream, encoding);
|
||||
|
||||
// TIFF
|
||||
string tiffHeader = Encoding.ASCII.GetString (header, 0, 4);
|
||||
if (tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00")
|
||||
return new TIFFFile (stream, encoding);
|
||||
|
||||
throw new NotValidImageFileException ();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an image file directory.
|
||||
/// </summary>
|
||||
internal class ImageFileDirectory
|
||||
{
|
||||
/// <summary>
|
||||
/// The fields contained in this IFD.
|
||||
/// </summary>
|
||||
public List<ImageFileDirectoryEntry> Fields { get; private set; }
|
||||
/// <summary>
|
||||
/// Offset to the next IFD.
|
||||
/// </summary>
|
||||
public uint NextIFDOffset { get; private set; }
|
||||
/// <summary>
|
||||
/// Compressed image data.
|
||||
/// </summary>
|
||||
public List<TIFFStrip> Strips { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFileDirectory"/> class.
|
||||
/// </summary>
|
||||
public ImageFileDirectory()
|
||||
{
|
||||
Fields = new List<ImageFileDirectoryEntry>();
|
||||
Strips = new List<TIFFStrip>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="ImageFileDirectory"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="ImageFileDirectory"/> initialized from the given byte data.</returns>
|
||||
public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
|
||||
{
|
||||
ImageFileDirectory ifd = new ImageFileDirectory();
|
||||
BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
List<uint> stripOffsets = new List<uint>();
|
||||
List<uint> stripLengths = new List<uint>();
|
||||
|
||||
// Count
|
||||
ushort fieldcount = conv.ToUInt16(data, offset);
|
||||
|
||||
// Read fields
|
||||
for (uint i = 0; i < fieldcount; i++)
|
||||
{
|
||||
uint fieldoffset = offset + 2 + 12 * i;
|
||||
ImageFileDirectoryEntry field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder);
|
||||
ifd.Fields.Add(field);
|
||||
|
||||
// Read strip offsets
|
||||
if (field.Tag == 273)
|
||||
{
|
||||
int baselen = field.Data.Length / (int)field.Count;
|
||||
for (uint j = 0; j < field.Count; j++)
|
||||
{
|
||||
byte[] val = new byte[baselen];
|
||||
Array.Copy(field.Data, j * baselen, val, 0, baselen);
|
||||
uint stripOffset = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
|
||||
stripOffsets.Add(stripOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Read strip lengths
|
||||
if (field.Tag == 279)
|
||||
{
|
||||
int baselen = field.Data.Length / (int)field.Count;
|
||||
for (uint j = 0; j < field.Count; j++)
|
||||
{
|
||||
byte[] val = new byte[baselen];
|
||||
Array.Copy(field.Data, j * baselen, val, 0, baselen);
|
||||
uint stripLength = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
|
||||
stripLengths.Add(stripLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save strips
|
||||
if (stripOffsets.Count != stripLengths.Count)
|
||||
throw new NotValidTIFFileException();
|
||||
for (int i = 0; i < stripOffsets.Count; i++)
|
||||
ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i]));
|
||||
|
||||
// Offset to next ifd
|
||||
ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + 12 * fieldcount);
|
||||
|
||||
return ifd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entry in the image file directory.
|
||||
/// </summary>
|
||||
internal struct ImageFileDirectoryEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag that identifies the field.
|
||||
/// </summary>
|
||||
public ushort Tag;
|
||||
/// <summary>
|
||||
/// Field type identifier.
|
||||
/// </summary>
|
||||
public ushort Type;
|
||||
/// <summary>
|
||||
/// Count of Type.
|
||||
/// </summary>
|
||||
public uint Count;
|
||||
/// <summary>
|
||||
/// Field data.
|
||||
/// </summary>
|
||||
public byte[] Data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageFileDirectoryEntry"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag that identifies the field.</param>
|
||||
/// <param name="type">Field type identifier.</param>
|
||||
/// <param name="count">Count of Type.</param>
|
||||
/// <param name="data">Field data.</param>
|
||||
public ImageFileDirectoryEntry(ushort tag, ushort type, uint count, byte[] data)
|
||||
{
|
||||
Tag = tag;
|
||||
Type = type;
|
||||
Count = count;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.</returns>
|
||||
public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
|
||||
{
|
||||
// Tag ID
|
||||
ushort tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Tag Type
|
||||
ushort type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Count of Type
|
||||
uint count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
// Field value or offset to field data
|
||||
byte[] value = new byte[4];
|
||||
Array.Copy(data, offset + 8, value, 0, 4);
|
||||
|
||||
// Calculate the bytes we need to read
|
||||
uint baselength = GetBaseLength(type);
|
||||
uint totallength = count * baselength;
|
||||
|
||||
// If field value does not fit in 4 bytes
|
||||
// the value field is an offset to the actual
|
||||
// field value
|
||||
if (totallength > 4)
|
||||
{
|
||||
uint dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder);
|
||||
value = new byte[totallength];
|
||||
Array.Copy(data, dataoffset, value, 0, totallength);
|
||||
}
|
||||
|
||||
// Reverse array order if byte orders are different
|
||||
if (byteOrder != BitConverterEx.SystemByteOrder)
|
||||
{
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte[] val = new byte[baselength];
|
||||
Array.Copy(value, i * baselength, val, 0, baselength);
|
||||
Array.Reverse(val);
|
||||
Array.Copy(val, 0, value, i * baselength, baselength);
|
||||
}
|
||||
}
|
||||
|
||||
return new ImageFileDirectoryEntry(tag, type, count, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base byte length for the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">Type identifier.</param>
|
||||
private static uint GetBaseLength(ushort type)
|
||||
{
|
||||
if (type == 1 || type == 6) // BYTE and SBYTE
|
||||
return 1;
|
||||
else if (type == 2 || type == 7) // ASCII and UNDEFINED
|
||||
return 1;
|
||||
else if (type == 3 || type == 8) // SHORT and SSHORT
|
||||
return 2;
|
||||
else if (type == 4 || type == 9) // LONG and SLONG
|
||||
return 4;
|
||||
else if (type == 5 || type == 10) // RATIONAL (2xLONG) and SRATIONAL (2xSLONG)
|
||||
return 8;
|
||||
else if (type == 11) // FLOAT
|
||||
return 4;
|
||||
else if (type == 12) // DOUBLE
|
||||
return 8;
|
||||
|
||||
throw new ArgumentException("Unknown type identifier.", "type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the format of the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
internal enum ImageFileFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// The file is not recognized.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
/// <summary>
|
||||
/// The file is a JPEG/Exif or JPEG/JFIF file.
|
||||
/// </summary>
|
||||
JPEG,
|
||||
/// <summary>
|
||||
/// The file is a TIFF File.
|
||||
/// </summary>
|
||||
TIFF,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the units for the X and Y densities
|
||||
/// for a JFIF file.
|
||||
/// </summary>
|
||||
internal enum JFIFDensityUnit : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No units, XDensity and YDensity specify the pixel aspect ratio.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// XDensity and YDensity are dots per inch.
|
||||
/// </summary>
|
||||
DotsPerInch = 1,
|
||||
/// <summary>
|
||||
/// XDensity and YDensity are dots per cm.
|
||||
/// </summary>
|
||||
DotsPerCm = 2,
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents the JFIF extension.
|
||||
/// </summary>
|
||||
internal enum JFIFExtension : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Thumbnail coded using JPEG.
|
||||
/// </summary>
|
||||
ThumbnailJPEG = 0x10,
|
||||
/// <summary>
|
||||
/// Thumbnail stored using a 256-Color RGB palette.
|
||||
/// </summary>
|
||||
ThumbnailPaletteRGB = 0x11,
|
||||
/// <summary>
|
||||
/// Thumbnail stored using 3 bytes/pixel (24-bit) RGB values.
|
||||
/// </summary>
|
||||
Thumbnail24BitRGB = 0x13,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the JFIF version as a 16 bit unsigned integer. (EXIF Specification: SHORT)
|
||||
/// </summary>
|
||||
internal class JFIFVersion : ExifUShort
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the major version.
|
||||
/// </summary>
|
||||
public byte Major { get { return (byte)(mValue >> 8); } }
|
||||
/// <summary>
|
||||
/// Gets the minor version.
|
||||
/// </summary>
|
||||
public byte Minor { get { return (byte)(mValue - (mValue >> 8) * 256); } }
|
||||
|
||||
public JFIFVersion(ExifTag tag, ushort value)
|
||||
: base(tag, value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}.{1:00}", Major, Minor);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a JFIF thumbnail. (EXIF Specification: BYTE)
|
||||
/// </summary>
|
||||
internal class JFIFThumbnailProperty : ExifProperty
|
||||
{
|
||||
protected JFIFThumbnail mValue;
|
||||
protected override object _Value { get { return Value; } set { Value = (JFIFThumbnail)value; } }
|
||||
public new JFIFThumbnail Value { get { return mValue; } set { mValue = value; } }
|
||||
|
||||
public override string ToString() { return mValue.Format.ToString(); }
|
||||
|
||||
public JFIFThumbnailProperty(ExifTag tag, JFIFThumbnail value)
|
||||
: base(tag)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public override ExifInterOperability Interoperability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mValue.Format == JFIFThumbnail.ImageFormat.BMP24Bit)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData);
|
||||
else if (mValue.Format == JFIFThumbnail.ImageFormat.BMPPalette)
|
||||
{
|
||||
byte[] data = new byte[mValue.Palette.Length + mValue.PixelData.Length];
|
||||
Array.Copy(mValue.Palette, data, mValue.Palette.Length);
|
||||
Array.Copy(mValue.PixelData, 0, data, mValue.Palette.Length, mValue.PixelData.Length);
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data);
|
||||
}
|
||||
else if (mValue.Format == JFIFThumbnail.ImageFormat.JPEG)
|
||||
return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData);
|
||||
else
|
||||
throw new InvalidOperationException("Unknown thumbnail type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JFIF thumbnail.
|
||||
/// </summary>
|
||||
internal class JFIFThumbnail
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the 256 color RGB palette.
|
||||
/// </summary>
|
||||
public byte[] Palette { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets raw image data.
|
||||
/// </summary>
|
||||
public byte[] PixelData { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the image format.
|
||||
/// </summary>
|
||||
public ImageFormat Format { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Public Enums
|
||||
public enum ImageFormat
|
||||
{
|
||||
JPEG,
|
||||
BMPPalette,
|
||||
BMP24Bit,
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
protected JFIFThumbnail()
|
||||
{
|
||||
Palette = new byte[0];
|
||||
PixelData = new byte[0];
|
||||
}
|
||||
|
||||
public JFIFThumbnail(ImageFormat format, byte[] data)
|
||||
: this()
|
||||
{
|
||||
Format = format;
|
||||
PixelData = data;
|
||||
}
|
||||
|
||||
public JFIFThumbnail(byte[] palette, byte[] data)
|
||||
: this()
|
||||
{
|
||||
Format = ImageFormat.BMPPalette;
|
||||
Palette = palette;
|
||||
PixelData = data;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the image file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class UnknownImageFormatException : Exception
|
||||
{
|
||||
public UnknownImageFormatException()
|
||||
: base("Unkown image format.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public UnknownImageFormatException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the image file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidImageFileException : Exception
|
||||
{
|
||||
public NotValidImageFileException()
|
||||
: base("Not a valid image file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidImageFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidJPEGFileException : Exception
|
||||
{
|
||||
public NotValidJPEGFileException()
|
||||
: base("Not a valid JPEG file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidJPEGFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidTIFFileException : Exception
|
||||
{
|
||||
public NotValidTIFFileException()
|
||||
: base("Not a valid TIFF file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidTIFFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF header
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidTIFFHeader : Exception
|
||||
{
|
||||
public NotValidTIFFHeader()
|
||||
: base("Not a valid TIFF header.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidTIFFHeader(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the length of a section exceeds 64 kB.
|
||||
/// </summary>
|
||||
internal class SectionExceeds64KBException : Exception
|
||||
{
|
||||
public SectionExceeds64KBException()
|
||||
: base("Section length exceeds 64 kB.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public SectionExceeds64KBException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,918 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the binary view of a JPEG compressed file.
|
||||
/// </summary>
|
||||
internal class JPEGFile : ImageFile
|
||||
{
|
||||
#region Member Variables
|
||||
private JPEGSection jfifApp0;
|
||||
private JPEGSection jfxxApp0;
|
||||
private JPEGSection exifApp1;
|
||||
private uint makerNoteOffset;
|
||||
private long exifIFDFieldOffset, gpsIFDFieldOffset, interopIFDFieldOffset, firstIFDFieldOffset;
|
||||
private long thumbOffsetLocation, thumbSizeLocation;
|
||||
private uint thumbOffsetValue, thumbSizeValue;
|
||||
private bool makerNoteProcessed;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets or sets the byte-order of the Exif properties.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder ByteOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the sections contained in the <see cref="ImageFile"/>.
|
||||
/// </summary>
|
||||
public List<JPEGSection> Sections { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets non-standard trailing data following the End of Image (EOI) marker.
|
||||
/// </summary>
|
||||
public byte[] TrailingData { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExifFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
protected internal JPEGFile(Stream stream, Encoding encoding)
|
||||
{
|
||||
Format = ImageFileFormat.JPEG;
|
||||
Sections = new List<JPEGSection>();
|
||||
TrailingData = new byte[0];
|
||||
Encoding = encoding;
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Read the Start of Image (SOI) marker. SOI marker is represented
|
||||
// with two bytes: 0xFF, 0xD8.
|
||||
byte[] markerbytes = new byte[2];
|
||||
if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8)
|
||||
throw new NotValidJPEGFileException();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Search and read sections until we reach the end of file.
|
||||
while (stream.Position != stream.Length)
|
||||
{
|
||||
// Read the next section marker. Section markers are two bytes
|
||||
// with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF.
|
||||
if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || markerbytes[1] == 0xFF)
|
||||
throw new NotValidJPEGFileException();
|
||||
|
||||
JPEGMarker marker = (JPEGMarker)markerbytes[1];
|
||||
|
||||
byte[] header = new byte[0];
|
||||
// SOI, EOI and RST markers do not contain any header
|
||||
if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
|
||||
{
|
||||
// Length of the header including the length bytes.
|
||||
// This value is a 16-bit unsigned integer
|
||||
// in big endian byte-order.
|
||||
byte[] lengthbytes = new byte[2];
|
||||
if (stream.Read(lengthbytes, 0, 2) != 2)
|
||||
throw new NotValidJPEGFileException();
|
||||
long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);
|
||||
|
||||
// Read section header.
|
||||
header = new byte[length - 2];
|
||||
int bytestoread = header.Length;
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(header, header.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
}
|
||||
|
||||
// Start of Scan (SOS) sections and RST sections are immediately
|
||||
// followed by entropy coded data. For that, we need to read until
|
||||
// the next section marker once we reach a SOS or RST.
|
||||
byte[] entropydata = new byte[0];
|
||||
if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
|
||||
{
|
||||
long position = stream.Position;
|
||||
|
||||
// Search for the next section marker
|
||||
while (true)
|
||||
{
|
||||
// Search for an 0xFF indicating start of a marker
|
||||
int nextbyte = 0;
|
||||
do
|
||||
{
|
||||
nextbyte = stream.ReadByte();
|
||||
if (nextbyte == -1)
|
||||
throw new NotValidJPEGFileException();
|
||||
} while ((byte)nextbyte != 0xFF);
|
||||
|
||||
// Skip filler bytes (0xFF)
|
||||
do
|
||||
{
|
||||
nextbyte = stream.ReadByte();
|
||||
if (nextbyte == -1)
|
||||
throw new NotValidJPEGFileException();
|
||||
} while ((byte)nextbyte == 0xFF);
|
||||
|
||||
// Looks like a section marker. The next byte must not be 0x00.
|
||||
if ((byte)nextbyte != 0x00)
|
||||
{
|
||||
// We reached a section marker. Calculate the
|
||||
// length of the entropy coded data.
|
||||
stream.Seek(-2, SeekOrigin.Current);
|
||||
long edlength = stream.Position - position;
|
||||
stream.Seek(-edlength, SeekOrigin.Current);
|
||||
|
||||
// Read entropy coded data
|
||||
entropydata = new byte[edlength];
|
||||
int bytestoread = entropydata.Length;
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store section.
|
||||
JPEGSection section = new JPEGSection(marker, header, entropydata);
|
||||
Sections.Add(section);
|
||||
|
||||
// Some propriety formats store data past the EOI marker
|
||||
if (marker == JPEGMarker.EOI)
|
||||
{
|
||||
int bytestoread = (int)(stream.Length - stream.Position);
|
||||
TrailingData = new byte[bytestoread];
|
||||
while (bytestoread > 0)
|
||||
{
|
||||
int count = (int)Math.Min(bytestoread, 4 * 1024);
|
||||
int bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count);
|
||||
if (bytesread == 0)
|
||||
throw new NotValidJPEGFileException();
|
||||
bytestoread -= bytesread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read metadata sections
|
||||
ReadJFIFAPP0();
|
||||
ReadJFXXAPP0();
|
||||
ReadExifAPP1();
|
||||
|
||||
// Process the maker note
|
||||
makerNoteProcessed = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
/// <param name="preserveMakerNote">Determines whether the maker note offset of
|
||||
/// the original file will be preserved.</param>
|
||||
public void Save(Stream stream, bool preserveMakerNote)
|
||||
{
|
||||
WriteJFIFApp0();
|
||||
WriteJFXXApp0();
|
||||
WriteExifApp1(preserveMakerNote);
|
||||
|
||||
// Write sections
|
||||
foreach (JPEGSection section in Sections)
|
||||
{
|
||||
// Section header (including length bytes and section marker)
|
||||
// must not exceed 64 kB.
|
||||
if (section.Header.Length + 2 + 2 > 64 * 1024)
|
||||
throw new SectionExceeds64KBException();
|
||||
|
||||
// APP sections must have a header.
|
||||
// Otherwise skip the entire section.
|
||||
if (section.Marker >= JPEGMarker.APP0 && section.Marker <= JPEGMarker.APP15 && section.Header.Length == 0)
|
||||
continue;
|
||||
|
||||
// Write section marker
|
||||
stream.Write(new byte[] { 0xFF, (byte)section.Marker }, 0, 2);
|
||||
|
||||
// SOI, EOI and RST markers do not contain any header
|
||||
if (section.Marker != JPEGMarker.SOI && section.Marker != JPEGMarker.EOI && !(section.Marker >= JPEGMarker.RST0 && section.Marker <= JPEGMarker.RST7))
|
||||
{
|
||||
// Header length including the length field itself
|
||||
stream.Write(BitConverterEx.BigEndian.GetBytes((ushort)(section.Header.Length + 2)), 0, 2);
|
||||
|
||||
// Write section header
|
||||
if (section.Header.Length != 0)
|
||||
stream.Write(section.Header, 0, section.Header.Length);
|
||||
}
|
||||
|
||||
// Write entropy coded data
|
||||
if (section.EntropyData.Length != 0)
|
||||
stream.Write(section.EntropyData, 0, section.EntropyData.Length);
|
||||
}
|
||||
|
||||
// Write trailing data, if any
|
||||
if (TrailingData.Length != 0)
|
||||
stream.Write(TrailingData, 0, TrailingData.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
/// <param name="preserveMakerNote">Determines whether the maker note offset of
|
||||
/// the original file will be preserved.</param>
|
||||
public void Save(string filename, bool preserveMakerNote)
|
||||
{
|
||||
using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
Save(stream, preserveMakerNote);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
public override void Save(string filename)
|
||||
{
|
||||
Save(filename, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the JPEG/Exif image to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the JPEG/Exif file.</param>
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
Save(stream, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a System.Drawing.Image created with image data.
|
||||
/// </summary>
|
||||
public override Image ToImage()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Save(stream);
|
||||
return Image.FromStream(stream);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Helper Methods
|
||||
/// <summary>
|
||||
/// Reads the APP0 section containing JFIF metadata.
|
||||
/// </summary>
|
||||
private void ReadJFIFAPP0()
|
||||
{
|
||||
// Find the APP0 section containing JFIF metadata
|
||||
jfifApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
|
||||
a.Header.Length >= 5 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"));
|
||||
|
||||
// If there is no APP0 section, return.
|
||||
if (jfifApp0 == null)
|
||||
return;
|
||||
|
||||
byte[] header = jfifApp0.Header;
|
||||
BitConverterEx jfifConv = BitConverterEx.BigEndian;
|
||||
|
||||
// Version
|
||||
ushort version = jfifConv.ToUInt16(header, 5);
|
||||
Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version));
|
||||
|
||||
// Units
|
||||
byte unit = header[7];
|
||||
Properties.Add(new ExifEnumProperty<JFIFDensityUnit>(ExifTag.JFIFUnits, (JFIFDensityUnit)unit));
|
||||
|
||||
// X and Y densities
|
||||
ushort xdensity = jfifConv.ToUInt16(header, 8);
|
||||
Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity));
|
||||
ushort ydensity = jfifConv.ToUInt16(header, 10);
|
||||
Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity));
|
||||
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[12];
|
||||
Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[13];
|
||||
Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail));
|
||||
|
||||
// Read JFIF thumbnail
|
||||
int n = xthumbnail * ythumbnail;
|
||||
byte[] jfifThumbnail = new byte[n];
|
||||
Array.Copy(header, 14, jfifThumbnail, 0, n);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP0 section with the JFIF properties.
|
||||
/// </summary>
|
||||
private bool WriteJFIFApp0()
|
||||
{
|
||||
// Which IFD sections do we have?
|
||||
List<ExifProperty> ifdjfef = new List<ExifProperty>();
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
if (prop.IFD == IFD.JFIF)
|
||||
ifdjfef.Add(prop);
|
||||
}
|
||||
|
||||
if (ifdjfef.Count == 0)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create a memory stream to write the APP0 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// JFIF identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5);
|
||||
|
||||
// Write tags
|
||||
foreach (ExifProperty prop in ifdjfef)
|
||||
{
|
||||
ExifInterOperability interop = prop.Interoperability;
|
||||
byte[] data = interop.Data;
|
||||
if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3)
|
||||
Array.Reverse(data);
|
||||
ms.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP0 header
|
||||
jfifApp0.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Reads the APP0 section containing JFIF extension metadata.
|
||||
/// </summary>
|
||||
private void ReadJFXXAPP0()
|
||||
{
|
||||
// Find the APP0 section containing JFIF metadata
|
||||
jfxxApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
|
||||
a.Header.Length >= 5 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"));
|
||||
|
||||
// If there is no APP0 section, return.
|
||||
if (jfxxApp0 == null)
|
||||
return;
|
||||
|
||||
byte[] header = jfxxApp0.Header;
|
||||
|
||||
// Version
|
||||
JFIFExtension version = (JFIFExtension)header[5];
|
||||
Properties.Add(new ExifEnumProperty<JFIFExtension>(ExifTag.JFXXExtensionCode, version));
|
||||
|
||||
// Read thumbnail
|
||||
if (version == JFIFExtension.ThumbnailJPEG)
|
||||
{
|
||||
byte[] data = new byte[header.Length - 6];
|
||||
Array.Copy(header, 6, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data)));
|
||||
}
|
||||
else if (version == JFIFExtension.Thumbnail24BitRGB)
|
||||
{
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[6];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[7];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
|
||||
byte[] data = new byte[3 * xthumbnail * ythumbnail];
|
||||
Array.Copy(header, 8, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data)));
|
||||
}
|
||||
else if (version == JFIFExtension.ThumbnailPaletteRGB)
|
||||
{
|
||||
// Thumbnails pixel count
|
||||
byte xthumbnail = header[6];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
|
||||
byte ythumbnail = header[7];
|
||||
Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
|
||||
byte[] palette = new byte[768];
|
||||
Array.Copy(header, 8, palette, 0, palette.Length);
|
||||
byte[] data = new byte[xthumbnail * ythumbnail];
|
||||
Array.Copy(header, 8 + 768, data, 0, data.Length);
|
||||
Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data)));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP0 section with the JFIF extension properties.
|
||||
/// </summary>
|
||||
private bool WriteJFXXApp0()
|
||||
{
|
||||
// Which IFD sections do we have?
|
||||
List<ExifProperty> ifdjfef = new List<ExifProperty>();
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
if (prop.IFD == IFD.JFXX)
|
||||
ifdjfef.Add(prop);
|
||||
}
|
||||
|
||||
if (ifdjfef.Count == 0)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a memory stream to write the APP0 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// JFIF identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("JFXX\0"), 0, 5);
|
||||
|
||||
// Write tags
|
||||
foreach (ExifProperty prop in ifdjfef)
|
||||
{
|
||||
ExifInterOperability interop = prop.Interoperability;
|
||||
byte[] data = interop.Data;
|
||||
if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3)
|
||||
Array.Reverse(data);
|
||||
ms.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP0 header
|
||||
jfxxApp0.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the APP1 section containing Exif metadata.
|
||||
/// </summary>
|
||||
private void ReadExifAPP1()
|
||||
{
|
||||
// Find the APP1 section containing Exif metadata
|
||||
exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
|
||||
a.Header.Length >= 6 &&
|
||||
(Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"));
|
||||
|
||||
// If there is no APP1 section, add a new one after the last APP0 section (if any).
|
||||
if (exifApp1 == null)
|
||||
{
|
||||
int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
|
||||
if (insertionIndex == -1) insertionIndex = 0;
|
||||
insertionIndex++;
|
||||
exifApp1 = new JPEGSection(JPEGMarker.APP1);
|
||||
Sections.Insert(insertionIndex, exifApp1);
|
||||
if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
|
||||
ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else
|
||||
ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] header = exifApp1.Header;
|
||||
SortedList<int, IFD> ifdqueue = new SortedList<int, IFD>();
|
||||
makerNoteOffset = 0;
|
||||
|
||||
// TIFF header
|
||||
int tiffoffset = 6;
|
||||
if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
|
||||
ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
|
||||
ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidExifFileException();
|
||||
|
||||
// TIFF header may have a different byte order
|
||||
BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
|
||||
if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
|
||||
tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
|
||||
tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidExifFileException();
|
||||
|
||||
// Offset to 0th IFD
|
||||
int ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);
|
||||
ifdqueue.Add(ifd0offset, IFD.Zeroth);
|
||||
|
||||
BitConverterEx conv = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
|
||||
int thumboffset = -1;
|
||||
int thumblength = 0;
|
||||
int thumbtype = -1;
|
||||
// Read IFDs
|
||||
while (ifdqueue.Count != 0)
|
||||
{
|
||||
int ifdoffset = tiffoffset + ifdqueue.Keys[0];
|
||||
IFD currentifd = ifdqueue.Values[0];
|
||||
ifdqueue.RemoveAt(0);
|
||||
|
||||
// Field count
|
||||
ushort fieldcount = conv.ToUInt16(header, ifdoffset);
|
||||
for (short i = 0; i < fieldcount; i++)
|
||||
{
|
||||
// Read field info
|
||||
int fieldoffset = ifdoffset + 2 + 12 * i;
|
||||
ushort tag = conv.ToUInt16(header, fieldoffset);
|
||||
ushort type = conv.ToUInt16(header, fieldoffset + 2);
|
||||
uint count = conv.ToUInt32(header, fieldoffset + 4);
|
||||
byte[] value = new byte[4];
|
||||
Array.Copy(header, fieldoffset + 8, value, 0, 4);
|
||||
|
||||
// Fields containing offsets to other IFDs
|
||||
if (currentifd == IFD.Zeroth && tag == 0x8769)
|
||||
{
|
||||
int exififdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(exififdpointer, IFD.EXIF);
|
||||
}
|
||||
else if (currentifd == IFD.Zeroth && tag == 0x8825)
|
||||
{
|
||||
int gpsifdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(gpsifdpointer, IFD.GPS);
|
||||
}
|
||||
else if (currentifd == IFD.EXIF && tag == 0xa005)
|
||||
{
|
||||
int interopifdpointer = (int)conv.ToUInt32(value, 0);
|
||||
ifdqueue.Add(interopifdpointer, IFD.Interop);
|
||||
}
|
||||
|
||||
// Save the offset to maker note data
|
||||
if (currentifd == IFD.EXIF && tag == 37500)
|
||||
makerNoteOffset = conv.ToUInt32(value, 0);
|
||||
|
||||
// Calculate the bytes we need to read
|
||||
uint baselength = 0;
|
||||
if (type == 1 || type == 2 || type == 7)
|
||||
baselength = 1;
|
||||
else if (type == 3)
|
||||
baselength = 2;
|
||||
else if (type == 4 || type == 9)
|
||||
baselength = 4;
|
||||
else if (type == 5 || type == 10)
|
||||
baselength = 8;
|
||||
uint totallength = count * baselength;
|
||||
|
||||
// If field value does not fit in 4 bytes
|
||||
// the value field is an offset to the actual
|
||||
// field value
|
||||
int fieldposition = 0;
|
||||
if (totallength > 4)
|
||||
{
|
||||
fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
|
||||
value = new byte[totallength];
|
||||
Array.Copy(header, fieldposition, value, 0, totallength);
|
||||
}
|
||||
|
||||
// Compressed thumbnail data
|
||||
if (currentifd == IFD.First && tag == 0x201)
|
||||
{
|
||||
thumbtype = 0;
|
||||
thumboffset = (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
else if (currentifd == IFD.First && tag == 0x202)
|
||||
thumblength = (int)conv.ToUInt32(value, 0);
|
||||
|
||||
// Uncompressed thumbnail data
|
||||
if (currentifd == IFD.First && tag == 0x111)
|
||||
{
|
||||
thumbtype = 1;
|
||||
// Offset to first strip
|
||||
if (type == 3)
|
||||
thumboffset = (int)conv.ToUInt16(value, 0);
|
||||
else
|
||||
thumboffset = (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
else if (currentifd == IFD.First && tag == 0x117)
|
||||
{
|
||||
thumblength = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
if (type == 3)
|
||||
thumblength += (int)conv.ToUInt16(value, 0);
|
||||
else
|
||||
thumblength += (int)conv.ToUInt32(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the exif property from the interop data
|
||||
ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding);
|
||||
Properties.Add(prop);
|
||||
}
|
||||
|
||||
// 1st IFD pointer
|
||||
int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
|
||||
if (firstifdpointer != 0)
|
||||
ifdqueue.Add(firstifdpointer, IFD.First);
|
||||
// Read thumbnail
|
||||
if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
|
||||
{
|
||||
if (thumbtype == 0)
|
||||
{
|
||||
using (MemoryStream ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength))
|
||||
{
|
||||
Thumbnail = ImageFile.FromStream(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the contents of the APP1 section with the Exif properties.
|
||||
/// </summary>
|
||||
private bool WriteExifApp1(bool preserveMakerNote)
|
||||
{
|
||||
// Zero out IFD field offsets. We will fill those as we write the IFD sections
|
||||
exifIFDFieldOffset = 0;
|
||||
gpsIFDFieldOffset = 0;
|
||||
interopIFDFieldOffset = 0;
|
||||
firstIFDFieldOffset = 0;
|
||||
// We also do not know the location of the embedded thumbnail yet
|
||||
thumbOffsetLocation = 0;
|
||||
thumbOffsetValue = 0;
|
||||
thumbSizeLocation = 0;
|
||||
thumbSizeValue = 0;
|
||||
// Write thumbnail tags if they are missing, remove otherwise
|
||||
if (Thumbnail == null)
|
||||
{
|
||||
Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormat);
|
||||
Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormatLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormat))
|
||||
Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0));
|
||||
if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormatLength))
|
||||
Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0));
|
||||
}
|
||||
|
||||
// Which IFD sections do we have?
|
||||
Dictionary<ExifTag, ExifProperty> ifdzeroth = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdexif = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdgps = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdinterop = new Dictionary<ExifTag, ExifProperty>();
|
||||
Dictionary<ExifTag, ExifProperty> ifdfirst = new Dictionary<ExifTag, ExifProperty>();
|
||||
|
||||
foreach (ExifProperty prop in Properties)
|
||||
{
|
||||
switch (prop.IFD)
|
||||
{
|
||||
case IFD.Zeroth:
|
||||
ifdzeroth.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.EXIF:
|
||||
ifdexif.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.GPS:
|
||||
ifdgps.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.Interop:
|
||||
ifdinterop.Add(prop.Tag, prop);
|
||||
break;
|
||||
case IFD.First:
|
||||
ifdfirst.Add(prop.Tag, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add IFD pointers if they are missing
|
||||
// We will write the pointer values later on
|
||||
if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
|
||||
ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0));
|
||||
if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
|
||||
ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0));
|
||||
if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
|
||||
ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0));
|
||||
|
||||
// Remove IFD pointers if IFD sections are missing
|
||||
if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
|
||||
ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
|
||||
if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
|
||||
ifdzeroth.Remove(ExifTag.GPSIFDPointer);
|
||||
if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
|
||||
ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);
|
||||
|
||||
if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && Thumbnail == null)
|
||||
{
|
||||
// Nothing to write
|
||||
return false;
|
||||
}
|
||||
|
||||
// We will need these bitconverter to write byte-ordered data
|
||||
BitConverterEx bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);
|
||||
|
||||
// Create a memory stream to write the APP1 section to
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
// Exif identifer
|
||||
ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6);
|
||||
|
||||
// TIFF header
|
||||
// Byte order
|
||||
long tiffoffset = ms.Position;
|
||||
ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
|
||||
// TIFF ID
|
||||
ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
|
||||
// Offset to 0th IFD
|
||||
ms.Write(bceExif.GetBytes((uint)8), 0, 4);
|
||||
|
||||
// Write IFDs
|
||||
WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
|
||||
uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
|
||||
uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
|
||||
uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
|
||||
uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);
|
||||
WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);
|
||||
|
||||
// Now that we now the location of IFDs we can go back and write IFD offsets
|
||||
if (exifIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (gpsIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (interopIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
|
||||
}
|
||||
if (firstIFDFieldOffset != 0)
|
||||
{
|
||||
ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
|
||||
}
|
||||
// We can write thumbnail location now
|
||||
if (thumbOffsetLocation != 0)
|
||||
{
|
||||
ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
|
||||
}
|
||||
if (thumbSizeLocation != 0)
|
||||
{
|
||||
ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
|
||||
ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
|
||||
// Return APP1 header
|
||||
exifApp1.Header = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void WriteIFD(MemoryStream stream, Dictionary<ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
|
||||
{
|
||||
BitConverterEx conv = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);
|
||||
|
||||
// Create a queue of fields to write
|
||||
Queue<ExifProperty> fieldqueue = new Queue<ExifProperty>();
|
||||
foreach (ExifProperty prop in ifd.Values)
|
||||
if (prop.Tag != ExifTag.MakerNote)
|
||||
fieldqueue.Enqueue(prop);
|
||||
// Push the maker note data to the end
|
||||
if (ifd.ContainsKey(ExifTag.MakerNote))
|
||||
fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);
|
||||
|
||||
// Offset to start of field data from start of TIFF header
|
||||
uint dataoffset = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
|
||||
uint currentdataoffset = dataoffset;
|
||||
long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);
|
||||
|
||||
bool makernotewritten = false;
|
||||
// Field count
|
||||
stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
|
||||
// Fields
|
||||
while (fieldqueue.Count != 0)
|
||||
{
|
||||
ExifProperty field = fieldqueue.Dequeue();
|
||||
ExifInterOperability interop = field.Interoperability;
|
||||
|
||||
uint fillerbytecount = 0;
|
||||
|
||||
// Try to preserve the makernote data offset
|
||||
if (!makernotewritten &&
|
||||
!makerNoteProcessed &&
|
||||
makerNoteOffset != 0 &&
|
||||
ifdtype == IFD.EXIF &&
|
||||
field.Tag != ExifTag.MakerNote &&
|
||||
interop.Data.Length > 4 &&
|
||||
currentdataoffset + interop.Data.Length > makerNoteOffset &&
|
||||
ifd.ContainsKey(ExifTag.MakerNote))
|
||||
{
|
||||
// Delay writing this field until we write makernote data
|
||||
fieldqueue.Enqueue(field);
|
||||
continue;
|
||||
}
|
||||
else if (field.Tag == ExifTag.MakerNote)
|
||||
{
|
||||
makernotewritten = true;
|
||||
// We may need to write filler bytes to preserve maker note offset
|
||||
if (preserveMakerNote && !makerNoteProcessed && (makerNoteOffset > currentdataoffset))
|
||||
fillerbytecount = makerNoteOffset - currentdataoffset;
|
||||
else
|
||||
fillerbytecount = 0;
|
||||
}
|
||||
|
||||
// Tag
|
||||
stream.Write(conv.GetBytes(interop.TagID), 0, 2);
|
||||
// Type
|
||||
stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
|
||||
// Count
|
||||
stream.Write(conv.GetBytes(interop.Count), 0, 4);
|
||||
// Field data
|
||||
byte[] data = interop.Data;
|
||||
if (ByteOrder != BitConverterEx.SystemByteOrder &&
|
||||
(interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9 ||
|
||||
interop.TypeID == 5 || interop.TypeID == 10))
|
||||
{
|
||||
int vlen = 4;
|
||||
if (interop.TypeID == 3) vlen = 2;
|
||||
int n = data.Length / vlen;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
Array.Reverse(data, i * vlen, vlen);
|
||||
}
|
||||
|
||||
// Fields containing offsets to other IFDs
|
||||
// Just store their offets, we will write the values later on when we know the lengths of IFDs
|
||||
if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
|
||||
exifIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
|
||||
gpsIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
|
||||
interopIFDFieldOffset = stream.Position;
|
||||
else if (ifdtype == IFD.First && interop.TagID == 0x201)
|
||||
thumbOffsetLocation = stream.Position;
|
||||
else if (ifdtype == IFD.First && interop.TagID == 0x202)
|
||||
thumbSizeLocation = stream.Position;
|
||||
|
||||
// Write 4 byte field value or field data
|
||||
if (data.Length <= 4)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
for (int i = data.Length; i < 4; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pointer to data area relative to TIFF header
|
||||
stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
|
||||
// Actual data
|
||||
long currentoffset = stream.Position;
|
||||
stream.Seek(absolutedataoffset, SeekOrigin.Begin);
|
||||
// Write filler bytes
|
||||
for (int i = 0; i < fillerbytecount; i++)
|
||||
stream.WriteByte(0xFF);
|
||||
stream.Write(data, 0, data.Length);
|
||||
stream.Seek(currentoffset, SeekOrigin.Begin);
|
||||
// Increment pointers
|
||||
currentdataoffset += fillerbytecount + (uint)data.Length;
|
||||
absolutedataoffset += fillerbytecount + data.Length;
|
||||
}
|
||||
}
|
||||
// Offset to 1st IFD
|
||||
// We will write zeros for now. This will be filled after we write all IFDs
|
||||
if (ifdtype == IFD.Zeroth)
|
||||
firstIFDFieldOffset = stream.Position;
|
||||
stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);
|
||||
|
||||
// Seek to end of IFD
|
||||
stream.Seek(absolutedataoffset, SeekOrigin.Begin);
|
||||
|
||||
// Write thumbnail data
|
||||
if (ifdtype == IFD.First)
|
||||
{
|
||||
if (Thumbnail != null)
|
||||
{
|
||||
MemoryStream ts = new MemoryStream();
|
||||
Thumbnail.Save(ts);
|
||||
ts.Close();
|
||||
byte[] thumb = ts.ToArray();
|
||||
thumbOffsetValue = (uint)(stream.Position - tiffoffset);
|
||||
thumbSizeValue = (uint)thumb.Length;
|
||||
stream.Write(thumb, 0, thumb.Length);
|
||||
ts.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbOffsetValue = 0;
|
||||
thumbSizeValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JPEG marker byte.
|
||||
/// </summary>
|
||||
internal enum JPEGMarker : byte
|
||||
{
|
||||
// Start Of Frame markers, non-differential, Huffman coding
|
||||
SOF0 = 0xc0,
|
||||
SOF1 = 0xc1,
|
||||
SOF2 = 0xc2,
|
||||
SOF3 = 0xc3,
|
||||
// Start Of Frame markers, differential, Huffman coding
|
||||
SOF5 = 0xc5,
|
||||
SOF6 = 0xc6,
|
||||
SOF7 = 0xc7,
|
||||
// Start Of Frame markers, non-differential, arithmetic coding
|
||||
JPG = 0xc8,
|
||||
SOF9 = 0xc9,
|
||||
SOF10 = 0xca,
|
||||
SOF11 = 0xcb,
|
||||
// Start Of Frame markers, differential, arithmetic coding
|
||||
SOF13 = 0xcd,
|
||||
SOF14 = 0xce,
|
||||
SOF15 = 0xcf,
|
||||
// Huffman table specification
|
||||
DHT = 0xc4,
|
||||
// Arithmetic coding conditioning specification
|
||||
DAC = 0xcc,
|
||||
// Restart interval termination
|
||||
RST0 = 0xd0,
|
||||
RST1 = 0xd1,
|
||||
RST2 = 0xd2,
|
||||
RST3 = 0xd3,
|
||||
RST4 = 0xd4,
|
||||
RST5 = 0xd5,
|
||||
RST6 = 0xd6,
|
||||
RST7 = 0xd7,
|
||||
// Other markers
|
||||
SOI = 0xd8,
|
||||
EOI = 0xd9,
|
||||
SOS = 0xda,
|
||||
DQT = 0xdb,
|
||||
DNL = 0xdc,
|
||||
DRI = 0xdd,
|
||||
DHP = 0xde,
|
||||
EXP = 0xdf,
|
||||
// application segments
|
||||
APP0 = 0xe0,
|
||||
APP1 = 0xe1,
|
||||
APP2 = 0xe2,
|
||||
APP3 = 0xe3,
|
||||
APP4 = 0xe4,
|
||||
APP5 = 0xe5,
|
||||
APP6 = 0xe6,
|
||||
APP7 = 0xe7,
|
||||
APP8 = 0xe8,
|
||||
APP9 = 0xe9,
|
||||
APP10 = 0xea,
|
||||
APP11 = 0xeb,
|
||||
APP12 = 0xec,
|
||||
APP13 = 0xed,
|
||||
APP14 = 0xee,
|
||||
APP15 = 0xef,
|
||||
// JPEG extensions
|
||||
JPG0 = 0xf0,
|
||||
JPG1 = 0xf1,
|
||||
JPG2 = 0xf2,
|
||||
JPG3 = 0xf3,
|
||||
JPG4 = 0xf4,
|
||||
JPG5 = 0xf5,
|
||||
JPG6 = 0xf6,
|
||||
JPG7 = 0xf7,
|
||||
JPG8 = 0xf8,
|
||||
JPG9 = 0xf9,
|
||||
JPG10 = 0xfa,
|
||||
JPG11 = 0xfb,
|
||||
JP1G2 = 0xfc,
|
||||
JPG13 = 0xfd,
|
||||
// Comment
|
||||
COM = 0xfe,
|
||||
// Temporary
|
||||
TEM = 0x01,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the memory view of a JPEG section.
|
||||
/// A JPEG section is the data between markers of the JPEG file.
|
||||
/// </summary>
|
||||
internal class JPEGSection
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The marker byte representing the section.
|
||||
/// </summary>
|
||||
public JPEGMarker Marker { get; private set; }
|
||||
/// <summary>
|
||||
/// Section header as a byte array. This is different from the header
|
||||
/// definition in JPEG specification in that it does not include the
|
||||
/// two byte section length.
|
||||
/// </summary>
|
||||
public byte[] Header { get; set; }
|
||||
/// <summary>
|
||||
/// For the SOS and RST markers, this contains the entropy coded data.
|
||||
/// </summary>
|
||||
public byte[] EntropyData { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructs a JPEGSection represented by the marker byte and containing
|
||||
/// the given data.
|
||||
/// </summary>
|
||||
/// <param name="marker">The marker byte representing the section.</param>
|
||||
/// <param name="data">Section data.</param>
|
||||
/// <param name="entropydata">Entropy coded data.</param>
|
||||
public JPEGSection(JPEGMarker marker, byte[] data, byte[] entropydata)
|
||||
{
|
||||
Marker = marker;
|
||||
Header = data;
|
||||
EntropyData = entropydata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a JPEGSection represented by the marker byte.
|
||||
/// </summary>
|
||||
/// <param name="marker">The marker byte representing the section.</param>
|
||||
public JPEGSection(JPEGMarker marker)
|
||||
: this(marker, new byte[0], new byte[0])
|
||||
{
|
||||
;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Returns a string representation of the current section.
|
||||
/// </summary>
|
||||
/// <returns>A System.String that represents the current section.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} => Header: {1} bytes, Entropy Data: {2} bytes", Marker, Header.Length, EntropyData.Length);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the binary view of a TIFF file.
|
||||
/// </summary>
|
||||
internal class TIFFFile : ImageFile
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the TIFF header.
|
||||
/// </summary>
|
||||
public TIFFHeader TIFFHeader { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the image file directories.
|
||||
/// </summary>
|
||||
public List<ImageFileDirectory> IFDs { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JPEGFile"/> class from the
|
||||
/// specified data stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param>
|
||||
/// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
|
||||
protected internal TIFFFile(Stream stream, System.Text.Encoding encoding)
|
||||
{
|
||||
Format = ImageFileFormat.TIFF;
|
||||
IFDs = new List<ImageFileDirectory>();
|
||||
Encoding = encoding;
|
||||
|
||||
// Read the entire stream
|
||||
byte[] data = Utility.GetStreamBytes(stream);
|
||||
|
||||
// Read the TIFF header
|
||||
TIFFHeader = TIFFHeader.FromBytes(data, 0);
|
||||
uint nextIFDOffset = TIFFHeader.IFDOffset;
|
||||
if (nextIFDOffset == 0)
|
||||
throw new NotValidTIFFileException("The first IFD offset is zero.");
|
||||
|
||||
// Read IFDs in order
|
||||
while (nextIFDOffset != 0)
|
||||
{
|
||||
ImageFileDirectory ifd = ImageFileDirectory.FromBytes(data, nextIFDOffset, TIFFHeader.ByteOrder);
|
||||
nextIFDOffset = ifd.NextIFDOffset;
|
||||
IFDs.Add(ifd);
|
||||
}
|
||||
|
||||
// Process IFDs
|
||||
// TODO: Add support for multiple frames
|
||||
foreach (ImageFileDirectoryEntry field in IFDs[0].Fields)
|
||||
{
|
||||
Properties.Add(ExifPropertyFactory.Get(field.Tag, field.Type, field.Count, field.Data, BitConverterEx.SystemByteOrder, IFD.Zeroth, Encoding));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance Methods
|
||||
/// <summary>
|
||||
/// Saves the <see cref="ImageFile"/> to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The data stream used to save the image.</param>
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
BitConverterEx conv = BitConverterEx.SystemEndian;
|
||||
|
||||
// Write TIFF header
|
||||
uint ifdoffset = 8;
|
||||
// Byte order
|
||||
stream.Write((BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
|
||||
// TIFF ID
|
||||
stream.Write(conv.GetBytes((ushort)42), 0, 2);
|
||||
// Offset to 0th IFD, will be corrected below
|
||||
stream.Write(conv.GetBytes(ifdoffset), 0, 4);
|
||||
|
||||
// Write IFD sections
|
||||
for (int i = 0; i < IFDs.Count; i++)
|
||||
{
|
||||
ImageFileDirectory ifd = IFDs[i];
|
||||
|
||||
// Save the location of IFD offset
|
||||
long ifdLocation = stream.Position - 4;
|
||||
|
||||
// Write strips first
|
||||
byte[] stripOffsets = new byte[4 * ifd.Strips.Count];
|
||||
byte[] stripLengths = new byte[4 * ifd.Strips.Count];
|
||||
uint stripOffset = ifdoffset;
|
||||
for (int j = 0; j < ifd.Strips.Count; j++)
|
||||
{
|
||||
byte[] stripData = ifd.Strips[j].Data;
|
||||
byte[] oBytes = BitConverter.GetBytes(stripOffset);
|
||||
byte[] lBytes = BitConverter.GetBytes((uint)stripData.Length);
|
||||
Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4);
|
||||
Array.Copy(lBytes, 0, stripLengths, 4 * j, 4);
|
||||
stream.Write(stripData, 0, stripData.Length);
|
||||
stripOffset += (uint)stripData.Length;
|
||||
}
|
||||
|
||||
// Remove old strip tags
|
||||
for (int j = ifd.Fields.Count - 1; j > 0; j--)
|
||||
{
|
||||
ushort tag = ifd.Fields[j].Tag;
|
||||
if (tag == 273 || tag == 279)
|
||||
ifd.Fields.RemoveAt(j);
|
||||
}
|
||||
// Write new strip tags
|
||||
ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets));
|
||||
ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths));
|
||||
|
||||
// Write fields after strips
|
||||
ifdoffset = stripOffset;
|
||||
|
||||
// Correct IFD offset
|
||||
long currentLocation = stream.Position;
|
||||
stream.Seek(ifdLocation, SeekOrigin.Begin);
|
||||
stream.Write(conv.GetBytes(ifdoffset), 0, 4);
|
||||
stream.Seek(currentLocation, SeekOrigin.Begin);
|
||||
|
||||
// Offset to field data
|
||||
uint dataOffset = ifdoffset + 2 + (uint)ifd.Fields.Count * 12 + 4;
|
||||
|
||||
// Field count
|
||||
stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2);
|
||||
|
||||
// Fields
|
||||
foreach (ImageFileDirectoryEntry field in ifd.Fields)
|
||||
{
|
||||
// Tag
|
||||
stream.Write(conv.GetBytes(field.Tag), 0, 2);
|
||||
// Type
|
||||
stream.Write(conv.GetBytes(field.Type), 0, 2);
|
||||
// Count
|
||||
stream.Write(conv.GetBytes(field.Count), 0, 4);
|
||||
|
||||
// Field data
|
||||
byte[] data = field.Data;
|
||||
if (data.Length <= 4)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
for (int j = data.Length; j < 4; j++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Write(conv.GetBytes(dataOffset), 0, 4);
|
||||
long currentOffset = stream.Position;
|
||||
stream.Seek(dataOffset, SeekOrigin.Begin);
|
||||
stream.Write(data, 0, data.Length);
|
||||
dataOffset += (uint)data.Length;
|
||||
stream.Seek(currentOffset, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
// Offset to next IFD
|
||||
ifdoffset = dataOffset;
|
||||
stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ImageFile"/> to a <see cref="System.Drawing.Image"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="System.Drawing.Image"/> containing image data.</returns>
|
||||
public override Image ToImage()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Save(stream);
|
||||
return Image.FromStream(stream);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a TIFF Header.
|
||||
/// </summary>
|
||||
internal struct TIFFHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte order of the image file.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder ByteOrder;
|
||||
/// <summary>
|
||||
/// TIFF ID. This value should always be 42.
|
||||
/// </summary>
|
||||
public byte ID;
|
||||
/// <summary>
|
||||
/// The offset to the first IFD section from the
|
||||
/// start of the TIFF header.
|
||||
/// </summary>
|
||||
public uint IFDOffset;
|
||||
/// <summary>
|
||||
/// The byte order of the TIFF header itself.
|
||||
/// </summary>
|
||||
public BitConverterEx.ByteOrder TIFFHeaderByteOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TIFFHeader"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="byteOrder">The byte order.</param>
|
||||
/// <param name="id">The TIFF ID. This value should always be 42.</param>
|
||||
/// <param name="ifdOffset">The offset to the first IFD section from the
|
||||
/// start of the TIFF header.</param>
|
||||
/// <param name="headerByteOrder">The byte order of the TIFF header itself.</param>
|
||||
public TIFFHeader(BitConverterEx.ByteOrder byteOrder, byte id, uint ifdOffset, BitConverterEx.ByteOrder headerByteOrder)
|
||||
{
|
||||
if (id != 42)
|
||||
throw new NotValidTIFFHeader();
|
||||
|
||||
ByteOrder = byteOrder;
|
||||
ID = id;
|
||||
IFDOffset = ifdOffset;
|
||||
TIFFHeaderByteOrder = headerByteOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TIFFHeader"/> initialized from the given byte data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset into <paramref name="data"/>.</param>
|
||||
/// <returns>A <see cref="TIFFHeader"/> initialized from the given byte data.</returns>
|
||||
public static TIFFHeader FromBytes(byte[] data, int offset)
|
||||
{
|
||||
TIFFHeader header = new TIFFHeader();
|
||||
|
||||
// TIFF header
|
||||
if (data[offset] == 0x49 && data[offset + 1] == 0x49)
|
||||
header.ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (data[offset] == 0x4D && data[offset + 1] == 0x4D)
|
||||
header.ByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidTIFFHeader();
|
||||
|
||||
// TIFF header may have a different byte order
|
||||
if (BitConverterEx.LittleEndian.ToUInt16(data, offset + 2) == 42)
|
||||
header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.LittleEndian;
|
||||
else if (BitConverterEx.BigEndian.ToUInt16(data, offset + 2) == 42)
|
||||
header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.BigEndian;
|
||||
else
|
||||
throw new NotValidTIFFHeader();
|
||||
header.ID = 42;
|
||||
|
||||
// IFD offset
|
||||
header.IFDOffset = BitConverterEx.ToUInt32(data, offset + 4, header.TIFFHeaderByteOrder, BitConverterEx.SystemByteOrder);
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a strip of compressed image data in a TIFF file.
|
||||
/// </summary>
|
||||
internal class TIFFStrip
|
||||
{
|
||||
/// <summary>
|
||||
/// Compressed image data contained in this strip.
|
||||
/// </summary>
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TIFFStrip"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">The byte array to copy strip from.</param>
|
||||
/// <param name="offset">The offset to the beginning of strip.</param>
|
||||
/// <param name="length">The length of strip.</param>
|
||||
public TIFFStrip(byte[] data, uint offset, uint length)
|
||||
{
|
||||
Data = new byte[length];
|
||||
Array.Copy(data, offset, Data, 0, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains utility functions.
|
||||
/// </summary>
|
||||
internal class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the entire stream and returns its contents as a byte array.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="System.IO.Stream"/> to read.</param>
|
||||
/// <returns>Contents of the <paramref name="stream"/> as a byte array.</returns>
|
||||
public static byte[] GetStreamBytes(Stream stream)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream())
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
byte[] b = new byte[32768];
|
||||
int r;
|
||||
while ((r = stream.Read(b, 0, b.Length)) > 0)
|
||||
mem.Write(b, 0, r);
|
||||
|
||||
return mem.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,8 @@ using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
using Umbraco.Core.Media.Exif;
|
||||
|
||||
namespace Umbraco.Core.Media
|
||||
{
|
||||
@@ -18,6 +16,52 @@ namespace Umbraco.Core.Media
|
||||
/// </summary>
|
||||
internal static class ImageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the dimensions of an image based on a stream
|
||||
/// </summary>
|
||||
/// <param name="imageStream"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// First try with EXIF, this is because it is insanely faster and doesn't use any memory to read exif data than to load in the entire
|
||||
/// image via GDI. Otherwise loading an image into GDI consumes a crazy amount of memory on large images.
|
||||
///
|
||||
/// Of course EXIF data might not exist in every file and can only exist in JPGs
|
||||
/// </remarks>
|
||||
public static Size GetDimensions(Stream imageStream)
|
||||
{
|
||||
//Try to load with exif
|
||||
try
|
||||
{
|
||||
var jpgInfo = ImageFile.FromStream(imageStream);
|
||||
|
||||
if (jpgInfo.Format != ImageFileFormat.Unknown
|
||||
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelYDimension)
|
||||
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelXDimension))
|
||||
{
|
||||
var height = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelYDimension].Value);
|
||||
var width = Convert.ToInt32(jpgInfo.Properties[ExifTag.PixelXDimension].Value);
|
||||
if (height > 0 && width > 0)
|
||||
{
|
||||
return new Size(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//We will just swallow, just means we can't read exif data, we don't want to log an error either
|
||||
}
|
||||
|
||||
//we have no choice but to try to read in via GDI
|
||||
using (var image = Image.FromStream(imageStream))
|
||||
{
|
||||
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
return new Size(fileWidth, fileHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static string GetMimeType(this Image image)
|
||||
{
|
||||
var format = image.RawFormat;
|
||||
|
||||
@@ -10,12 +10,14 @@ namespace Umbraco.Core.Models
|
||||
internal class ContentPreviewEntity<TContent> : ContentXmlEntity<TContent>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentPreviewEntity(bool previewExists, TContent content, Func<TContent, XElement> xml)
|
||||
: base(previewExists, content, xml)
|
||||
public ContentPreviewEntity(TContent content, Func<TContent, XElement> xml)
|
||||
: base(content, xml)
|
||||
{
|
||||
Version = content.Version;
|
||||
}
|
||||
|
||||
public Guid Version { get; private set; }
|
||||
public Guid Version
|
||||
{
|
||||
get { return Content.Version; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
private int _defaultTemplate;
|
||||
private IEnumerable<ITemplate> _allowedTemplates;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent's id.
|
||||
/// </summary>
|
||||
/// <remarks>You usually only want to use this for creating ContentTypes at the root.</remarks>
|
||||
/// <remarks>Only use this for creating ContentTypes at the root (with ParentId -1).</remarks>
|
||||
/// <param name="parentId"></param>
|
||||
public ContentType(int parentId) : base(parentId)
|
||||
{
|
||||
@@ -31,14 +31,26 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
public ContentType(IContentType parent) : base(parent)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
[Obsolete("This method is obsolete, use ContentType(IContentType parent, string alias) instead.", false)]
|
||||
public ContentType(IContentType parent) : this(parent, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent as an inherited type.
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="alias"></param>
|
||||
public ContentType(IContentType parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
_allowedTemplates = new List<ITemplate>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo DefaultTemplateSelector = ExpressionHelper.GetPropertyInfo<ContentType, int>(x => x.DefaultTemplateId);
|
||||
private static readonly PropertyInfo AllowedTemplatesSelector = ExpressionHelper.GetPropertyInfo<ContentType, IEnumerable<ITemplate>>(x => x.AllowedTemplates);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the default Template.
|
||||
/// </summary>
|
||||
@@ -95,7 +107,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
DefaultTemplateId = template.Id;
|
||||
if(_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
|
||||
if (_allowedTemplates.Any(x => x != null && x.Id == template.Id) == false)
|
||||
{
|
||||
var templates = AllowedTemplates.ToList();
|
||||
templates.Add(template);
|
||||
@@ -129,7 +141,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if(Key == Guid.Empty)
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
@@ -140,7 +152,7 @@ namespace Umbraco.Core.Models
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
{
|
||||
return DeepCloneWithResetIdentities(alias);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -15,6 +16,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
|
||||
public abstract class ContentTypeBase : Entity, IContentTypeBase
|
||||
{
|
||||
private Lazy<int> _parentId;
|
||||
@@ -48,17 +50,22 @@ namespace Umbraco.Core.Models
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
protected ContentTypeBase(IContentTypeBase parent)
|
||||
protected ContentTypeBase(IContentTypeBase parent) : this(parent, null)
|
||||
{
|
||||
Mandate.ParameterNotNull(parent, "parent");
|
||||
}
|
||||
|
||||
_parentId = new Lazy<int>(() => parent.Id);
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
protected ContentTypeBase(IContentTypeBase parent, string alias)
|
||||
{
|
||||
Mandate.ParameterNotNull(parent, "parent");
|
||||
|
||||
_alias = alias;
|
||||
_parentId = new Lazy<int>(() => parent.Id);
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
_propertyTypes = new PropertyTypeCollection();
|
||||
_propertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, string>(x => x.Name);
|
||||
private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, int>(x => x.ParentId);
|
||||
@@ -492,7 +499,6 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypeAlias">Alias of the <see cref="PropertyType"/> to remove</param>
|
||||
public void RemovePropertyType(string propertyTypeAlias)
|
||||
{
|
||||
|
||||
//check if the property exist in one of our collections
|
||||
if (PropertyGroups.Any(group => group.PropertyTypes.Any(pt => pt.Alias == propertyTypeAlias))
|
||||
|| _propertyTypes.Any(x => x.Alias == propertyTypeAlias))
|
||||
@@ -519,6 +525,7 @@ namespace Umbraco.Core.Models
|
||||
public void RemovePropertyGroup(string propertyGroupName)
|
||||
{
|
||||
PropertyGroups.RemoveItem(propertyGroupName);
|
||||
OnPropertyChanged(PropertyGroupCollectionSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,11 +22,16 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent)
|
||||
: base(parent)
|
||||
: this(parent, null)
|
||||
{
|
||||
AddContentType(parent);
|
||||
}
|
||||
|
||||
protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
AddContentType(parent);
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo ContentTypeCompositionSelector =
|
||||
ExpressionHelper.GetPropertyInfo<ContentTypeCompositionBase, IEnumerable<IContentTypeComposition>>(
|
||||
x => x.ContentTypeComposition);
|
||||
@@ -76,6 +81,9 @@ namespace Umbraco.Core.Models
|
||||
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(Alias) == false && Alias.Equals(contentType.Alias))
|
||||
return false;
|
||||
|
||||
if (ContentTypeCompositionExists(contentType.Alias) == false)
|
||||
{
|
||||
//Before we actually go ahead and add the ContentType as a Composition we ensure that we don't
|
||||
|
||||
@@ -11,13 +11,11 @@ namespace Umbraco.Core.Models
|
||||
internal class ContentXmlEntity<TContent> : IAggregateRoot
|
||||
where TContent : IContentBase
|
||||
{
|
||||
private readonly bool _entityExists;
|
||||
private readonly Func<TContent, XElement> _xml;
|
||||
|
||||
public ContentXmlEntity(bool entityExists, TContent content, Func<TContent, XElement> xml)
|
||||
{
|
||||
public ContentXmlEntity(TContent content, Func<TContent, XElement> xml)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
_entityExists = entityExists;
|
||||
_xml = xml;
|
||||
Content = content;
|
||||
}
|
||||
@@ -32,6 +30,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get { return _xml(Content); }
|
||||
}
|
||||
|
||||
public TContent Content { get; private set; }
|
||||
|
||||
public int Id
|
||||
@@ -44,9 +43,14 @@ namespace Umbraco.Core.Models
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Special case, always return false, this will cause the repositories managing
|
||||
/// this object to always do an 'insert' but these are special repositories that
|
||||
/// do an InsertOrUpdate on insert since the data for this needs to be managed this way
|
||||
/// </summary>
|
||||
public bool HasIdentity
|
||||
{
|
||||
get { return _entityExists; }
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the site
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string Path
|
||||
@@ -99,6 +99,11 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
public string VirtualPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the file could be validated
|
||||
/// </summary>
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path to the File from the root of the site
|
||||
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
|
||||
/// </summary>
|
||||
string Path { get; set; }
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
|
||||
/// </summary>
|
||||
string VirtualPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the file could be validated
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IPartialView : IFile
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -48,7 +49,7 @@ namespace Umbraco.Core.Models
|
||||
Id = id;
|
||||
UseInEditor = useInEditor;
|
||||
CacheDuration = cacheDuration;
|
||||
Alias = alias;
|
||||
Alias = alias.ToCleanString(CleanStringType.Alias);
|
||||
Name = name;
|
||||
ControlType = controlType;
|
||||
ControlAssembly = controlAssembly;
|
||||
@@ -87,7 +88,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
UseInEditor = useInEditor;
|
||||
CacheDuration = cacheDuration;
|
||||
Alias = alias;
|
||||
Alias = alias.ToCleanString(CleanStringType.Alias);
|
||||
Name = name;
|
||||
ControlType = controlType;
|
||||
ControlAssembly = controlAssembly;
|
||||
@@ -207,7 +208,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value;
|
||||
_alias = value.ToCleanString(CleanStringType.Alias);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Constuctor for creating a MediaType with the parent's id.
|
||||
/// </summary>
|
||||
/// <remarks>You usually only want to use this for creating MediaTypes at the root.</remarks>
|
||||
/// <remarks>Only use this for creating MediaTypes at the root (with ParentId -1).</remarks>
|
||||
/// <param name="parentId"></param>
|
||||
public MediaType(int parentId) : base(parentId)
|
||||
{
|
||||
@@ -24,10 +24,21 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
public MediaType(IMediaType parent) : base(parent)
|
||||
public MediaType(IMediaType parent) : this(parent, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a MediaType with the parent as an inherited type.
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="alias"></param>
|
||||
public MediaType(IMediaType parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Umbraco.Core.Models
|
||||
private string _email;
|
||||
private string _rawPasswordValue;
|
||||
private object _providerUserKey;
|
||||
private Type _userTypeKey;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating an empty Member object
|
||||
@@ -114,7 +113,6 @@ namespace Umbraco.Core.Models
|
||||
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Email);
|
||||
private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.RawPasswordValue);
|
||||
private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo<Member, object>(x => x.ProviderUserKey);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<Member, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Username
|
||||
@@ -502,38 +500,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
internal void SetProviderUserKeyType(Type type)
|
||||
{
|
||||
ProviderUserKeyType = type;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
@@ -21,7 +20,12 @@ namespace Umbraco.Core.Models
|
||||
MemberTypePropertyTypes = new Dictionary<string, MemberTypePropertyProfileAccess>();
|
||||
}
|
||||
|
||||
public MemberType(IContentTypeComposition parent) : base(parent)
|
||||
public MemberType(IContentTypeComposition parent) : this(parent, null)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberType(IContentTypeComposition parent, string alias)
|
||||
: base(parent, alias)
|
||||
{
|
||||
MemberTypePropertyTypes = new Dictionary<string, MemberTypePropertyProfileAccess>();
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal static class MembershipUserExtensions
|
||||
{
|
||||
internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName)
|
||||
internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName, bool providerKeyAsGuid = false)
|
||||
{
|
||||
var membershipMember = new UmbracoMembershipMember(member, providerName);
|
||||
var membershipMember = new UmbracoMembershipMember(member, providerName, providerKeyAsGuid);
|
||||
return membershipMember;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
@@ -25,7 +26,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
//NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor
|
||||
// validation for that doesn't need to be there anyways (have checked the source).
|
||||
public UmbracoMembershipMember(IMembershipUser member, string providerName)
|
||||
public UmbracoMembershipMember(IMembershipUser member, string providerName, bool providerKeyAsGuid = false)
|
||||
{
|
||||
_member = member;
|
||||
//NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user
|
||||
@@ -37,7 +38,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
if (member.PasswordQuestion != null)
|
||||
_passwordQuestion = member.PasswordQuestion.Trim();
|
||||
_providerName = providerName;
|
||||
_providerUserKey = member.ProviderUserKey;
|
||||
_providerUserKey = providerKeyAsGuid ? member.ProviderUserKey : member.Id;
|
||||
_comment = member.Comments;
|
||||
_isApproved = member.IsApproved;
|
||||
_isLockedOut = member.IsLockedOut;
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
private IUserType _userType;
|
||||
private string _name;
|
||||
private Type _userTypeKey;
|
||||
private List<string> _addedSections;
|
||||
private List<string> _removedSections;
|
||||
private ObservableCollection<string> _sectionCollection;
|
||||
@@ -80,7 +79,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartMediaId);
|
||||
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.AllowedSections);
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Name);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<User, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Username);
|
||||
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Email);
|
||||
@@ -101,37 +99,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
set { throw new NotSupportedException("Cannot set the provider user key for a user"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
internal void SetProviderUserKeyType(Type type)
|
||||
{
|
||||
ProviderUserKeyType = type;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Username
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PartialView : File
|
||||
public class PartialView : File, IPartialView
|
||||
{
|
||||
//public PartialView(): base(string.Empty)
|
||||
//{
|
||||
|
||||
@@ -71,16 +71,34 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
using (new WriteLock(_addLocker))
|
||||
{
|
||||
var key = GetKeyForItem(item);
|
||||
if (key != null)
|
||||
//Note this is done to ensure existig groups can be renamed
|
||||
if (item.HasIdentity && item.Id > 0)
|
||||
{
|
||||
var exists = this.Contains(key);
|
||||
var exists = this.Contains(item.Id);
|
||||
if (exists)
|
||||
{
|
||||
SetItem(IndexOfKey(key), item);
|
||||
var keyExists = this.Contains(item.Name);
|
||||
if(keyExists)
|
||||
throw new Exception(string.Format("Naming conflict: Changing the name of PropertyGroup '{0}' would result in duplicates", item.Name));
|
||||
|
||||
SetItem(IndexOfKey(item.Id), item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = GetKeyForItem(item);
|
||||
if (key != null)
|
||||
{
|
||||
var exists = this.Contains(key);
|
||||
if (exists)
|
||||
{
|
||||
SetItem(IndexOfKey(key), item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Add(item);
|
||||
OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
|
||||
|
||||
@@ -99,6 +117,11 @@ namespace Umbraco.Core.Models
|
||||
return this.Any(x => x.Name == groupName);
|
||||
}
|
||||
|
||||
public bool Contains(int id)
|
||||
{
|
||||
return this.Any(x => x.Id == id);
|
||||
}
|
||||
|
||||
public void RemoveItem(string propertyGroupName)
|
||||
{
|
||||
var key = IndexOfKey(propertyGroupName);
|
||||
@@ -119,6 +142,18 @@ namespace Umbraco.Core.Models
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int IndexOfKey(int id)
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (this[i].Id == id)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected override string GetKeyForItem(PropertyGroup item)
|
||||
{
|
||||
return item.Name;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
|
||||
public class PropertyType : Entity, IEquatable<PropertyType>
|
||||
{
|
||||
private readonly bool _isExplicitDbType;
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("uniqueID")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodeUniqueID")]
|
||||
public Guid? UniqueId { get; set; }
|
||||
|
||||
[Column("text")]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/// </summary>
|
||||
public enum TaggableObjectTypes
|
||||
{
|
||||
All,
|
||||
Content,
|
||||
Media,
|
||||
Member
|
||||
|
||||
@@ -1,11 +1,36 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal static class UserExtensions
|
||||
public static class UserExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the culture info associated with this user, based on the language they're assigned to in the back office
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="textService"></param>
|
||||
/// <returns></returns>
|
||||
public static CultureInfo GetUserCulture(this IUser user, ILocalizedTextService textService)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (textService == null) throw new ArgumentNullException("textService");
|
||||
return GetUserCulture(user.Language, textService);
|
||||
}
|
||||
|
||||
internal static CultureInfo GetUserCulture(string userLanguage, ILocalizedTextService textService)
|
||||
{
|
||||
return textService.GetSupportedCultures()
|
||||
.FirstOrDefault(culture =>
|
||||
//match on full name first
|
||||
culture.Name.InvariantEquals(userLanguage.Replace("_", "-")) ||
|
||||
//then match on the 2 letter name
|
||||
culture.TwoLetterISOLanguageName.InvariantEquals(userLanguage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the user has access to the content item based on their start noe
|
||||
/// </summary>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
where TResolver : ResolverBase
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
|
||||
/// with creation of objects based on an HttpRequest lifetime scope.
|
||||
@@ -31,9 +31,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application.</param>
|
||||
/// <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>
|
||||
protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: base(scope)
|
||||
{ }
|
||||
protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: base(scope)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
|
||||
@@ -41,9 +43,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
|
||||
protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
|
||||
: base(httpContext)
|
||||
{ }
|
||||
protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
|
||||
: base(httpContext)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list
|
||||
@@ -101,23 +105,37 @@ namespace Umbraco.Core.ObjectResolution
|
||||
private readonly List<Lazy<Type>> _lazyTypeList = new List<Lazy<Type>>();
|
||||
private readonly List<Func<IEnumerable<Type>>> _typeListProducerList = new List<Func<IEnumerable<Type>>>();
|
||||
private readonly List<Type> _excludedTypesList = new List<Type>();
|
||||
private Lazy<List<Type>> _resolvedTypes = null;
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_resolvedTypes = new Lazy<List<Type>>(() =>
|
||||
{
|
||||
var resolvedTypes = new List<Type>();
|
||||
|
||||
// get the types by evaluating the lazy & producers
|
||||
var types = new List<Type>();
|
||||
types.AddRange(_lazyTypeList.Select(x => x.Value));
|
||||
types.AddRange(_typeListProducerList.SelectMany(x => x()));
|
||||
|
||||
// we need to validate each resolved type now since we could
|
||||
// not do it before evaluating the lazy & producers
|
||||
foreach (var type in types.Where(x => _excludedTypesList.Contains(x) == false))
|
||||
{
|
||||
AddValidAndNoDuplicate(resolvedTypes, type);
|
||||
}
|
||||
|
||||
return resolvedTypes;
|
||||
});
|
||||
}
|
||||
|
||||
private List<Type> _resolvedTypes = null;
|
||||
private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the resolver has resolved types to create instances from.
|
||||
/// </summary>
|
||||
/// <remarks>To be used in unit tests.</remarks>
|
||||
public bool HasResolvedTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
using (new ReadLock(_resolvedTypesLock))
|
||||
{
|
||||
return _resolvedTypes != null;
|
||||
}
|
||||
}
|
||||
get { return _resolvedTypes.IsValueCreated; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -126,32 +144,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <remarks>When called, will get the types from the lazy list.</remarks>
|
||||
protected override IEnumerable<Type> InstanceTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(_resolvedTypesLock))
|
||||
{
|
||||
if (_resolvedTypes == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
_resolvedTypes = new List<Type>();
|
||||
|
||||
// get the types by evaluating the lazy & producers
|
||||
var types = new List<Type>();
|
||||
types.AddRange(_lazyTypeList.Select(x => x.Value));
|
||||
types.AddRange(_typeListProducerList.SelectMany(x => x()));
|
||||
|
||||
// we need to validate each resolved type now since we could
|
||||
// not do it before evaluating the lazy & producers
|
||||
foreach (var type in types.Where(x => !_excludedTypesList.Contains(x)))
|
||||
{
|
||||
AddValidAndNoDuplicate(_resolvedTypes, type);
|
||||
}
|
||||
}
|
||||
|
||||
return _resolvedTypes;
|
||||
}
|
||||
}
|
||||
get { return _resolvedTypes.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
where TResolved : class
|
||||
where TResolver : ResolverBase
|
||||
{
|
||||
private IEnumerable<TResolved> _applicationInstances = null;
|
||||
private Lazy<IEnumerable<TResolved>> _applicationInstances = null;
|
||||
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
private readonly string _httpContextKey;
|
||||
private readonly List<Type> _instanceTypes = new List<Type>();
|
||||
@@ -47,6 +47,8 @@ namespace Umbraco.Core.ObjectResolution
|
||||
if (scope == ObjectLifetimeScope.HttpRequest)
|
||||
_httpContextKey = this.GetType().FullName;
|
||||
_instanceTypes = new List<Type>();
|
||||
|
||||
InitializeAppInstances();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,6 +66,8 @@ namespace Umbraco.Core.ObjectResolution
|
||||
_httpContextKey = this.GetType().FullName;
|
||||
CurrentHttpContext = httpContext;
|
||||
_instanceTypes = new List<Type>();
|
||||
|
||||
InitializeAppInstances();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +98,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void InitializeAppInstances()
|
||||
{
|
||||
_applicationInstances = new Lazy<IEnumerable<TResolved>>(() => CreateInstances().ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
|
||||
/// </summary>
|
||||
@@ -178,30 +187,17 @@ namespace Umbraco.Core.ObjectResolution
|
||||
switch (LifetimeScope)
|
||||
{
|
||||
case ObjectLifetimeScope.HttpRequest:
|
||||
// create new instances per HttpContext
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
{
|
||||
// create if not already there
|
||||
if (CurrentHttpContext.Items[_httpContextKey] == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
}
|
||||
|
||||
case ObjectLifetimeScope.Application:
|
||||
// create new instances per application
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
// create new instances per HttpContext
|
||||
if (CurrentHttpContext.Items[_httpContextKey] == null)
|
||||
{
|
||||
// create if not already there
|
||||
if (_applicationInstances == null)
|
||||
{
|
||||
l.UpgradeToWriteLock();
|
||||
_applicationInstances = CreateInstances().ToArray();
|
||||
}
|
||||
return _applicationInstances;
|
||||
CurrentHttpContext.Items[_httpContextKey] = CreateInstances().ToArray();
|
||||
}
|
||||
return (TResolved[])CurrentHttpContext.Items[_httpContextKey];
|
||||
|
||||
case ObjectLifetimeScope.Application:
|
||||
|
||||
return _applicationInstances.Value;
|
||||
|
||||
case ObjectLifetimeScope.Transient:
|
||||
default:
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Persistence
|
||||
connection.Open();
|
||||
connection.Close();
|
||||
}
|
||||
catch (SqlException)
|
||||
catch (DbException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
internal void AddAdditionalData(UmbracoEntity entity, IDictionary<string, object> originalEntityProperties)
|
||||
{
|
||||
var entityProps = TypeHelper.GetPublicProperties(typeof(IUmbracoEntity)).Select(x => x.Name).ToArray();
|
||||
var entityProps = typeof(IUmbracoEntity).GetPublicProperties().Select(x => x.Name).ToArray();
|
||||
|
||||
//figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data
|
||||
foreach (var k in originalEntityProperties.Keys
|
||||
|
||||
@@ -13,12 +13,21 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
internal abstract void BuildMap();
|
||||
|
||||
internal string Map(string propertyName)
|
||||
internal string Map(string propertyName, bool throws = false)
|
||||
{
|
||||
DtoMapModel dtoTypeProperty;
|
||||
return PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty)
|
||||
? GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo)
|
||||
: string.Empty;
|
||||
if (PropertyInfoCache.TryGetValue(propertyName, out dtoTypeProperty))
|
||||
{
|
||||
return GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (throws)
|
||||
{
|
||||
throw new InvalidOperationException("Could not get the value with the key " + propertyName + " from the property info cache, keys available: " + string.Join(", ", PropertyInfoCache.Keys));
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal void CacheMap<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
@@ -30,7 +39,12 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
internal DtoMapModel ResolveMapping<TSource, TDestination>(Expression<Func<TSource, object>> sourceMember, Expression<Func<TDestination, object>> destinationMember)
|
||||
{
|
||||
var source = ExpressionHelper.FindProperty(sourceMember);
|
||||
var destination = ExpressionHelper.FindProperty(destinationMember) as PropertyInfo;
|
||||
var destination = (PropertyInfo)ExpressionHelper.FindProperty(destinationMember);
|
||||
|
||||
if (destination == null)
|
||||
{
|
||||
throw new InvalidOperationException("The 'destination' returned was null, cannot resolve the mapping");
|
||||
}
|
||||
|
||||
return new DtoMapModel(typeof(TDestination), destination, source.Name);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
SourcePropertyName = sourcePropertyName;
|
||||
}
|
||||
|
||||
public string SourcePropertyName { get; set; }
|
||||
public Type Type { get; set; }
|
||||
public PropertyInfo PropertyInfo { get; set; }
|
||||
public string SourcePropertyName { get; private set; }
|
||||
public Type Type { get; private set; }
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
return byAttribute.Result;
|
||||
}
|
||||
|
||||
//static mapper registration if not using attributes, could be something like this:
|
||||
//if (type == typeof (UserType))
|
||||
// return new UserTypeMapper();
|
||||
|
||||
throw new Exception("Invalid Type: A Mapper could not be resolved based on the passed in Type");
|
||||
});
|
||||
}
|
||||
@@ -56,31 +51,18 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
/// <param name="entityType"></param>
|
||||
/// <returns></returns>
|
||||
private Attempt<BaseMapper> TryGetMapperByAttribute(Type entityType)
|
||||
{
|
||||
//get all BaseMapper types that have a MapperFor attribute:
|
||||
var assignedMapperTypes = InstanceTypes;
|
||||
|
||||
{
|
||||
//check if any of the mappers are assigned to this type
|
||||
var mapper = assignedMapperTypes.FirstOrDefault(
|
||||
x => x.GetCustomAttributes<MapperForAttribute>(false)
|
||||
var mapper = Values.FirstOrDefault(
|
||||
x => x.GetType().GetCustomAttributes<MapperForAttribute>(false)
|
||||
.Any(m => m.EntityType == entityType));
|
||||
|
||||
if (mapper == null)
|
||||
{
|
||||
return Attempt<BaseMapper>.Fail();
|
||||
}
|
||||
try
|
||||
{
|
||||
var instance = Activator.CreateInstance(mapper) as BaseMapper;
|
||||
return instance != null
|
||||
? Attempt<BaseMapper>.Succeed(instance)
|
||||
: Attempt<BaseMapper>.Fail();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error(typeof(MappingResolver), "Could not instantiate mapper of type " + mapper, ex);
|
||||
return Attempt<BaseMapper>.Fail(ex);
|
||||
}
|
||||
|
||||
return Attempt<BaseMapper>.Succeed(mapper);
|
||||
}
|
||||
|
||||
internal string GetMapping(Type type, string propertyName)
|
||||
|
||||
+5
-6
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these default constraints
|
||||
//If we are on SQLServer, we need to delete default constraints by name, older versions of umbraco did not name these default constraints
|
||||
// consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
|
||||
// http://issues.umbraco.org/issue/U4-4133
|
||||
var sqlServerSyntaxProvider = new SqlServerSyntaxProvider();
|
||||
@@ -51,16 +51,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
|
||||
Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty");
|
||||
|
||||
if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer)
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id");
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints
|
||||
// consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
|
||||
// http://issues.umbraco.org/issue/U4-4133
|
||||
|
||||
//Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
|
||||
// differently than the default.
|
||||
|
||||
var keyConstraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct();
|
||||
var constraint = keyConstraints
|
||||
.SingleOrDefault(x => x.Item1 == "cmsMacroProperty" && x.Item2 == "macroPropertyType" && x.Item3.InvariantStartsWith("PK_") == false);
|
||||
|
||||
+9
-21
@@ -31,15 +31,14 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable();
|
||||
|
||||
//drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql.
|
||||
if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer)
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id");
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints
|
||||
// consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue:
|
||||
// http://issues.umbraco.org/issue/U4-4133
|
||||
//Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
|
||||
// differently than the default.
|
||||
|
||||
var constraint = constraints
|
||||
.SingleOrDefault(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
|
||||
@@ -62,7 +61,12 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
}
|
||||
else
|
||||
{
|
||||
Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
|
||||
//lookup the PK by name
|
||||
var pkName = constraints.FirstOrDefault(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantStartsWith("PK_"));
|
||||
if (pkName != null)
|
||||
{
|
||||
Delete.PrimaryKey(pkName.Item3).FromTable("cmsTagRelationship");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -137,22 +141,6 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
.OnDelete(Rule.None)
|
||||
.OnUpdate(Rule.None);
|
||||
|
||||
//Some very old schemas don't have an index on the cmsContent.nodeId column, I'm not actually sure when it was added but
|
||||
// it is absolutely required to exist in order to add this foreign key, so we'll need to check it's existence
|
||||
// this came to light from this issue: http://issues.umbraco.org/issue/U4-4133
|
||||
var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition()
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContent")) == false)
|
||||
{
|
||||
Create.Index("IX_cmsContent").OnTable("cmsContent").OnColumn("nodeId").Ascending().WithOptions().Unique();
|
||||
}
|
||||
|
||||
//now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode)
|
||||
Create.ForeignKey("FK_cmsTagRelationship_cmsContent")
|
||||
.FromTable("cmsTagRelationship")
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
|
||||
{
|
||||
/// <summary>
|
||||
/// I'm not actually sure how this is possible but I've come across one install that was missing these PKs
|
||||
/// and it wasn't a MySQL install.
|
||||
/// see: http://issues.umbraco.org/issue/U4-5707
|
||||
/// </summary>
|
||||
[Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AssignMissingKeysAndIndexes : MigrationBase
|
||||
{
|
||||
public override void Up()
|
||||
{
|
||||
|
||||
//Some very old schemas don't have an index on the cmsContent.nodeId column, I'm not actually sure when it was added but
|
||||
// it is absolutely required to exist in order to have it as a foreign key reference, so we'll need to check it's existence
|
||||
// this came to light from this issue: http://issues.umbraco.org/issue/U4-4133
|
||||
var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition()
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContent")) == false)
|
||||
{
|
||||
Create.Index("IX_cmsContent").OnTable("cmsContent").OnColumn("nodeId").Ascending().WithOptions().Unique();
|
||||
}
|
||||
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.SqlServer
|
||||
|| Context.CurrentDatabaseProvider == DatabaseProviders.SqlServerCE)
|
||||
{
|
||||
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
|
||||
|
||||
//This should be 2 because this table has 2 keys
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsPreviewXml") && x.Item3.InvariantStartsWith("PK_")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsContentPreviewXml")
|
||||
.OnTable("cmsPreviewXml")
|
||||
.Columns(new[] { "nodeId", "versionId" });
|
||||
}
|
||||
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsTags") && x.Item3.InvariantStartsWith("PK_")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsTags")
|
||||
.OnTable("cmsTags")
|
||||
.Columns(new[] { "id" });
|
||||
}
|
||||
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsStylesheetProperty") && x.Item3.InvariantStartsWith("PK_")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsStylesheetProperty")
|
||||
.OnTable("cmsStylesheetProperty")
|
||||
.Columns(new[] { "nodeId" });
|
||||
}
|
||||
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsStylesheet") && x.Item3.InvariantStartsWith("PK_")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsStylesheet")
|
||||
.OnTable("cmsStylesheet")
|
||||
.Columns(new[] { "nodeId" });
|
||||
|
||||
Create.ForeignKey("FK_cmsStylesheet_umbracoNode_id").FromTable("cmsStylesheet").ForeignColumn("nodeId")
|
||||
.ToTable("umbracoNode").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
|
||||
}
|
||||
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsMember") && x.Item3.InvariantStartsWith("PK_")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsMember")
|
||||
.OnTable("cmsMember")
|
||||
.Columns(new[] { "nodeId" });
|
||||
|
||||
Create.ForeignKey("FK_cmsMember_umbracoNode_id").FromTable("cmsMember").ForeignColumn("nodeId")
|
||||
.ToTable("umbracoNode").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
|
||||
|
||||
Create.ForeignKey("FK_cmsMember_cmsContent_nodeId").FromTable("cmsMember").ForeignColumn("nodeId")
|
||||
.ToTable("cmsContent").PrimaryColumn("nodeId").OnDeleteOrUpdate(Rule.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
//don't do anything, these keys should have always existed!
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
|
||||
{
|
||||
[Migration("7.2.0", 3, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AddIndexToUmbracoNodeTable : MigrationBase
|
||||
{
|
||||
private readonly bool _skipIndexCheck;
|
||||
|
||||
internal AddIndexToUmbracoNodeTable(bool skipIndexCheck)
|
||||
{
|
||||
_skipIndexCheck = skipIndexCheck;
|
||||
}
|
||||
|
||||
public AddIndexToUmbracoNodeTable()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")) == false)
|
||||
{
|
||||
Create.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Ascending().WithOptions().NonClustered();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -1,3 +1,5 @@
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
@@ -11,9 +13,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
|
||||
{
|
||||
public override void Up()
|
||||
{
|
||||
//To text
|
||||
var textType = SqlSyntaxContext.SqlSyntaxProvider.GetSpecialDbType(SpecialDbTypes.NTEXT);
|
||||
Alter.Table("cmsDataTypePreValues").AlterColumn("value").AsCustom(textType).Nullable();
|
||||
var columns = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(Context.Database).Distinct().ToArray();
|
||||
|
||||
//Check if it's already text
|
||||
if (columns.Any(x => x.ColumnName.InvariantEquals("value") && x.TableName.InvariantEquals("cmsDataTypePreValues")
|
||||
//mysql check
|
||||
&& (x.DataType.InvariantEquals("longtext") == false
|
||||
//sql server check
|
||||
&& x.DataType.InvariantEquals("ntext") == false)))
|
||||
{
|
||||
//To text
|
||||
var textType = SqlSyntaxContext.SqlSyntaxProvider.GetSpecialDbType(SpecialDbTypes.NTEXT);
|
||||
Alter.Table("cmsDataTypePreValues").AlterColumn("value").AsCustom(textType).Nullable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
|
||||
@@ -562,7 +562,14 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
object val = cmd.ExecuteScalarWithRetry();
|
||||
OnExecutedCommand(cmd);
|
||||
return (T)Convert.ChangeType(val, typeof(T));
|
||||
|
||||
if (val == null || val == DBNull.Value)
|
||||
return default(T);
|
||||
|
||||
Type t = typeof(T);
|
||||
Type u = Nullable.GetUnderlyingType(t);
|
||||
|
||||
return (T)Convert.ChangeType(val, u ?? t);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -843,7 +850,7 @@ namespace Umbraco.Core.Persistence
|
||||
public IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql.SQL, sql.Arguments); }
|
||||
|
||||
// Automagically guess the property relationships between various POCOs and create a delegate that will set them up
|
||||
object GetAutoMapper(Type[] types)
|
||||
Delegate GetAutoMapper(Type[] types)
|
||||
{
|
||||
// Build a key
|
||||
var kb = new StringBuilder();
|
||||
@@ -858,7 +865,7 @@ namespace Umbraco.Core.Persistence
|
||||
RWLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
object mapper;
|
||||
Delegate mapper;
|
||||
if (AutoMappers.TryGetValue(key, out mapper))
|
||||
return mapper;
|
||||
}
|
||||
@@ -872,7 +879,7 @@ namespace Umbraco.Core.Persistence
|
||||
try
|
||||
{
|
||||
// Try again
|
||||
object mapper;
|
||||
Delegate mapper;
|
||||
if (AutoMappers.TryGetValue(key, out mapper))
|
||||
return mapper;
|
||||
|
||||
@@ -945,22 +952,53 @@ namespace Umbraco.Core.Persistence
|
||||
throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", typeThis, typeNext));
|
||||
}
|
||||
|
||||
|
||||
// Instance data used by the Multipoco factory delegate - essentially a list of the nested poco factories to call
|
||||
public class MultiPocoFactory
|
||||
{
|
||||
public List<Delegate> m_Delegates;
|
||||
public Delegate GetItem(int index) { return m_Delegates[index]; }
|
||||
|
||||
public MultiPocoFactory(IEnumerable<Delegate> dels)
|
||||
{
|
||||
Delegates = new List<Delegate>(dels);
|
||||
}
|
||||
private List<Delegate> Delegates { get; set; }
|
||||
private Delegate GetItem(int index) { return Delegates[index]; }
|
||||
|
||||
/// <summary>
|
||||
/// Calls the delegate at the specified index and returns its values
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <returns></returns>
|
||||
private object CallDelegate(int index, IDataReader reader)
|
||||
{
|
||||
var d = GetItem(index);
|
||||
var output = d.DynamicInvoke(reader);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the callback delegate and passes in the output of all delegates as the parameters
|
||||
/// </summary>
|
||||
/// <typeparam name="TRet"></typeparam>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="dr"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public TRet CallCallback<TRet>(Delegate callback, IDataReader dr, int count)
|
||||
{
|
||||
var args = new List<object>();
|
||||
for(var i = 0;i<count;i++)
|
||||
{
|
||||
args.Add(CallDelegate(i, dr));
|
||||
}
|
||||
return (TRet)callback.DynamicInvoke(args.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// Create a multi-poco factory
|
||||
Func<IDataReader, object, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
|
||||
{
|
||||
var m = new DynamicMethod("petapoco_multipoco_factory", typeof(TRet), new Type[] { typeof(MultiPocoFactory), typeof(IDataReader), typeof(object) }, typeof(MultiPocoFactory));
|
||||
var il = m.GetILGenerator();
|
||||
|
||||
// Load the callback
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
Func<IDataReader, Delegate, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
|
||||
{
|
||||
// Call each delegate
|
||||
var dels = new List<Delegate>();
|
||||
int pos = 0;
|
||||
@@ -969,33 +1007,19 @@ namespace Umbraco.Core.Persistence
|
||||
// Add to list of delegates to call
|
||||
var del = FindSplitPoint(types[i], i + 1 < types.Length ? types[i + 1] : null, sql, r, ref pos);
|
||||
dels.Add(del);
|
||||
|
||||
// Get the delegate
|
||||
il.Emit(OpCodes.Ldarg_0); // callback,this
|
||||
il.Emit(OpCodes.Ldc_I4, i); // callback,this,Index
|
||||
il.Emit(OpCodes.Callvirt, typeof(MultiPocoFactory).GetMethod("GetItem")); // callback,Delegate
|
||||
il.Emit(OpCodes.Ldarg_1); // callback,delegate, datareader
|
||||
|
||||
// Call Invoke
|
||||
var tDelInvoke = del.GetType().GetMethod("Invoke");
|
||||
il.Emit(OpCodes.Callvirt, tDelInvoke); // Poco left on stack
|
||||
}
|
||||
|
||||
// By now we should have the callback and the N pocos all on the stack. Call the callback and we're done
|
||||
il.Emit(OpCodes.Callvirt, Expression.GetFuncType(types.Concat(new Type[] { typeof(TRet) }).ToArray()).GetMethod("Invoke"));
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
// Finish up
|
||||
return (Func<IDataReader, object, TRet>)m.CreateDelegate(typeof(Func<IDataReader, object, TRet>), new MultiPocoFactory() { m_Delegates = dels });
|
||||
var mpFactory = new MultiPocoFactory(dels);
|
||||
return (reader, arg3) => mpFactory.CallCallback<TRet>(arg3, reader, types.Length);
|
||||
}
|
||||
|
||||
// Various cached stuff
|
||||
static Dictionary<string, object> MultiPocoFactories = new Dictionary<string, object>();
|
||||
static Dictionary<string, object> AutoMappers = new Dictionary<string, object>();
|
||||
static Dictionary<string, Delegate> AutoMappers = new Dictionary<string, Delegate>();
|
||||
static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
|
||||
|
||||
// Get (or create) the multi-poco factory for a query
|
||||
Func<IDataReader, object, TRet> GetMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
|
||||
// Get (or create) the multi-poco factory for a query
|
||||
Func<IDataReader, Delegate, TRet> GetMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
|
||||
{
|
||||
// Build a key string (this is crap, should address this at some point)
|
||||
var kb = new StringBuilder();
|
||||
@@ -1017,7 +1041,10 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
object oFactory;
|
||||
if (MultiPocoFactories.TryGetValue(key, out oFactory))
|
||||
return (Func<IDataReader, object, TRet>)oFactory;
|
||||
{
|
||||
//mpFactory = oFactory;
|
||||
return (Func<IDataReader, Delegate, TRet>)oFactory;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1029,15 +1056,17 @@ namespace Umbraco.Core.Persistence
|
||||
try
|
||||
{
|
||||
// Check again
|
||||
object oFactory;
|
||||
object oFactory; ;
|
||||
if (MultiPocoFactories.TryGetValue(key, out oFactory))
|
||||
return (Func<IDataReader, object, TRet>)oFactory;
|
||||
{
|
||||
return (Func<IDataReader, Delegate, TRet>)oFactory;
|
||||
}
|
||||
|
||||
// Create the factory
|
||||
var factory = CreateMultiPocoFactory<TRet>(types, sql, r);
|
||||
|
||||
// Create the factory
|
||||
var Factory = CreateMultiPocoFactory<TRet>(types, sql, r);
|
||||
|
||||
MultiPocoFactories.Add(key, Factory);
|
||||
return Factory;
|
||||
MultiPocoFactories.Add(key, factory);
|
||||
return factory;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1047,7 +1076,7 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
|
||||
// Actual implementation of the multi-poco query
|
||||
public IEnumerable<TRet> Query<TRet>(Type[] types, object cb, string sql, params object[] args)
|
||||
public IEnumerable<TRet> Query<TRet>(Type[] types, Delegate cb, string sql, params object[] args)
|
||||
{
|
||||
OpenSharedConnection();
|
||||
try
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -18,6 +20,95 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
internal static event CreateTableEventHandler NewTable;
|
||||
|
||||
/// <summary>
|
||||
/// This will handle the issue of inserting data into a table when there can be a violation of a primary key or unique constraint which
|
||||
/// can occur when two threads are trying to insert data at the exact same time when the data violates this constraint.
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="poco"></param>
|
||||
/// <returns>
|
||||
/// Returns the action that executed, either an insert or an update
|
||||
///
|
||||
/// NOTE: If an insert occurred and a PK value got generated, the poco object passed in will contain the updated value.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// In different databases, there are a few raw SQL options like MySql's ON DUPLICATE KEY UPDATE or MSSQL's MERGE WHEN MATCHED, but since we are
|
||||
/// also supporting SQLCE for which this doesn't exist we cannot simply rely on the underlying database to help us here. So we'll actually need to
|
||||
/// try to be as proficient as possible when we know this can occur and manually handle the issue.
|
||||
///
|
||||
/// We do this by first trying to Update the record, this will return the number of rows affected. If it is zero then we insert, if it is one, then
|
||||
/// we know the update was successful and the row was already inserted by another thread. If the rowcount is zero and we insert and get an exception,
|
||||
/// that's due to a race condition, in which case we need to retry and update.
|
||||
/// </remarks>
|
||||
internal static RecordPersistenceType InsertOrUpdate<T>(this Database db, T poco)
|
||||
where T : class
|
||||
{
|
||||
return db.InsertOrUpdate(poco, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will handle the issue of inserting data into a table when there can be a violation of a primary key or unique constraint which
|
||||
/// can occur when two threads are trying to insert data at the exact same time when the data violates this constraint.
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="poco"></param>
|
||||
/// <param name="updateArgs"></param>
|
||||
/// <param name="updateCommand">If the entity has a composite key they you need to specify the update command explicitly</param>
|
||||
/// <returns>
|
||||
/// Returns the action that executed, either an insert or an update
|
||||
///
|
||||
/// NOTE: If an insert occurred and a PK value got generated, the poco object passed in will contain the updated value.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// In different databases, there are a few raw SQL options like MySql's ON DUPLICATE KEY UPDATE or MSSQL's MERGE WHEN MATCHED, but since we are
|
||||
/// also supporting SQLCE for which this doesn't exist we cannot simply rely on the underlying database to help us here. So we'll actually need to
|
||||
/// try to be as proficient as possible when we know this can occur and manually handle the issue.
|
||||
///
|
||||
/// We do this by first trying to Update the record, this will return the number of rows affected. If it is zero then we insert, if it is one, then
|
||||
/// we know the update was successful and the row was already inserted by another thread. If the rowcount is zero and we insert and get an exception,
|
||||
/// that's due to a race condition, in which case we need to retry and update.
|
||||
/// </remarks>
|
||||
internal static RecordPersistenceType InsertOrUpdate<T>(this Database db,
|
||||
T poco,
|
||||
string updateCommand,
|
||||
object updateArgs)
|
||||
where T : class
|
||||
{
|
||||
if (poco == null) throw new ArgumentNullException("poco");
|
||||
|
||||
var rowCount = updateCommand.IsNullOrWhiteSpace()
|
||||
? db.Update(poco)
|
||||
: db.Update<T>(updateCommand, updateArgs);
|
||||
|
||||
if (rowCount > 0) return RecordPersistenceType.Update;
|
||||
|
||||
try
|
||||
{
|
||||
db.Insert(poco);
|
||||
return RecordPersistenceType.Insert;
|
||||
}
|
||||
//TODO: Need to find out if this is the same exception that will occur for all databases... pretty sure it will be
|
||||
catch (SqlException ex)
|
||||
{
|
||||
//This will occur if the constraint was violated and this record was already inserted by another thread,
|
||||
//at this exact same time, in this case we need to do an update
|
||||
|
||||
rowCount = updateCommand.IsNullOrWhiteSpace()
|
||||
? db.Update(poco)
|
||||
: db.Update<T>(updateCommand, updateArgs);
|
||||
|
||||
if (rowCount == 0)
|
||||
{
|
||||
//this would be strange! in this case the only circumstance would be that at the exact same time, 3 threads executed, one
|
||||
// did the insert and the other somehow managed to do a delete precisely before this update was executed... now that would
|
||||
// be real crazy. In that case we need to throw an exception.
|
||||
throw new DataException("Record could not be inserted or updated");
|
||||
}
|
||||
|
||||
return RecordPersistenceType.Update;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
@@ -24,11 +25,17 @@ namespace Umbraco.Core.Persistence
|
||||
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionHelper<T>();
|
||||
string whereExpression = expresionist.Visit(predicate);
|
||||
|
||||
var whereExpression = expresionist.Visit(predicate);
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
public static Sql WhereIn<T>(this Sql sql, Expression<Func<T, object>> fieldSelector, IEnumerable values)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionHelper<T>();
|
||||
var fieldExpression = expresionist.Visit(fieldSelector);
|
||||
return sql.Where(fieldExpression + " IN (@values)", new {@values = values});
|
||||
}
|
||||
|
||||
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember)
|
||||
{
|
||||
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@@ -26,13 +27,17 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
m.Expression.NodeType == ExpressionType.Parameter
|
||||
&& m.Expression.Type == typeof(T))
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name);
|
||||
return field;
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert)
|
||||
{
|
||||
var field = _mapper.Map(m.Member.Name);
|
||||
var field = _mapper.Map(m.Member.Name, true);
|
||||
if (field.IsNullOrWhiteSpace())
|
||||
throw new InvalidOperationException("The mapper returned an empty field for the member name: " + m.Member.Name);
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
internal enum RecordPersistenceType
|
||||
{
|
||||
Insert,
|
||||
Update,
|
||||
Delete
|
||||
}
|
||||
}
|
||||
@@ -60,13 +60,20 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
//NOTE: Not implemented because all ContentPreviewEntity will always return false for having an Identity
|
||||
protected override void PersistUpdatedItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void PersistNewItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot insert a preview for a content item that has no identity");
|
||||
throw new InvalidOperationException("Cannot insert or update a preview for a content item that has no identity");
|
||||
}
|
||||
|
||||
var previewPoco = new PreviewXmlDto
|
||||
@@ -77,33 +84,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Xml = entity.Xml.ToString(SaveOptions.None)
|
||||
};
|
||||
|
||||
Database.Insert(previewPoco);
|
||||
//We need to do a special InsertOrUpdate here because we know that the PreviewXmlDto table has a composite key and thus
|
||||
// a unique constraint which can be violated if 2+ threads try to execute the same insert sql at the same time.
|
||||
Database.InsertOrUpdate(previewPoco,
|
||||
//Since the table has a composite key, we need to specify an explit update statement
|
||||
"SET xml = @Xml, timestamp = @Timestamp WHERE nodeId=@NodeId AND versionId=@VersionId",
|
||||
new {NodeId = previewPoco.NodeId, VersionId = previewPoco.VersionId, Xml = previewPoco.Xml, Timestamp = previewPoco.Timestamp});
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update a preview for a content item that has no identity");
|
||||
}
|
||||
|
||||
var previewPoco = new PreviewXmlDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Timestamp = DateTime.Now,
|
||||
VersionId = entity.Version,
|
||||
Xml = entity.Xml.ToString(SaveOptions.None)
|
||||
};
|
||||
|
||||
Database.Update<PreviewXmlDto>(
|
||||
"SET xml = @Xml, timestamp = @Timestamp WHERE nodeId = @Id AND versionId = @Version",
|
||||
new
|
||||
{
|
||||
Xml = previewPoco.Xml,
|
||||
Timestamp = previewPoco.Timestamp,
|
||||
Id = previewPoco.NodeId,
|
||||
Version = previewPoco.VersionId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,6 +631,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
repo.ReplaceEntityPermissions(permissionSet);
|
||||
}
|
||||
|
||||
public void ClearPublished(IContent content)
|
||||
{
|
||||
// race cond!
|
||||
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
|
||||
foreach (var documentDto in documentDtos)
|
||||
{
|
||||
documentDto.Published = false;
|
||||
Database.Update(documentDto);
|
||||
}
|
||||
}
|
||||
|
||||
public IContent GetByLanguage(int id, string language)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
@@ -670,10 +681,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
var contentExists = Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @Id", new { Id = content.Id }) != 0;
|
||||
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(contentExists, content, xml));
|
||||
{
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(content, xml));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -692,11 +701,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
var previewExists =
|
||||
Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId = @Id AND versionId = @Version",
|
||||
new { Id = content.Id, Version = content.Version }) != 0;
|
||||
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(previewExists, content, xml));
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(content, xml));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -728,7 +733,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
Func<Tuple<string, object[]>> filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString(), args.ToArray());
|
||||
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, orderBy, orderDirection,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -297,9 +298,38 @@ AND umbracoNode.id <> @id",
|
||||
//Delete Tabs/Groups by excepting entries from db with entries from collections
|
||||
var dbPropertyGroups =
|
||||
Database.Fetch<PropertyTypeGroupDto>("WHERE contenttypeNodeId = @Id", new {Id = entity.Id})
|
||||
.Select(x => new Tuple<int, string>(x.Id, x.Text));
|
||||
var entityPropertyGroups = entity.PropertyGroups.Select(x => new Tuple<int, string>(x.Id, x.Name));
|
||||
var tabs = dbPropertyGroups.Except(entityPropertyGroups);
|
||||
.Select(x => new Tuple<int, string>(x.Id, x.Text))
|
||||
.ToList();
|
||||
var entityPropertyGroups = entity.PropertyGroups.Select(x => new Tuple<int, string>(x.Id, x.Name)).ToList();
|
||||
var tabsToDelete = dbPropertyGroups.Select(x => x.Item1).Except(entityPropertyGroups.Select(x => x.Item1));
|
||||
var tabs = dbPropertyGroups.Where(x => tabsToDelete.Any(y => y == x.Item1));
|
||||
//Update Tab name downstream to ensure renaming is done properly
|
||||
foreach (var propertyGroup in entityPropertyGroups)
|
||||
{
|
||||
Database.Update<PropertyTypeGroupDto>("SET Text = @TabName WHERE parentGroupId = @TabId",
|
||||
new { TabName = propertyGroup.Item2, TabId = propertyGroup.Item1 });
|
||||
|
||||
var childGroups = Database.Fetch<PropertyTypeGroupDto>("WHERE parentGroupId = @TabId", new { TabId = propertyGroup.Item1 });
|
||||
foreach (var childGroup in childGroups)
|
||||
{
|
||||
var sibling = Database.Fetch<PropertyTypeGroupDto>("WHERE contenttypeNodeId = @Id AND text = @Name",
|
||||
new { Id = childGroup.ContentTypeNodeId, Name = propertyGroup.Item2 })
|
||||
.FirstOrDefault(x => x.ParentGroupId.HasValue == false || x.ParentGroupId.Value.Equals(propertyGroup.Item1) == false);
|
||||
//If the child group doesn't have a sibling there is no chance of duplicates and we continue
|
||||
if (sibling == null || (sibling.ParentGroupId.HasValue && sibling.ParentGroupId.Value.Equals(propertyGroup.Item1))) continue;
|
||||
|
||||
//Since the child group has a sibling with the same name we need to point any PropertyTypes to the sibling
|
||||
//as this child group is about to leave the party.
|
||||
Database.Update<PropertyTypeDto>(
|
||||
"SET propertyTypeGroupId = @PropertyTypeGroupId WHERE propertyTypeGroupId = @PropertyGroupId AND ContentTypeId = @ContentTypeId",
|
||||
new { PropertyTypeGroupId = sibling.Id, PropertyGroupId = childGroup.Id, ContentTypeId = childGroup.ContentTypeNodeId });
|
||||
|
||||
//Since the parent group has been renamed and we have duplicates we remove this group
|
||||
//and leave our sibling in charge of the part.
|
||||
Database.Delete(childGroup);
|
||||
}
|
||||
}
|
||||
//Do Tab updates
|
||||
foreach (var tab in tabs)
|
||||
{
|
||||
Database.Update<PropertyTypeDto>("SET propertyTypeGroupId = NULL WHERE propertyTypeGroupId = @PropertyGroupId",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user