Compare commits
487 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ffb73e467 | |||
| 57c3665d0f | |||
| 8037f929aa | |||
| fec3e57ed6 | |||
| 5106ffab97 | |||
| 9f569171c3 | |||
| d607041ca4 | |||
| b3daf7f55d | |||
| a77d3bc4ac | |||
| c4aa87d73d | |||
| 57909489cb | |||
| c78b371965 | |||
| c525be3e44 | |||
| cc804bd3bd | |||
| 24dc8309f1 | |||
| ce81f9b1b1 | |||
| 3f70761d49 | |||
| dee45146a4 | |||
| b35397b464 | |||
| 147cc92ac5 | |||
| 3230fce76a | |||
| 1d22c1e81e | |||
| ece2e2359e | |||
| 8cbe53f49d | |||
| cac3ddc3c8 | |||
| 0eac50ceba | |||
| d5e735ac14 | |||
| 8d841d3537 | |||
| 5f175874b2 | |||
| fa053426c3 | |||
| 437e7f0ffc | |||
| afa6c35bb5 | |||
| 8d00dbf648 | |||
| cd26a1ad21 | |||
| 437ab4aaf1 | |||
| 051e9366e5 | |||
| 46c6503320 | |||
| 1cf2fc7870 | |||
| cf1c38dcaa | |||
| 73d7cd4856 | |||
| 1b87fd074c | |||
| 33f21e0528 | |||
| 0b5a2452c2 | |||
| cc8962f090 | |||
| f6b9fdba72 | |||
| e0effec3b4 | |||
| 76a2f433d9 | |||
| a2bfca4d3a | |||
| fb24becd0d | |||
| 58857c5d83 | |||
| 7a31d6cae3 | |||
| 021bb6d367 | |||
| 0742108ec1 | |||
| 1c3a7b95fc | |||
| fda72b2acc | |||
| ec742d1f66 | |||
| 4bdac1475f | |||
| 58ce04e26b | |||
| 4c3de920c6 | |||
| be37026587 | |||
| a73b7a5849 | |||
| b7436dc55f | |||
| bc068b201d | |||
| 912b01c9aa | |||
| a82035061c | |||
| 0c107fd869 | |||
| 6419f301f7 | |||
| e775471013 | |||
| cc109d1ed0 | |||
| 9dfccd86fd | |||
| ffaf1f96b9 | |||
| aa3c1d645b | |||
| c6b393f2e8 | |||
| 344d909e6b | |||
| e3925f18ef | |||
| 53a4d063e0 | |||
| 907e5e7ca4 | |||
| 94e281e951 | |||
| 03b60d4923 | |||
| 27e96d6d5b | |||
| d2b4345773 | |||
| c5bbf97921 | |||
| 8c4eba7837 | |||
| 08a2ec6883 | |||
| b02e0c0232 | |||
| fff0151b62 | |||
| 90fdbb007e | |||
| 19e4fe5059 | |||
| 311d503d8b | |||
| f0fc3313e4 | |||
| dc7cb08824 | |||
| 5ddcf42bc3 | |||
| af112f6b20 | |||
| cbef51eaf0 | |||
| 85ba41dd06 | |||
| e5cf7b089c | |||
| 5fae61b196 | |||
| db5c204d8c | |||
| b9082cf390 | |||
| 9469b0b844 | |||
| 5737cdfbe2 | |||
| 1a082464e9 | |||
| e004ad31b5 | |||
| 6cf25f0043 | |||
| c0f50a4c1b | |||
| 00fbc6dfcb | |||
| 3a9ad91f8f | |||
| c2cfd26ee6 | |||
| c5f26c7c15 | |||
| f742c0a958 | |||
| 8cefe22bee | |||
| fad3599f50 | |||
| 37b219f6e1 | |||
| 4e9667a067 | |||
| 300a46f1b2 | |||
| 9aa5df43bf | |||
| 02b0e4dd6a | |||
| 9c1d3fabb8 | |||
| e9e84a7faa | |||
| 137d8553de | |||
| 7210c6cd95 | |||
| 6856f9f341 | |||
| d24ef3650e | |||
| 2feef247ed | |||
| 4974e2a2ce | |||
| 3eb0dac48a | |||
| a44b19da18 | |||
| 3ee4234968 | |||
| f06a468fb4 | |||
| 420c334af4 | |||
| 20b21c86ea | |||
| d2083673be | |||
| fe40b8fe99 | |||
| dd477a0cbe | |||
| 2834ccdc2b | |||
| b3f6f48834 | |||
| 4544520b66 | |||
| 017663833a | |||
| 9a5923771d | |||
| 0932c980e9 | |||
| 7e9261d4bc | |||
| 0ce503b369 | |||
| 6d5d8fb39c | |||
| 9613772036 | |||
| 604779667d | |||
| 46212904ef | |||
| b7bb98d824 | |||
| f31d2ca7cf | |||
| 88ae95150e | |||
| 97a085d12b | |||
| 74d0ec949d | |||
| 9a042fbbdf | |||
| 834b780d8e | |||
| 8905878a87 | |||
| 36bed40db0 | |||
| f708a24401 | |||
| d32cf12c6f | |||
| 100485f748 | |||
| 56017a340c | |||
| 21c70462c9 | |||
| 9b254f8bf9 | |||
| 6dd63e8a01 | |||
| bac940d4ed | |||
| c1c090faab | |||
| 32bedfaa6a | |||
| b003c027e5 | |||
| 0295aaba3b | |||
| bb04f83a49 | |||
| 793ba42f9e | |||
| ded1def8e2 | |||
| 5a2a3c3b1e | |||
| 6f2fa2ed4d | |||
| 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 | |||
| 9c8e6ab864 | |||
| 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 | |||
| fec70fcf47 | |||
| 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 | |||
| 6555fd5791 | |||
| 71ca90d74b | |||
| fb42472f31 | |||
| b6695b6953 | |||
| 8820fa1fdf |
+5
-15
@@ -23,14 +23,15 @@ 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 Remove -Name MyGetUmbracoCore >NUL
|
||||
..\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
|
||||
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
|
||||
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
|
||||
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
|
||||
|
||||
ECHO Removing existing built files to make sure everything is clean as a whistle
|
||||
RMDIR /Q /S _BuildOutput
|
||||
@@ -44,18 +45,8 @@ 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
|
||||
SET dummytext=This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete.
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Code\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Data\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\App_Plugins\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\css\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\masterpages\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\media\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\scripts\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\usercontrols\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\Views\Partials\dummy.txt
|
||||
ECHO %dummytext% > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt
|
||||
ECHO 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\MacroScripts\Web.config Web.config.transform
|
||||
@@ -65,7 +56,6 @@ 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%
|
||||
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version%
|
||||
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoExamine.PDF.nuspec
|
||||
|
||||
IF ERRORLEVEL 1 GOTO :showerror
|
||||
|
||||
|
||||
+90
-113
@@ -1,4 +1,3 @@
|
||||
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
@@ -14,18 +13,18 @@
|
||||
|
||||
<Import Project="..\tools\UmbracoMSBuildTasks\Umbraco.MSBuild.Tasks.Targets" />
|
||||
<Import Project="..\tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
|
||||
|
||||
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
|
||||
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
<ParameterGroup>
|
||||
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
||||
<OutputFile ParameterType="System.String" Required="true" />
|
||||
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
||||
<OutputFile ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Using Namespace="System.IO" />
|
||||
<Using Namespace="System.Linq" />
|
||||
<Using Namespace="System.Security.Cryptography" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
<Using Namespace="System.IO" />
|
||||
<Using Namespace="System.Linq" />
|
||||
<Using Namespace="System.Security.Cryptography" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
foreach (var item in InputFiles)
|
||||
@@ -48,11 +47,11 @@
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</Code>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
</UsingTask>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
****************************************************
|
||||
VARIABLES
|
||||
@@ -76,23 +75,19 @@
|
||||
<BuildZipFileName>UmbracoCms$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileName>
|
||||
<BuildZipFileNameBin>UmbracoCms.AllBinaries$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameBin>
|
||||
<BuildZipFileNameWebPi>UmbracoCms.WebPI$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameWebPi>
|
||||
<BuildZipExaminePDFName>UmbracoExamine.PDF.0.6.0.zip</BuildZipExaminePDFName>
|
||||
<IncludeSymbols>False</IncludeSymbols>
|
||||
<BuildFolderRelativeToProjects>..\..\build\$(BuildFolder)</BuildFolderRelativeToProjects>
|
||||
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
|
||||
<SolutionBinFolder>$(BuildFolder)bin\</SolutionBinFolder>
|
||||
<WebAppFolder>$(BuildFolder)WebApp\</WebAppFolder>
|
||||
<WebPiFolder>$(BuildFolder)WebPi\</WebPiFolder>
|
||||
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
|
||||
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
|
||||
<SolutionBinFolderRelativeToProjects>$(BuildFolderRelativeToProjects)bin\</SolutionBinFolderRelativeToProjects>
|
||||
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)bin\</SolutionBinFolderAbsolutePath>
|
||||
<WebAppFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebApp\</WebAppFolderRelativeToProjects>
|
||||
<WebAppFolderAbsolutePath>$(BuildFolderAbsolutePath)WebApp\</WebAppFolderAbsolutePath>
|
||||
<WebPiFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebPi\</WebPiFolderRelativeToProjects>
|
||||
<WebPiFolderAbsolutePath>$(BuildFolderAbsolutePath)WebPi\</WebPiFolderAbsolutePath>
|
||||
<ExaminePDFPath>$(BuildFolderAbsolutePath)UmbracoExamine.PDF\</ExaminePDFPath>
|
||||
<ExamineAzurePath>$(BuildFolderAbsolutePath)UmbracoExamine.Azure\</ExamineAzurePath>
|
||||
<ExaminePDFAzurePath>$(BuildFolderAbsolutePath)UmbracoExamine.PDF.Azure\</ExaminePDFAzurePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -114,16 +109,16 @@
|
||||
*****************************************************
|
||||
-->
|
||||
|
||||
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
|
||||
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
|
||||
<Message Text="Build finished" />
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
|
||||
<ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
|
||||
<ItemGroup>
|
||||
<WebPiFile Include="$(BuildZipFileNameWebPi)" />
|
||||
</ItemGroup>
|
||||
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
|
||||
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
|
||||
</ItemGroup>
|
||||
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
|
||||
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanUp" DependsOnTargets="ZipWebPiApp">
|
||||
@@ -153,7 +148,7 @@
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameWebPi)%22 %22$(WebPiFolderAbsolutePath)*%22" />
|
||||
</Target>
|
||||
|
||||
<Target Name="ZipWebApp" DependsOnTargets="ZipUmbracoExaminePDF" >
|
||||
<Target Name="ZipWebApp" DependsOnTargets="CreateSystemFolders" >
|
||||
<Message Text="Starting to zip to $(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22 -x!dotLess.Core.dll" />
|
||||
@@ -162,14 +157,6 @@
|
||||
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="ZipUmbracoExaminePDF" DependsOnTargets="CreateSystemFolders" >
|
||||
<Message Text="Starting to zip to $(buildDate)-$(BuildZipExaminePDFName)" Importance="high" />
|
||||
|
||||
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipExaminePDFName)%22 %22$(ExaminePDFPath)*%22" />
|
||||
|
||||
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipExaminePDFName)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CreateSystemFolders" DependsOnTargets="CopyBelleBuild" Inputs="@(SystemFolders)" Outputs="%(Identity).Dummy">
|
||||
<MakeDir Directories="@(SystemFolders)" />
|
||||
</Target>
|
||||
@@ -177,14 +164,14 @@
|
||||
<Target Name="CopyBelleBuild" DependsOnTargets="CopyLibraries" >
|
||||
<ItemGroup>
|
||||
<BelleFiles Include="..\src\Umbraco.Web.UI.Client\build\belle\**\*.*" Exclude="..\src\Umbraco.Web.UI.Client\build\belle\index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(BelleFiles)"
|
||||
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(BelleFiles)"
|
||||
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
|
||||
<Target Name="CopyLibraries" DependsOnTargets="OffsetTimestamps" >
|
||||
<!-- Copy SQL CE -->
|
||||
<ItemGroup>
|
||||
@@ -217,7 +204,7 @@
|
||||
</Target>
|
||||
|
||||
<!-- Copy the transformed web.config file to the root -->
|
||||
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
|
||||
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
|
||||
<ItemGroup>
|
||||
<WebConfigFile Include="..\src\Umbraco.Web.UI\web.$(BuildConfiguration).Config.transformed" />
|
||||
</ItemGroup>
|
||||
@@ -226,24 +213,24 @@
|
||||
DestinationFiles="$(WebAppFolder)Web.config"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
<!-- Copy the config files and rename them to *.config.transform -->
|
||||
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
|
||||
</Target>
|
||||
|
||||
<!-- Copy the config files and rename them to *.config.transform -->
|
||||
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
|
||||
<ItemGroup>
|
||||
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
|
||||
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(ConfigFiles)"
|
||||
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
<Copy SourceFiles="@(WebConfigTransformFile)"
|
||||
DestinationFiles="$(ConfigsFolder)Web.config.transform"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
|
||||
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(ConfigFiles)"
|
||||
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
|
||||
<Copy SourceFiles="@(WebConfigTransformFile)"
|
||||
DestinationFiles="$(ConfigsFolder)Web.config.transform"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
<!-- Copy the xml documentation to the bin folder -->
|
||||
@@ -261,78 +248,68 @@
|
||||
|
||||
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
|
||||
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
|
||||
<ItemGroup>
|
||||
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
|
||||
<ItemGroup>
|
||||
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(PresentationFolderToDelete)" />
|
||||
<RemoveDir Directories="@(PresentationFolderToDelete)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CompileProjects" DependsOnTargets="CompileExternalExamineProjects">
|
||||
|
||||
<Target Name="CompileProjects" DependsOnTargets="SetVersionNumber">
|
||||
|
||||
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
|
||||
<!-- For UseWPP_CopyWebApplication=True see http://stackoverflow.com/questions/1983575/copywebapplication-with-web-config-transformations -->
|
||||
<!-- Build the Umbraco.Web.UI project -->
|
||||
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath)" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False">
|
||||
</MSBuild>
|
||||
|
||||
|
||||
<!-- DONE -->
|
||||
<Message Text="Finished compiling projects" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CompileExternalExamineProjects" DependsOnTargets="SetVersionNumber">
|
||||
<Message Text="Building externally shipped examine projects" Importance="high" />
|
||||
|
||||
<!-- First remove old files from the examine builds -->
|
||||
<ItemGroup>
|
||||
<FilesToDeleteBeforeBuild Include="$(ExaminePDFPath)*;$(ExamineAzurePath)*"/>
|
||||
</ItemGroup>
|
||||
<Delete Files="@(FilesToDeleteBeforeBuild)" />
|
||||
<!--
|
||||
Build the UmbracoExamine.PDF.Azure library ... this builds the 3 projects that we want to bundle separately so no need to build each one separately .
|
||||
We'll just build this one and then manually copy the DLLs to where we want them.
|
||||
-->
|
||||
<MSBuild Projects="..\src\UmbracoExamine.PDF.Azure\UmbracoExamine.PDF.Azure.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(ExaminePDFAzurePath);" Targets="Clean;Build;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False">
|
||||
</MSBuild>
|
||||
<ItemGroup>
|
||||
<ExaminePDFFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.pdf;$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.xml;$(ExaminePDFAzurePath)**\itextsharp.dll" />
|
||||
<ExamineAzureFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.pdb;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.xml" />
|
||||
<ExaminePDFAzureFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.pdb;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.xml" />
|
||||
</ItemGroup>
|
||||
<!-- Now copy the built files to their correct deployment folders-->
|
||||
<Copy SourceFiles="@(ExaminePDFFiles)"
|
||||
DestinationFolder="$(ExaminePDFPath)"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
<Copy SourceFiles="@(ExamineAzureFiles)"
|
||||
DestinationFolder="$(ExamineAzurePath)"
|
||||
OverwriteReadOnlyFiles="true"
|
||||
SkipUnchangedFiles="false" />
|
||||
<!-- Then just delete the files we don't want to ship in the UmbracoExamine.Azure.PDF folder -->
|
||||
<ItemGroup>
|
||||
<FilesToDeleteAfterBuild Include="$(ExaminePDFAzurePath)*" Exclude="$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.dll;$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.pdb;$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.xml"/>
|
||||
</ItemGroup>
|
||||
<Delete Files="@(FilesToDeleteAfterBuild)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
|
||||
<PropertyGroup>
|
||||
<NewVersion>$(BUILD_RELEASE)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Match & replace 3 and 4 digit version numbers and -beta (if it's there) -->
|
||||
<PropertyGroup>
|
||||
<NewVersion>$(BUILD_RELEASE)</NewVersion>
|
||||
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Match & replace 3 and 4 digit version numbers and -beta (if it's there) -->
|
||||
<FileUpdate
|
||||
Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Regex="(\d+)\.(\d+)\.(\d+)(.(\d+))?"
|
||||
ReplacementText="$(BUILD_RELEASE)"/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Regex="CurrentComment { get { return "([a-zA-Z]+)?""
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
|
||||
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyFileVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyFileVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the copyright year-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyCopyright\("Copyright © Umbraco (\d{4})"\)"
|
||||
ReplacementText="AssemblyCopyright("Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))")"/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,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://opensource.org/licenses/MIT</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://opensource.org/licenses/MIT</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.30506.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.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, 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\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://opensource.org/licenses/MIT</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.3.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.62, 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,45 +1,40 @@
|
||||
<?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://opensource.org/licenses/MIT</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\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
|
||||
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
|
||||
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
|
||||
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.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://opensource.org/licenses/MIT</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>
|
||||
@@ -1,18 +1,18 @@
|
||||
A note about running Umbraco from Visual Studio.
|
||||
|
||||
_ _ __ __ ____ _____ _____ ____
|
||||
| | | | \/ | _ \| __ \ /\ / ____/ __ \
|
||||
| | | | \ / | |_) | |__) | / \ | | | | | |
|
||||
| | | | |\/| | _ <| _ / / /\ \| | | | | |
|
||||
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
|
||||
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
Don't forget to build!
|
||||
|
||||
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 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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
_ _ __ __ ____ _____ _____ ____
|
||||
| | | | \/ | _ \| __ \ /\ / ____/ __ \
|
||||
| | | | \ / | |_) | |__) | / \ | | | | | |
|
||||
| | | | |\/| | _ <| _ / / /\ \| | | | | |
|
||||
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
|
||||
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
Don't forget to build!
|
||||
|
||||
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 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.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
- Umbraco
|
||||
@@ -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 }
|
||||
@@ -64,7 +63,6 @@ if ($project) {
|
||||
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
|
||||
|
||||
@@ -47,108 +47,46 @@ if ($project) {
|
||||
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
|
||||
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
|
||||
}
|
||||
|
||||
$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..
|
||||
$dte2 = Get-Interface $dte ([EnvDTE80.DTE2])
|
||||
|
||||
if ($dte2.ActiveWindow.Caption -eq "Package Manager Console")
|
||||
{
|
||||
# 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
|
||||
|
||||
$consoleWindow = $(Get-VSComponentModel).GetService([NuGetConsole.IPowerConsoleWindow])
|
||||
|
||||
$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 }
|
||||
|
||||
$hostInfo = $prop.GetValue($consoleWindow)
|
||||
if ($hostInfo -eq $null) { return }
|
||||
|
||||
$history = $hostInfo.WpfConsole.InputHistory.History
|
||||
|
||||
$lastCommand = $history | select -last 1
|
||||
|
||||
if ($lastCommand)
|
||||
{
|
||||
$lastCommand = $lastCommand.Trim().ToLower()
|
||||
if ($lastCommand.StartsWith("install-package") -and $lastCommand.Contains("umbracocms"))
|
||||
{
|
||||
$copyWebconfig = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# 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 }
|
||||
|
||||
$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 }
|
||||
|
||||
$messages = $messagesField.GetValue($console)
|
||||
if ($messages -eq $null) { return }
|
||||
|
||||
$operations = $messages -split "=============================="
|
||||
|
||||
$lastOperation = $operations | select -last 1
|
||||
|
||||
if ($lastOperation)
|
||||
{
|
||||
$lastOperation = $lastOperation.ToLower()
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
$copyWebconfig = $true
|
||||
$destinationWebConfig = Join-Path $projectDestinationPath "Web.config"
|
||||
|
||||
if(Test-Path $destinationWebConfig)
|
||||
{
|
||||
Try
|
||||
{
|
||||
[xml]$config = Get-Content $destinationWebConfig
|
||||
|
||||
$config.configuration.appSettings.ChildNodes | ForEach-Object {
|
||||
if($_.key -eq "umbracoConfigurationStatus")
|
||||
{
|
||||
# The web.config has an umbraco-specific appSetting in it
|
||||
# don't overwrite it and let config transforms do their thing
|
||||
$copyWebconfig = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
}
|
||||
|
||||
if($copyWebconfig -eq $true)
|
||||
{
|
||||
$packageWebConfigSource = Join-Path $rootPath "UmbracoFiles\Web.config"
|
||||
$destinationWebConfig = Join-Path $projectDestinationPath "Web.config"
|
||||
Copy-Item $packageWebConfigSource $destinationWebConfig -Force
|
||||
}
|
||||
}
|
||||
|
||||
$installFolder = Join-Path $projectDestinationPath "Install"
|
||||
if(Test-Path $installFolder) {
|
||||
Remove-Item $installFolder -Force -Recurse -Confirm:$false
|
||||
}
|
||||
|
||||
# Open readme.txt file
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
|
||||
# Open appropriate readme
|
||||
if($copyWebconfig -eq $true)
|
||||
{
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
|
||||
}
|
||||
else
|
||||
{
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\ReadmeUpgrade.txt')
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.2.0
|
||||
7.2.5
|
||||
RC
|
||||
@@ -1,13 +0,0 @@
|
||||
Umbraco CMS version 4.5 is licensed under the OSI approved MIT License:
|
||||
|
||||
Copyright (c) 2007-2010 umbraco I/S.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Umbraco versions prior to 4.5 were licensed under a combination of MIT and a propriatary UI license. The license history can be found here:
|
||||
http://umbraco.codeplex.com/license
|
||||
@@ -1,33 +0,0 @@
|
||||
Umbraco - the simple, flexible and friendly ASP.NET CMS
|
||||
=======================================================
|
||||
For the first time on the Microsoft platform a free user and developer friendly cms that makes it quick and easy to create websites - or a breeze to build complex web applications. umbraco got award-winning integration capabilities and supports your ASP.NET User and Custom Controls out of the box. It's a developers dream and your users will love it too. Used by more than 57.000 active websites including Heinz.com, Peugeot.com, NAIAS.com and Microsofts documentinteropinitiative.org website you can be sure that the technology is proven, stable and scales.
|
||||
|
||||
More info at http://umbraco.com
|
||||
|
||||
Exploring the repository
|
||||
========================
|
||||
Most contributors will work on their own fork and should pick the branch in which they need their fix. For more information see:
|
||||
http://our.umbraco.org/contribute/guidelines-for-core-contribution
|
||||
|
||||
Exploring the source
|
||||
====================
|
||||
The Umbraco source code is never required for you to start building sites. If you are not using the source code for debugging purposes or to contribute to the source then please download the binaries listed at http://umbraco.codeplex.com
|
||||
|
||||
With that said, the Umbraco solution can be opened in Visual Studio 2010 with SP1 installed or Visual Studio 2012.
|
||||
|
||||
Contributing
|
||||
============
|
||||
Umbraco is Open Source which means you can contribute to make it great!
|
||||
Read all about it at http://our.umbraco.org/contribute
|
||||
|
||||
Forums
|
||||
======
|
||||
We have a forum running on http://our.umbraco.org for friendly community support.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
Our documentation section provides help on installing and using Umbraco including API reference documentation: http://our.umbraco.org/documentation
|
||||
|
||||
Submitting Issues
|
||||
=================
|
||||
Another way you can contribute to Umbraco is by providing issue reports, for information on how to submit an issue report refer to our online guide at http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature
|
||||
+6
-3
@@ -1,8 +1,8 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2012")]
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -10,3 +10,6 @@ using System.Resources;
|
||||
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.2.5")]
|
||||
[assembly: AssemblyInformationalVersion("7.2.5-RC")]
|
||||
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
|
||||
//
|
||||
// notes:
|
||||
// - this is NOT a reader/writer lock
|
||||
// - this is NOT a recursive lock
|
||||
//
|
||||
internal class AsyncLock
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
private readonly Semaphore _semaphore2;
|
||||
private readonly IDisposable _releaser;
|
||||
private readonly Task<IDisposable> _releaserTask;
|
||||
|
||||
public AsyncLock()
|
||||
: this (null)
|
||||
{ }
|
||||
|
||||
public AsyncLock(string name)
|
||||
{
|
||||
// WaitOne() waits until count > 0 then decrements count
|
||||
// Release() increments count
|
||||
// initial count: the initial count value
|
||||
// maximum count: the max value of count, and then Release() throws
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
// anonymous semaphore
|
||||
// use one unique releaser, that will not release the semaphore when finalized
|
||||
// because the semaphore is destroyed anyway if the app goes down
|
||||
|
||||
_semaphore = new SemaphoreSlim(1, 1); // create a local (to the app domain) semaphore
|
||||
_releaser = new SemaphoreSlimReleaser(_semaphore);
|
||||
_releaserTask = Task.FromResult(_releaser);
|
||||
}
|
||||
else
|
||||
{
|
||||
// named semaphore
|
||||
// use dedicated releasers, that will release the semaphore when finalized
|
||||
// because the semaphore is system-wide and we cannot leak counts
|
||||
|
||||
_semaphore2 = new Semaphore(1, 1, name); // create a system-wide named semaphore
|
||||
}
|
||||
}
|
||||
|
||||
private IDisposable CreateReleaser()
|
||||
{
|
||||
// for anonymous semaphore, use the unique releaser, else create a new one
|
||||
return _semaphore != null
|
||||
? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore)
|
||||
: (IDisposable)new NamedSemaphoreReleaser(_semaphore2);
|
||||
}
|
||||
|
||||
public Task<IDisposable> LockAsync()
|
||||
{
|
||||
var wait = _semaphore != null
|
||||
? _semaphore.WaitAsync()
|
||||
: WaitOneAsync(_semaphore2);
|
||||
|
||||
return wait.IsCompleted
|
||||
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
|
||||
: wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
|
||||
this, CancellationToken.None,
|
||||
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public IDisposable Lock()
|
||||
{
|
||||
if (_semaphore != null)
|
||||
_semaphore.Wait();
|
||||
else
|
||||
_semaphore2.WaitOne();
|
||||
return _releaser ?? CreateReleaser(); // anonymous vs named
|
||||
}
|
||||
|
||||
public IDisposable Lock(int millisecondsTimeout)
|
||||
{
|
||||
var entered = _semaphore != null
|
||||
? _semaphore.Wait(millisecondsTimeout)
|
||||
: _semaphore2.WaitOne(millisecondsTimeout);
|
||||
if (entered == false)
|
||||
throw new TimeoutException("Failed to enter the lock within timeout.");
|
||||
return _releaser ?? CreateReleaser(); // anonymous vs named
|
||||
}
|
||||
|
||||
// note - before making those classes some structs, read
|
||||
// about "impure methods" and mutating readonly structs...
|
||||
|
||||
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
|
||||
{
|
||||
private readonly Semaphore _semaphore;
|
||||
|
||||
internal NamedSemaphoreReleaser(Semaphore semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// critical
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
// we WANT to release the semaphore because it's a system object
|
||||
// ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
|
||||
// which means that the finalizer "should" run in all situations
|
||||
|
||||
// however... that can fail with System.ObjectDisposedException because the
|
||||
// underlying handle was closed... because we cannot guarantee that the semaphore
|
||||
// is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
|
||||
// which should keep it around and then we free the handle?
|
||||
|
||||
// so... I'm not sure this is safe really...
|
||||
|
||||
~NamedSemaphoreReleaser()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
|
||||
private class SemaphoreSlimReleaser : IDisposable
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
|
||||
internal SemaphoreSlimReleaser(SemaphoreSlim semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// normal
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
~SemaphoreSlimReleaser()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
|
||||
// http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
|
||||
// F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
|
||||
// version below should be OK
|
||||
|
||||
private static Task WaitOneAsync(WaitHandle handle)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var callbackHandleInitLock = new object();
|
||||
lock (callbackHandleInitLock)
|
||||
{
|
||||
RegisteredWaitHandle callbackHandle = null;
|
||||
// ReSharper disable once RedundantAssignment
|
||||
callbackHandle = ThreadPool.RegisterWaitForSingleObject(
|
||||
handle,
|
||||
(state, timedOut) =>
|
||||
{
|
||||
tcs.SetResult(null);
|
||||
|
||||
// we take a lock here to make sure the outer method has completed setting the local variable callbackHandle.
|
||||
lock (callbackHandleInitLock)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
callbackHandle.Unregister(null);
|
||||
}
|
||||
},
|
||||
/*state:*/ null,
|
||||
/*millisecondsTimeOutInterval:*/ Timeout.Infinite,
|
||||
/*executeOnlyOnce:*/ true);
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Extensions for strongly typed access
|
||||
/// </summary>
|
||||
internal static class CacheProviderExtensions
|
||||
public static class CacheProviderExtensions
|
||||
{
|
||||
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
string cacheKey,
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
bool InternalRedirectPreservesTemplate { get; }
|
||||
|
||||
bool DisableAlternativeTemplates { get; }
|
||||
|
||||
bool DisableFindContentByIdPath { get; }
|
||||
|
||||
string UrlProviderMode { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,17 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return (bool) base["internalRedirectPreservesTemplate"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("disableAlternativeTemplates", DefaultValue = "false")]
|
||||
public bool DisableAlternativeTemplates
|
||||
{
|
||||
get { return (bool) base["disableAlternativeTemplates"]; }
|
||||
}
|
||||
[ConfigurationProperty("disableFindContentByIdPath", DefaultValue = "false")]
|
||||
public bool DisableFindContentByIdPath
|
||||
{
|
||||
get { return (bool) base["disableFindContentByIdPath"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
|
||||
public string UrlProviderMode
|
||||
{
|
||||
|
||||
@@ -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.5");
|
||||
|
||||
/// <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 ""; } }
|
||||
public static string CurrentComment { get { return "RC"; } }
|
||||
|
||||
// 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
|
||||
|
||||
@@ -192,73 +192,64 @@ namespace Umbraco.Core
|
||||
{
|
||||
{
|
||||
Comments,
|
||||
new PropertyType(PropertyEditors.TextboxMultipleAlias, DataTypeDatabaseType.Ntext, true)
|
||||
new PropertyType(PropertyEditors.TextboxMultipleAlias, DataTypeDatabaseType.Ntext, true, Comments)
|
||||
{
|
||||
Alias = Comments,
|
||||
Name = CommentsLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
FailedPasswordAttempts,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer, true, FailedPasswordAttempts)
|
||||
{
|
||||
Alias = FailedPasswordAttempts,
|
||||
Name = FailedPasswordAttemptsLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
IsApproved,
|
||||
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true)
|
||||
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true, IsApproved)
|
||||
{
|
||||
Alias = IsApproved,
|
||||
Name = IsApprovedLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
IsLockedOut,
|
||||
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true)
|
||||
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true, IsLockedOut)
|
||||
{
|
||||
Alias = IsLockedOut,
|
||||
Name = IsLockedOutLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastLockoutDate,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastLockoutDate)
|
||||
{
|
||||
Alias = LastLockoutDate,
|
||||
Name = LastLockoutDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastLoginDate,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastLoginDate)
|
||||
{
|
||||
Alias = LastLoginDate,
|
||||
Name = LastLoginDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastPasswordChangeDate,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastPasswordChangeDate)
|
||||
{
|
||||
Alias = LastPasswordChangeDate,
|
||||
Name = LastPasswordChangeDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordAnswer,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true, PasswordAnswer)
|
||||
{
|
||||
Alias = PasswordAnswer,
|
||||
Name = PasswordAnswerLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordQuestion,
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true)
|
||||
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true, PasswordQuestion)
|
||||
{
|
||||
Alias = PasswordQuestion,
|
||||
Name = PasswordQuestionLabel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
@@ -21,6 +23,7 @@ using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Publishing;
|
||||
using Umbraco.Core.Macros;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Core.Strings;
|
||||
@@ -259,8 +262,12 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
protected virtual void InitializeResolvers()
|
||||
{
|
||||
PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors());
|
||||
ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors());
|
||||
var builder = new ManifestBuilder(
|
||||
ApplicationCache.RuntimeCache,
|
||||
new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationCache.RuntimeCache));
|
||||
|
||||
PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors(), builder);
|
||||
ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors(), builder);
|
||||
|
||||
//setup the validators resolver with our predefined validators
|
||||
ValidatorsResolver.Current = new ValidatorsResolver(new[]
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -114,13 +115,7 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
|
||||
{
|
||||
var matches = true;
|
||||
foreach (var i in other)
|
||||
{
|
||||
matches = source.Contains(i);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
return other.Except(source).Any() == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,7 +127,7 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
public static bool ContainsAny<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
|
||||
{
|
||||
return other.Any(i => source.Contains(i));
|
||||
return other.Any(source.Contains);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,7 +219,7 @@ namespace Umbraco.Core
|
||||
return sequence.Select(
|
||||
x =>
|
||||
{
|
||||
if (typeof(TActual).IsAssignableFrom(x.GetType()))
|
||||
if (x is TActual)
|
||||
{
|
||||
var casted = x as TActual;
|
||||
projection.Invoke(casted);
|
||||
@@ -276,6 +271,34 @@ namespace Umbraco.Core
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="item">The item to find.</param>
|
||||
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
|
||||
public static int IndexOf<T>(this IEnumerable<T> items, T item) { return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i)); }
|
||||
public static int IndexOf<T>(this IEnumerable<T> items, T item)
|
||||
{
|
||||
return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if 2 lists have equal elements within them regardless of how they are sorted
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The logic for this is taken from:
|
||||
/// http://stackoverflow.com/questions/4576723/test-whether-two-ienumerablet-have-the-same-values-with-the-same-frequencies
|
||||
///
|
||||
/// There's a few answers, this one seems the best for it's simplicity and based on the comment of Eamon
|
||||
/// </remarks>
|
||||
public static bool UnsortedSequenceEqual<T>(this IEnumerable<T> source, IEnumerable<T> other)
|
||||
{
|
||||
if (source == null && other == null) return true;
|
||||
if (source == null || other == null) return false;
|
||||
|
||||
var list1Groups = source.ToLookup(i => i);
|
||||
var list2Groups = other.ToLookup(i => i);
|
||||
return list1Groups.Count == list2Groups.Count
|
||||
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Macros
|
||||
internal class MacroTagParser
|
||||
{
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([\w\.]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures that virtual paths are taken care of
|
||||
/// </summary>
|
||||
internal class GridEditorConverter : JsonCreationConverter<GridEditor>
|
||||
{
|
||||
protected override GridEditor Create(Type objectType, JObject jObject)
|
||||
{
|
||||
return new GridEditor();
|
||||
}
|
||||
|
||||
protected override void Deserialize(JObject jObject, GridEditor target, JsonSerializer serializer)
|
||||
{
|
||||
base.Deserialize(jObject, target, serializer);
|
||||
|
||||
if (target.View.IsNullOrWhiteSpace() == false && target.View.StartsWith("~/"))
|
||||
{
|
||||
target.View = IOHelper.ResolveUrl(target.View);
|
||||
}
|
||||
|
||||
if (target.Render.IsNullOrWhiteSpace() == false && target.Render.StartsWith("~/"))
|
||||
{
|
||||
target.Render = IOHelper.ResolveUrl(target.Render);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
@@ -11,73 +13,92 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
internal class ManifestBuilder
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
private readonly ManifestParser _parser;
|
||||
|
||||
private static readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
private const string ManifestKey = "manifests";
|
||||
public ManifestBuilder(IRuntimeCacheProvider cache, ManifestParser parser)
|
||||
{
|
||||
_cache = cache;
|
||||
_parser = parser;
|
||||
}
|
||||
|
||||
private const string GridEditorsKey = "grideditors";
|
||||
private const string PropertyEditorsKey = "propertyeditors";
|
||||
private const string ParameterEditorsKey = "parametereditors";
|
||||
|
||||
/// <summary>
|
||||
/// Returns all property editors found in the manfifests
|
||||
/// Returns all grid editors found in the manfifests
|
||||
/// </summary>
|
||||
internal static IEnumerable<PropertyEditor> PropertyEditors
|
||||
internal IEnumerable<GridEditor> GridEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return (IEnumerable<PropertyEditor>) StaticCache.GetOrAdd(
|
||||
PropertyEditorsKey,
|
||||
s =>
|
||||
return _cache.GetCacheItem<IEnumerable<GridEditor>>(
|
||||
typeof (ManifestBuilder) + GridEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<GridEditor>();
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
var editors = new List<PropertyEditor>();
|
||||
foreach (var manifest in GetManifests())
|
||||
if (manifest.GridEditors != null)
|
||||
{
|
||||
if (manifest.PropertyEditors != null)
|
||||
{
|
||||
editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors));
|
||||
}
|
||||
|
||||
editors.AddRange(ManifestParser.GetGridEditors(manifest.GridEditors));
|
||||
}
|
||||
return editors;
|
||||
});
|
||||
|
||||
}
|
||||
return editors;
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all property editors found in the manfifests
|
||||
/// </summary>
|
||||
internal IEnumerable<PropertyEditor> PropertyEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cache.GetCacheItem<IEnumerable<PropertyEditor>>(
|
||||
typeof(ManifestBuilder) + PropertyEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<PropertyEditor>();
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
if (manifest.PropertyEditors != null)
|
||||
{
|
||||
editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors));
|
||||
}
|
||||
|
||||
}
|
||||
return editors;
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all parameter editors found in the manfifests and all property editors that are flagged to be parameter editors
|
||||
/// </summary>
|
||||
internal static IEnumerable<ParameterEditor> ParameterEditors
|
||||
internal IEnumerable<ParameterEditor> ParameterEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return (IEnumerable<ParameterEditor>)StaticCache.GetOrAdd(
|
||||
ParameterEditorsKey,
|
||||
s =>
|
||||
return _cache.GetCacheItem<IEnumerable<ParameterEditor>>(
|
||||
typeof (ManifestBuilder) + ParameterEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<ParameterEditor>();
|
||||
foreach (var manifest in GetManifests())
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
if (manifest.ParameterEditors != null)
|
||||
{
|
||||
editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors));
|
||||
editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors));
|
||||
}
|
||||
}
|
||||
return editors;
|
||||
});
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the manifests are found and loaded into memory
|
||||
/// </summary>
|
||||
private static IEnumerable<PackageManifest> GetManifests()
|
||||
{
|
||||
return (IEnumerable<PackageManifest>) StaticCache.GetOrAdd(ManifestKey, s =>
|
||||
{
|
||||
var parser = new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")));
|
||||
return parser.GetManifests();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -17,15 +18,29 @@ namespace Umbraco.Core.Manifest
|
||||
internal class ManifestParser
|
||||
{
|
||||
private readonly DirectoryInfo _pluginsDir;
|
||||
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
|
||||
//used to strip comments
|
||||
private static readonly Regex CommentsSurround = new Regex(@"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", RegexOptions.Compiled);
|
||||
private static readonly Regex CommentsLine = new Regex(@"//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
private static readonly Regex CommentsLine = new Regex(@"^\s*//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
|
||||
public ManifestParser(DirectoryInfo pluginsDir)
|
||||
public ManifestParser(DirectoryInfo pluginsDir, IRuntimeCacheProvider cache)
|
||||
{
|
||||
if (pluginsDir == null) throw new ArgumentNullException("pluginsDir");
|
||||
_pluginsDir = pluginsDir;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the grid editors from the json array
|
||||
/// </summary>
|
||||
/// <param name="jsonEditors"></param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<GridEditor> GetGridEditors(JArray jsonEditors)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<IEnumerable<GridEditor>>(
|
||||
jsonEditors.ToString(),
|
||||
new GridEditorConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,11 +72,17 @@ namespace Umbraco.Core.Manifest
|
||||
/// Get all registered manifests
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This ensures that we only build and look for all manifests once per Web app (based on the IRuntimeCache)
|
||||
/// </remarks>
|
||||
public IEnumerable<PackageManifest> GetManifests()
|
||||
{
|
||||
//get all Manifest.js files in the appropriate folders
|
||||
var manifestFileContents = GetAllManfifestFileContents(_pluginsDir);
|
||||
return CreateManifests(manifestFileContents.ToArray());
|
||||
return _cache.GetCacheItem<IEnumerable<PackageManifest>>(typeof (ManifestParser) + "GetManifests", () =>
|
||||
{
|
||||
//get all Manifest.js files in the appropriate folders
|
||||
var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
|
||||
return CreateManifests(manifestFileContents.ToArray());
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +90,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 +100,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;
|
||||
}
|
||||
@@ -154,6 +175,20 @@ namespace Umbraco.Core.Manifest
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'propertyEditors' element");
|
||||
}
|
||||
|
||||
//validate the parameterEditors section
|
||||
var paramEditors = deserialized.Properties().Where(x => x.Name == "parameterEditors").ToArray();
|
||||
if (paramEditors.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'parameterEditors' element");
|
||||
}
|
||||
|
||||
//validate the gridEditors section
|
||||
var gridEditors = deserialized.Properties().Where(x => x.Name == "gridEditors").ToArray();
|
||||
if (gridEditors.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'gridEditors' element");
|
||||
}
|
||||
|
||||
var jConfig = init.Any() ? (JArray)deserialized["javascript"] : new JArray();
|
||||
ReplaceVirtualPaths(jConfig);
|
||||
|
||||
@@ -175,13 +210,30 @@ namespace Umbraco.Core.Manifest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//replace virtual paths for each property editor
|
||||
if (deserialized["gridEditors"] != null)
|
||||
{
|
||||
foreach (JObject p in deserialized["gridEditors"])
|
||||
{
|
||||
if (p["view"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths(p["view"]);
|
||||
}
|
||||
if (p["render"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths(p["render"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var manifest = new PackageManifest()
|
||||
{
|
||||
JavaScriptInitialize = jConfig,
|
||||
StylesheetInitialize = cssConfig,
|
||||
PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(),
|
||||
ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray()
|
||||
ParameterEditors = paramEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(),
|
||||
GridEditors = gridEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray()
|
||||
};
|
||||
result.Add(manifest);
|
||||
}
|
||||
|
||||
@@ -26,5 +26,10 @@ namespace Umbraco.Core.Manifest
|
||||
/// The json array of parameter editors
|
||||
/// </summary>
|
||||
public JArray ParameterEditors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The json array of grid editors
|
||||
/// </summary>
|
||||
public JArray GridEditors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -460,10 +460,14 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Content)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IContentType)ContentType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace Umbraco.Core.Models
|
||||
return;
|
||||
}
|
||||
|
||||
var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
|
||||
var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
|
||||
if (propertyType == null)
|
||||
{
|
||||
throw new Exception(String.Format("No PropertyType exists with the supplied alias: {0}", propertyTypeAlias));
|
||||
|
||||
@@ -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,7 +15,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
private int _defaultTemplate;
|
||||
private IEnumerable<ITemplate> _allowedTemplates;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constuctor for creating a ContentType with the parent's id.
|
||||
/// </summary>
|
||||
@@ -31,9 +31,10 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Use this to ensure inheritance from parent.</remarks>
|
||||
/// <param name="parent"></param>
|
||||
public ContentType(IContentType parent) : this(parent, null)
|
||||
{
|
||||
}
|
||||
[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.
|
||||
@@ -49,7 +50,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
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>
|
||||
@@ -89,7 +90,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_allowedTemplates = value;
|
||||
return _allowedTemplates;
|
||||
}, _allowedTemplates, AllowedTemplatesSelector);
|
||||
}, _allowedTemplates, AllowedTemplatesSelector,
|
||||
//Custom comparer for enumerable
|
||||
new DelegateEqualityComparer<IEnumerable<ITemplate>>(
|
||||
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
|
||||
templates => templates.GetHashCode()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +111,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);
|
||||
@@ -140,7 +145,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if(Key == Guid.Empty)
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
@@ -151,7 +156,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;
|
||||
@@ -349,7 +351,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_allowedContentTypes = value;
|
||||
return _allowedContentTypes;
|
||||
}, _allowedContentTypes, AllowedContentTypesSelector);
|
||||
}, _allowedContentTypes, AllowedContentTypesSelector,
|
||||
//Custom comparer for enumerable
|
||||
new DelegateEqualityComparer<IEnumerable<ContentTypeSort>>(
|
||||
(sorts, enumerable) => sorts.UnsortedSequenceEqual(enumerable),
|
||||
sorts => sorts.GetHashCode()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +503,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))
|
||||
@@ -524,6 +529,7 @@ namespace Umbraco.Core.Models
|
||||
public void RemovePropertyGroup(string propertyGroupName)
|
||||
{
|
||||
PropertyGroups.RemoveItem(propertyGroupName);
|
||||
OnPropertyChanged(PropertyGroupCollectionSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -607,13 +613,18 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeBase)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually wire up the event handlers for the property type collections - we've ensured
|
||||
// its ignored from the auto-clone process because its return values are unions, not raw and
|
||||
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
|
||||
|
||||
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
|
||||
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -255,11 +255,15 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeCompositionBase)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since this is an internal field and will not be automatically mapped
|
||||
clone.RemovedContentTypeKeyTracker = new List<int>();
|
||||
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,20 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
{
|
||||
[Obsolete("This parameterless constructor should never be used")]
|
||||
public ContentTypeSort()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public ContentTypeSort(int id, int sortOrder)
|
||||
{
|
||||
Id = new Lazy<int>(() => id);
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public ContentTypeSort(Lazy<int> id, int sortOrder, string @alias)
|
||||
{
|
||||
Id = id;
|
||||
@@ -45,7 +55,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
protected bool Equals(ContentTypeSort other)
|
||||
{
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
@@ -60,7 +70,9 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Id.GetHashCode()*397) ^ Alias.GetHashCode();
|
||||
//The hash code will just be the alias if one is assigned, otherwise it will be the hash code of the Id.
|
||||
//In some cases the alias can be null of the non lazy ctor is used, in that case, the lazy Id will already have a value created.
|
||||
return Alias != null ? Alias.GetHashCode() : (Id.Value.GetHashCode() * 397);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -80,7 +80,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_translations = value;
|
||||
return _translations;
|
||||
}, _translations, TranslationsSelector);
|
||||
}, _translations, TranslationsSelector,
|
||||
//Custom comparer for enumerable
|
||||
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
|
||||
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
|
||||
enumerable => enumerable.GetHashCode()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
public abstract class Entity : TracksChangesEntityBase, IEntity, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
private bool _hasIdentity;
|
||||
private int? _hash;
|
||||
private int _id;
|
||||
private Guid _key;
|
||||
private DateTime _createDate;
|
||||
@@ -175,6 +174,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual bool SameIdentityAs(IEntity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
@@ -195,11 +195,13 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
return SameIdentityAs(other);
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual Type GetRealType()
|
||||
{
|
||||
return GetType();
|
||||
}
|
||||
|
||||
//TODO: Make this NOT virtual or even exist really!
|
||||
public virtual bool SameIdentityAs(Entity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
@@ -226,9 +228,13 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (!_hash.HasValue)
|
||||
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
|
||||
return _hash.Value;
|
||||
unchecked
|
||||
{
|
||||
var hashCode = HasIdentity.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Id;
|
||||
hashCode = (hashCode * 397) ^ GetType().GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual object DeepClone()
|
||||
@@ -236,9 +242,17 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
//Memberwise clone on Entity will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var clone = (Entity)MemberwiseClone();
|
||||
//ensure the clone has it's own dictionaries
|
||||
clone.ResetChangeTrackingCollections();
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -18,7 +19,9 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
public virtual IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
return _propertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _changeTrackingEnabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the properties that have changed
|
||||
@@ -41,6 +44,9 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// <param name="propertyInfo">The property info.</param>
|
||||
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
|
||||
{
|
||||
//return if we're not tracking changes
|
||||
if (_changeTrackingEnabled == false) return;
|
||||
|
||||
_propertyChangedInfo[propertyInfo.Name] = true;
|
||||
|
||||
if (PropertyChanged != null)
|
||||
@@ -132,6 +138,22 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
_propertyChangedInfo = new Dictionary<string, bool>();
|
||||
}
|
||||
|
||||
protected void ResetChangeTrackingCollections()
|
||||
{
|
||||
_propertyChangedInfo = new Dictionary<string, bool>();
|
||||
_lastPropertyChangedInfo = new Dictionary<string, bool>();
|
||||
}
|
||||
|
||||
protected void DisableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = false;
|
||||
}
|
||||
|
||||
protected void EnableChangeTracking()
|
||||
{
|
||||
_changeTrackingEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
|
||||
/// it will ensure that the property has a dirty flag set.
|
||||
@@ -145,17 +167,51 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
|
||||
/// to the same value, so it's really not dirty.
|
||||
/// </remarks>
|
||||
internal virtual bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector)
|
||||
internal bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector)
|
||||
{
|
||||
if ((typeof(T) == typeof(string) == false) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(typeof(T)))
|
||||
{
|
||||
throw new InvalidOperationException("This method does not support IEnumerable instances. For IEnumerable instances a manual custom equality check will be required");
|
||||
}
|
||||
|
||||
return SetPropertyValueAndDetectChanges(setValue, value, propertySelector,
|
||||
new DelegateEqualityComparer<T>(
|
||||
//Standard Equals comparison
|
||||
(arg1, arg2) => Equals(arg1, arg2),
|
||||
arg => arg.GetHashCode()));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
|
||||
/// it will ensure that the property has a dirty flag set.
|
||||
/// </summary>
|
||||
/// <param name="setValue"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="propertySelector"></param>
|
||||
/// <param name="comparer">The equality comparer to use</param>
|
||||
/// <returns>returns true if the value changed</returns>
|
||||
/// <remarks>
|
||||
/// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we
|
||||
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
|
||||
/// to the same value, so it's really not dirty.
|
||||
/// </remarks>
|
||||
internal bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector, IEqualityComparer<T> comparer)
|
||||
{
|
||||
var initVal = value;
|
||||
var newVal = setValue(value);
|
||||
|
||||
if (Equals(initVal, newVal) == false)
|
||||
//don't track changes, just set the value (above)
|
||||
if (_changeTrackingEnabled == false) return false;
|
||||
|
||||
if (comparer.Equals(initVal, newVal) == false)
|
||||
{
|
||||
OnPropertyChanged(propertySelector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -108,12 +113,15 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (File)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since they are readonly properties
|
||||
clone._alias = Alias;
|
||||
clone._name = Name;
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -398,14 +399,17 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Macro)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
clone._addedProperties = new List<string>();
|
||||
clone._removedProperties = new List<string>();
|
||||
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
|
||||
//re-assign the event handler
|
||||
clone._properties.CollectionChanged += clone.PropertiesChanged;
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -650,10 +617,14 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Member)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IMemberType)ContentType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// Gets/sets the user type for the user
|
||||
/// </summary>
|
||||
IUserType UserType { get; set; }
|
||||
|
||||
|
||||
//TODO: This should be a private set
|
||||
/// <summary>
|
||||
/// The default permission set for the user
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
if (userType == null) throw new ArgumentNullException("userType");
|
||||
|
||||
_userType = userType;
|
||||
_defaultPermissions = _userType.Permissions;
|
||||
_defaultPermissions = _userType.Permissions == null ? Enumerable.Empty<string>() : new List<string>(_userType.Permissions);
|
||||
//Groups = new List<object> { userType };
|
||||
SessionTimeout = 60;
|
||||
_sectionCollection = new ObservableCollection<string>();
|
||||
@@ -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;
|
||||
@@ -72,7 +71,9 @@ namespace Umbraco.Core.Models.Membership
|
||||
private bool _isApproved;
|
||||
private bool _isLockedOut;
|
||||
private string _language;
|
||||
private IEnumerable<string> _defaultPermissions;
|
||||
|
||||
private IEnumerable<string> _defaultPermissions;
|
||||
|
||||
private bool _defaultToLiveEditing;
|
||||
|
||||
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
|
||||
@@ -80,7 +81,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);
|
||||
@@ -88,7 +88,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsLockedOut);
|
||||
private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsApproved);
|
||||
private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Language);
|
||||
private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.DefaultPermissions);
|
||||
|
||||
private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.DefaultToLiveEditing);
|
||||
private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo<User, IUserType>(x => x.UserType);
|
||||
|
||||
@@ -101,37 +101,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
|
||||
@@ -352,19 +321,13 @@ namespace Umbraco.Core.Models.Membership
|
||||
}, _language, LanguageSelector);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TODO: This should be a private set
|
||||
[DataMember]
|
||||
public IEnumerable<string> DefaultPermissions
|
||||
{
|
||||
get { return _defaultPermissions; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_defaultPermissions = value;
|
||||
return _defaultPermissions;
|
||||
}, _defaultPermissions, DefaultPermissionsSelector);
|
||||
}
|
||||
get { return _defaultPermissions;}
|
||||
set { _defaultPermissions = value; }
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
@@ -448,15 +411,19 @@ namespace Umbraco.Core.Models.Membership
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (User)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to create new collections otherwise they'll get copied by ref
|
||||
clone._addedSections = new List<string>();
|
||||
clone._removedSections = new List<string>();
|
||||
clone._sectionCollection = new ObservableCollection<string>(_sectionCollection.ToList());
|
||||
clone._defaultPermissions = new List<string>(_defaultPermissions.ToList());
|
||||
//re-create the event handler
|
||||
clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged;
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -67,7 +68,11 @@ namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
_permissions = value;
|
||||
return _permissions;
|
||||
}, _permissions, PermissionsSelector);
|
||||
}, _permissions, PermissionsSelector,
|
||||
//Custom comparer for enumerable
|
||||
new DelegateEqualityComparer<IEnumerable<string>>(
|
||||
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
|
||||
enum1 => enum1.GetHashCode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
//{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -120,7 +123,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
bool typeValidation = _propertyType.IsPropertyTypeValid(value);
|
||||
|
||||
if (!typeValidation)
|
||||
if (typeValidation == false)
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
|
||||
@@ -130,7 +133,21 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_value = value;
|
||||
return _value;
|
||||
}, _value, ValueSelector);
|
||||
}, _value, ValueSelector,
|
||||
new DelegateEqualityComparer<object>(
|
||||
(o, o1) =>
|
||||
{
|
||||
//Custom comparer for enumerable if it is enumerable
|
||||
if (o == null && o1 == null) return true;
|
||||
if (o == null || o1 == null) return false;
|
||||
var enum1 = o as IEnumerable;
|
||||
var enum2 = o1 as IEnumerable;
|
||||
if (enum1 != null && enum2 != null)
|
||||
{
|
||||
return enum1.Cast<object>().UnsortedSequenceEqual(enum2.Cast<object>());
|
||||
}
|
||||
return o.Equals(o1);
|
||||
}, o => o.GetHashCode()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,10 +177,14 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Property)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since this is a readonly property
|
||||
clone._propertyType = (PropertyType)PropertyType.DeepClone();
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
|
||||
private readonly object _addLocker = new object();
|
||||
internal Action OnAdd;
|
||||
internal Func<Property, bool> ValidateAdd { get; set; }
|
||||
|
||||
internal PropertyCollection()
|
||||
{}
|
||||
: base(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyCollection"/> class with a delegate responsible for validating the addition of <see cref="Property"/> instances.
|
||||
@@ -29,11 +31,13 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="validationCallback">The validation callback.</param>
|
||||
/// <remarks></remarks>
|
||||
internal PropertyCollection(Func<Property, bool> validationCallback)
|
||||
: this()
|
||||
{
|
||||
ValidateAdd = validationCallback;
|
||||
}
|
||||
|
||||
public PropertyCollection(IEnumerable<Property> properties)
|
||||
: this()
|
||||
{
|
||||
Reset(properties);
|
||||
}
|
||||
@@ -78,7 +82,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
internal new void Add(Property item)
|
||||
{
|
||||
using (new WriteLock(_addLocker))
|
||||
lock (_addLocker)
|
||||
{
|
||||
var key = GetKeyForItem(item);
|
||||
if (key != null)
|
||||
@@ -113,14 +117,14 @@ namespace Umbraco.Core.Models
|
||||
/// <remarks></remarks>
|
||||
public new bool Contains(string propertyTypeAlias)
|
||||
{
|
||||
return this.Any(x => x.Alias == propertyTypeAlias);
|
||||
return base.Contains(propertyTypeAlias);
|
||||
}
|
||||
|
||||
public int IndexOfKey(string key)
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (this[i].Alias == key)
|
||||
if (this[i].Alias.InvariantEquals(key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
@@ -145,7 +149,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.FirstOrDefault(x => x.Alias == propertyType.Alias);
|
||||
return this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyType.Alias));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +169,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypes">List of PropertyType</param>
|
||||
protected internal void EnsurePropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
||||
{
|
||||
if(/*!this.Any() &&*/ propertyTypes != null)
|
||||
if (/*!this.Any() &&*/ propertyTypes != null)
|
||||
{
|
||||
foreach (var propertyType in propertyTypes)
|
||||
{
|
||||
@@ -180,7 +184,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypes">List of PropertyType</param>
|
||||
protected internal void EnsureCleanPropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
||||
{
|
||||
if(propertyTypes != null)
|
||||
if (propertyTypes != null)
|
||||
{
|
||||
//Remove PropertyTypes that doesn't exist in the list of new PropertyTypes
|
||||
var aliases = this.Select(p => p.Alias).Except(propertyTypes.Select(x => x.Alias)).ToList();
|
||||
|
||||
@@ -120,27 +120,22 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public bool Equals(PropertyGroup other)
|
||||
{
|
||||
//Check whether the compared object is null.
|
||||
if (Object.ReferenceEquals(other, null)) return false;
|
||||
|
||||
//Check whether the compared object references the same data.
|
||||
if (Object.ReferenceEquals(this, other)) return true;
|
||||
if (base.Equals(other)) return true;
|
||||
|
||||
//Check whether the PropertyGroup's properties are equal.
|
||||
return Id.Equals(other.Id) && Name.Equals(other.Name);
|
||||
return Name.InvariantEquals(other.Name);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
|
||||
//Get hash code for the Name field if it is not null.
|
||||
int hashName = Name == null ? 0 : Name.GetHashCode();
|
||||
int baseHash = base.GetHashCode();
|
||||
|
||||
//Get hash code for the Id field.
|
||||
int hashId = Id.GetHashCode();
|
||||
//Get hash code for the Alias field.
|
||||
int nameHash = Name.ToLowerInvariant().GetHashCode();
|
||||
|
||||
//Calculate the hash code for the product.
|
||||
return hashName ^ hashId;
|
||||
return baseHash ^ nameHash;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -37,11 +38,21 @@ namespace Umbraco.Core.Models
|
||||
_propertyEditorAlias = dataTypeDefinition.PropertyEditorAlias;
|
||||
_dataTypeDatabaseType = dataTypeDefinition.DatabaseType;
|
||||
}
|
||||
|
||||
public PropertyType(IDataTypeDefinition dataTypeDefinition, string propertyTypeAlias)
|
||||
: this(dataTypeDefinition)
|
||||
{
|
||||
SetAlias(propertyTypeAlias);
|
||||
}
|
||||
|
||||
internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
|
||||
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
|
||||
: this(propertyEditorAlias, dataTypeDatabaseType, false)
|
||||
{
|
||||
PropertyEditorAlias = propertyEditorAlias;
|
||||
}
|
||||
|
||||
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, string propertyTypeAlias)
|
||||
: this(propertyEditorAlias, dataTypeDatabaseType, false, propertyTypeAlias)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,6 +68,21 @@ namespace Umbraco.Core.Models
|
||||
_dataTypeDatabaseType = dataTypeDatabaseType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is.
|
||||
/// </summary>
|
||||
/// <param name="propertyEditorAlias"></param>
|
||||
/// <param name="dataTypeDatabaseType"></param>
|
||||
/// <param name="isExplicitDbType"></param>
|
||||
/// <param name="propertyTypeAlias"></param>
|
||||
internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType, string propertyTypeAlias)
|
||||
{
|
||||
_isExplicitDbType = isExplicitDbType;
|
||||
_propertyEditorAlias = propertyEditorAlias;
|
||||
_dataTypeDatabaseType = dataTypeDatabaseType;
|
||||
SetAlias(propertyTypeAlias);
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Name);
|
||||
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Alias);
|
||||
private static readonly PropertyInfo DescriptionSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Description);
|
||||
@@ -95,15 +121,9 @@ namespace Umbraco.Core.Models
|
||||
get { return _alias; }
|
||||
set
|
||||
{
|
||||
//NOTE: WE are doing this because we don't want to do a ToSafeAlias when the alias is the special case of
|
||||
// being prefixed with Constants.PropertyEditors.InternalGenericPropertiesPrefix
|
||||
// which is used internally
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value.StartsWith(Constants.PropertyEditors.InternalGenericPropertiesPrefix)
|
||||
? value
|
||||
: value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
SetAlias(value);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
@@ -283,6 +303,17 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAlias(string value)
|
||||
{
|
||||
//NOTE: WE are doing this because we don't want to do a ToSafeAlias when the alias is the special case of
|
||||
// being prefixed with Constants.PropertyEditors.InternalGenericPropertiesPrefix
|
||||
// which is used internally
|
||||
|
||||
_alias = value.StartsWith(Constants.PropertyEditors.InternalGenericPropertiesPrefix)
|
||||
? value
|
||||
: value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Property object from a "raw" database value.
|
||||
/// </summary>
|
||||
@@ -430,39 +461,38 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public bool Equals(PropertyType other)
|
||||
{
|
||||
//Check whether the compared object is null.
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
|
||||
//Check whether the compared object references the same data.
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
if (base.Equals(other)) return true;
|
||||
|
||||
//Check whether the PropertyType's properties are equal.
|
||||
return Alias.Equals(other.Alias) && Name.Equals(other.Name);
|
||||
return Alias.InvariantEquals(other.Alias);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
//Get hash code for the Name field if it is not null.
|
||||
int hashName = Name == null ? 0 : Name.GetHashCode();
|
||||
int baseHash = base.GetHashCode();
|
||||
|
||||
//Get hash code for the Alias field.
|
||||
int hashAlias = Alias.GetHashCode();
|
||||
int hashAlias = Alias.ToLowerInvariant().GetHashCode();
|
||||
|
||||
//Calculate the hash code for the product.
|
||||
return hashName ^ hashAlias;
|
||||
return baseHash ^ hashAlias;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (PropertyType)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign the Lazy value as it will not be automatically mapped
|
||||
if (PropertyGroupId != null)
|
||||
{
|
||||
clone._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
|
||||
}
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/// </summary>
|
||||
public enum TaggableObjectTypes
|
||||
{
|
||||
All,
|
||||
Content,
|
||||
Media,
|
||||
Member
|
||||
|
||||
@@ -193,12 +193,15 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Template)base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//need to manually assign since they are readonly properties
|
||||
clone._alias = Alias;
|
||||
clone._name = Name;
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -288,7 +288,8 @@ namespace Umbraco.Core.Models
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (UmbracoEntity) base.DeepClone();
|
||||
|
||||
//turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
//This ensures that any value in the dictionary that is deep cloneable is cloned too
|
||||
foreach (var key in clone.AdditionalData.Keys.ToArray())
|
||||
{
|
||||
@@ -298,7 +299,10 @@ namespace Umbraco.Core.Models
|
||||
clone.AdditionalData[key] = deepCloneable.DeepClone();
|
||||
}
|
||||
}
|
||||
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,45 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
var culture = CultureInfo.GetCultureInfo(userLanguage.Replace("_", "-"));
|
||||
//TODO: This is a hack because we store the user language as 2 chars instead of the full culture
|
||||
// which is actually stored in the language files (which are also named with 2 chars!) so we need to attempt
|
||||
// to convert to a supported full culture
|
||||
var result = textService.ConvertToSupportedCultureWithRegionCode(culture);
|
||||
return result;
|
||||
}
|
||||
catch (CultureNotFoundException)
|
||||
{
|
||||
//return the default one
|
||||
return CultureInfo.GetCultureInfo("en");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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:
|
||||
|
||||
@@ -105,9 +105,9 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
propDbType.Result,
|
||||
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
|
||||
// which is what we want for the built-in properties.
|
||||
propDbType.Success)
|
||||
propDbType.Success,
|
||||
typeDto.Alias)
|
||||
{
|
||||
Alias = typeDto.Alias,
|
||||
DataTypeDefinitionId = typeDto.DataTypeId,
|
||||
Description = typeDto.Description,
|
||||
Id = typeDto.Id.Value,
|
||||
@@ -158,9 +158,9 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
propDbType.Result,
|
||||
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
|
||||
// which is what we want for the built-in properties.
|
||||
propDbType.Success)
|
||||
propDbType.Success,
|
||||
typeDto.Alias)
|
||||
{
|
||||
Alias = typeDto.Alias,
|
||||
DataTypeDefinitionId = typeDto.DataTypeId,
|
||||
Description = typeDto.Description,
|
||||
HelpText = typeDto.HelpText,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -32,9 +32,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
IsLockedOut = dto.NoConsole,
|
||||
IsApproved = dto.Disabled == false,
|
||||
Email = dto.Email,
|
||||
Language = dto.UserLanguage,
|
||||
//NOTE: The default permission come from the user type's default permissions
|
||||
DefaultPermissions = _userType.Permissions
|
||||
Language = dto.UserLanguage
|
||||
};
|
||||
|
||||
foreach (var app in dto.User2AppDtos)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user