Compare commits

..

67 Commits

Author SHA1 Message Date
Shannon 2d230a7b01 Adds a ColdBootType so we can detect and work with that if we have our own custom callbacks 2017-01-11 16:40:40 +11:00
Shannon 18c5348bdf Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into temp-examine-sync 2017-01-11 16:40:10 +11:00
Shannon c50fea9794 Adds a ColdBootType so we can detect and work with that if we have our own custom callbacks 2017-01-11 16:39:13 +11:00
Mads Rasmussen 0a53f7636c remove orange color from discard changes button 2017-01-10 15:01:45 +01:00
Mads Rasmussen 6e6d552441 Merge pull request #1662 from marcemarc/patch-2
Add some green to 'stay' option - U4-9323
2017-01-10 14:59:08 +01:00
Sebastiaan Janssen 358dd7f32b Move ImageProcessor.Web.Config to not be in the .Core NuGet, it's not a dll 2017-01-10 07:30:35 +01:00
Shannon 6531d027fc fixes error with server side validation + data tyep editor 2017-01-10 11:55:36 +11:00
Shannon d11e787624 updates Nuspec with IP version 2017-01-10 11:37:31 +11:00
Shannon 055ae0d126 updates latest IP mem leak fixes 2017-01-10 11:36:35 +11:00
Sebastiaan Janssen 52b1a08912 Merge pull request #1671 from umbraco/temp-U4-9337
Perf improvements especially around URLs and node path
2017-01-09 16:12:26 +01:00
Stephan 9ba08f275e Merge branch 'temp-U4-9337' of https://github.com/umbraco/Umbraco-CMS into temp-U4-9337 2017-01-06 09:15:43 +01:00
Stephan aadd0c9129 U4-9337 - asymmetric route caching 2017-01-06 09:12:25 +01:00
Shannon b3b79a792f Merge branch 'refs/heads/dev-v7' into temp-U4-9337 2017-01-06 17:40:40 +11:00
Shannon 59cd4b5c5c Fixes more allocations at the config level 2017-01-06 17:40:03 +11:00
Shannon f5f9ff29ce Discovered we are not passing the ISqlSyntaxProvider correctly to where so it defaults to the global one which causes some issues for libraries such as UmbracoIdentity 2017-01-06 14:32:56 +11:00
Sebastiaan Janssen a10b59d03b Revert "Fixes: U4-9217 - Sanitize tags before storing them in the database"
This reverts commit 47c8e6854e.
2017-01-05 17:14:14 +01:00
Sebastiaan Janssen e6b1ecb851 Better way of cleaning the file extension to prevent XSS attacks 2017-01-05 11:52:06 +01:00
Sebastiaan Janssen 69d6609a3b Merge pull request #1675 from umbraco/temp-U4-9339
Trim the file name so that it doesn't get accepted by the server if i…
2017-01-05 11:34:23 +01:00
Claus 2196ab3713 changing this to use ToSafeFileName instead of just trimming for blank characters.
issuing a request with any character not allowed in a file path, appended to the filename - allows you to trick the validation like with the blank characters appended.
2017-01-05 11:10:21 +01:00
Stephan 89be1cf5d2 U4-9337 - with missing file... 2017-01-05 10:50:03 +01:00
Stephan 2041e85b9e U4-9337 - add doc and test 2017-01-05 10:46:40 +01:00
Claus 6c2f0e404f Merge pull request #1672 from umbraco/temp-U4-9336
U4-9336 Deleting a node with an incorrect path of -1 will unpublish every node in the entire site
2017-01-05 10:17:50 +01:00
Stephan d5e66252dc U4-9337 - minor simplification 2017-01-05 09:25:30 +01:00
Shannon c2359a96f8 missed nuspec update 2017-01-05 17:09:37 +11:00
Shannon 1df65bc3ce Updates to latest ImageProcessor.Web to fix mime types when stored outside web root 2017-01-05 16:00:45 +11:00
Shannon 530a80d7ea Adds a check to validate the NodeDto when persisting the item to validate that it's path is correct this should prevent any data corruption in the future - if the data corruption actually came from Core 2017-01-05 10:43:33 +11:00
Shannon 8be0f68390 Adds logic to validate an entity's path when it is used to GetDescendants, adds logic to fix an entity's path when it's state is going to be persisted when GetDescendants is used (i.e. MoveToRecycleBin), added this logic to Media too, adds unit tests to support 2017-01-05 10:29:03 +11:00
Sebastiaan Janssen 05cb30d79c Trim the file name so that it doesn't get accepted by the server if it's a disallowed file 2017-01-04 17:01:26 +01:00
Sebastiaan Janssen c472d4975e Add some extra logging to ScheduledPublishing to more easily see where errors are coming from 2017-01-04 16:31:55 +01:00
Stephan 0e31fcf3db U4-9337 - fix tests 2017-01-04 15:36:29 +01:00
Stephan 13ed3303f5 U4-9337 - url routing perfs 2017-01-04 13:56:41 +01:00
Claus f200ab423d updated test to fail and updated notes. 2017-01-04 11:59:07 +01:00
Shannon d889d3e385 adds test 2017-01-04 20:00:30 +11:00
Sebastiaan Janssen 0bf27fa319 Merge pull request #1669 from umbraco/temp-U4-9335
U4-9335 UmbracoExamine needs to ignore the useTempStorage option if a…
2017-01-04 09:42:01 +01:00
Shannon 466a37aab8 fix build 2017-01-04 18:42:11 +11:00
Shannon 4ab2130d7d stop allocating and re-processing config elements every time they are accessed! 2017-01-04 18:40:00 +11:00
Shannon 256ceef875 changes PublishedContentCache to explicitly cast to XmlElement (perf) 2017-01-04 18:12:28 +11:00
Shannon 2acde54880 Adds new benchmark and updates the benchmark bootstrapper to be the supported way 2017-01-04 18:11:21 +11:00
Shannon a92d255e4b adds notes, obsoletes a few classes that shouldn't have really been there 2017-01-04 17:10:48 +11:00
Sebastiaan Janssen dc62272e6e Revert "Add Microsoft.IO.RecyclableMemoryStream to the nuspec, update to the latest version (1.2.1)"
Keep the version the same as what ImageProcessor relies on to avoid confusion and conflicts

This reverts commit 56d2230251.

# Conflicts:
#	build/NuSpecs/UmbracoCms.Core.nuspec
2017-01-03 17:04:38 +01:00
Niels Hartvig 60f6a60ca9 Update install post url to https 2017-01-03 16:11:32 +01:00
Sebastiaan Janssen eef8d21112 Merge pull request #1667 from umbraco/U-9333
Makes EXIF data in resized images default
2017-01-03 15:59:43 +01:00
Sebastiaan Janssen 6a374cbfa6 Make the NuGet install and upgrade work 2017-01-03 15:57:21 +01:00
Sebastiaan Janssen ccb8ac4cfc Merge branch 'dev-v7' into U-9333
# Conflicts:
#	src/Umbraco.Web.UI/packages.config
2017-01-03 14:19:43 +01:00
hartvig 5de21734c0 Merge pull request #1650 from Bitmapped/U4-9298
Fix U4-9298
2017-01-03 12:56:46 +01:00
Sebastiaan Janssen 18deb8ad99 Merge pull request #1666 from defsteph/patch-1
Fixes issue U4-9332
2017-01-03 12:54:09 +01:00
Claus 3426ae3722 Merge pull request #1668 from umbraco/temp-U4-9318
U4-9318 Examine indexes inherited unpublished nodes on index rebuild
2017-01-03 11:44:33 +01:00
Sebastiaan Janssen d04510f5f2 Merge pull request #1661 from umbraco/temp-U4-9319
U4-9319 Update to latest ImageProcessor v2.5.0 and ImageProcessor.Web…
2017-01-03 10:21:26 +01:00
Sebastiaan Janssen 84c0682f16 Not necessary to install Microsoft.IO.RecyclableMemoryStream, the ImageProcessor dependency will do that for us 2017-01-03 10:20:22 +01:00
Shannon 53bbc5f922 U4-9335 UmbracoExamine needs to ignore the useTempStorage option if an IDirectoryFactory config option is supplied by the indexer 2017-01-03 19:39:34 +11:00
Sebastiaan Janssen 56d2230251 Add Microsoft.IO.RecyclableMemoryStream to the nuspec, update to the latest version (1.2.1) 2017-01-03 09:25:10 +01:00
Sebastiaan Janssen 62f21d3cee Update nuspec to install latest version of ImageProcessor 2017-01-03 09:17:05 +01:00
Shannon b9c90af8be U4-9329 ExamineIndex.config should not ship with the {machinename} token for Examine
Removes Sync from being the default for developers locally (that shouldnt have been committed), actually removes the machinename token from release
2017-01-03 17:37:15 +11:00
Shannon 7a661c1b48 clears the tracked paths collection just to possible make GC a bit happier 2017-01-03 17:16:07 +11:00
Shannon c871a711dd U4-9318 Examine indexes inherited unpublished nodes on index RebuildIndex
This reduces allocations since we don't need to create a new serializer for every doc
2017-01-03 16:43:54 +11:00
Shannon d671caf3ab moves static query to lazily initialize 2017-01-03 14:36:06 +11:00
Shannon d412d90b17 fixing more IP assembly/nuget refs 2017-01-03 11:14:34 +11:00
Shannon 2f74181262 fixing more IP assembly/nuget refs 2017-01-03 11:06:15 +11:00
Shannon 45fd23ff02 Merge branch 'refs/heads/dev-v7' into temp-U4-9319 2017-01-03 10:36:45 +11:00
Shannon 4e2fb8e5dc Fixes IP nuget references and fixes up several other Nuget assembly references. 2017-01-03 10:36:26 +11:00
Niels Hartvig afbceca184 Makes EXIF data in resized images default 2017-01-02 17:29:37 +01:00
Stephan Lonntorp 1b8c68af02 Fixes issue U4-9332
U4-9332 Language of DefaultCultureDictionary is cached without the current Culture identifier
2017-01-02 14:53:57 +01:00
Shannon 98430fd573 U4-9326 Removing a master template from a Template does not update the Template's path correctly 2016-12-30 11:42:02 +11:00
Marc Goodson 4a547ba1c9 Add some green to 'stay' option - U4-9323
Closes U4-9323 http://issues.umbraco.org/issue/U4-9323
2016-12-28 15:11:10 +00:00
Shannon 59f39fd535 Ensures we remove the new DLL with the install script like other files 2016-12-28 19:48:35 +11:00
Shannon b4ca04596c U4-9319 Update to latest ImageProcessor v2.5.0 and ImageProcessor.Web v4.7.0 2016-12-28 19:21:00 +11:00
Brian Powell afa39866ad Fix U4-9298 2016-12-17 21:12:45 -05:00
290 changed files with 4388 additions and 11903 deletions
-1
View File
@@ -142,4 +142,3 @@ build/ui-docs.zip
build/csharp-docs.zip
build/msbuild.log
.vs/
src/packages/
+14 -15
View File
@@ -2,23 +2,21 @@ version: '{build}'
shallow_clone: true
build_script:
- cmd: >-
SET SLN=%CD%
SET SRC=%SLN%\src
SET PACKAGES=%SRC%\packages
CD build
cd build
SET "release="
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
ECHO "Restoring NuGet into %PACKAGES%"
%SRC%\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
%SRC%\.nuget\NuGet.exe restore %SRC%\umbraco.sln -Verbosity Quiet -NonInteractive -PackagesDirectory %PACKAGES%
SET nuGetFolder=C:\Users\appveyor\.nuget\packages
..\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% -Verbosity quiet
IF EXIST ..\src\umbraco.businesslogic\packages.config ..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
ECHO Building Release %release% build%APPVEYOR_BUILD_NUMBER%
@@ -26,12 +24,13 @@ build_script:
SET MSBUILD="C:\Program Files (x86)\MSBuild\14.0\Bin\MsBuild.exe"
XCOPY "%SRC%\Umbraco.Tests\unit-test-log4net.CI.config" "%SRC%\Umbraco.Tests\unit-test-log4net.config" /Y
XCOPY "..\src\Umbraco.Tests\unit-test-log4net.CI.config" "..\src\Umbraco.Tests\unit-test-log4net.config" /Y
%MSBUILD% "%SLN%/src/Umbraco.Tests/Umbraco.Tests.csproj" /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /p:NugetPackagesDirectory=%PACKAGES%
%MSBUILD% "..\src\Umbraco.Tests\Umbraco.Tests.csproj" /consoleloggerparameters:Summary;ErrorsOnly
build.bat -integration -release:%release% -comment:build%APPVEYOR_BUILD_NUMBER% -nugetfolder:%PACKAGES%
build.bat nopause %release% build%APPVEYOR_BUILD_NUMBER%
ECHO %PATH%
test:
assemblies: src\Umbraco.Tests\bin\Debug\Umbraco.Tests.dll
artifacts:
+30 -109
View File
@@ -1,95 +1,31 @@
@ECHO OFF
:: UMBRACO BUILD FILE
:: ensure we have UmbracoVersion.txt
IF NOT EXIST UmbracoVersion.txt (
ECHO UmbracoVersion.txt is missing!
GOTO error
ECHO UmbracoVersion.txt missing!
GOTO :showerror
)
REM Get the version and comment from UmbracoVersion.txt lines 2 and 3
SET RELEASE=
SET COMMENT=
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED RELEASE SET RELEASE=%%i
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED COMMENT SET COMMENT=%%i
SET "release="
SET "comment="
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED comment SET "comment=%%i"
REM process args
REM If there's arguments on the command line overrule UmbracoVersion.txt and use that as the version
IF [%2] NEQ [] (SET release=%2)
IF [%3] NEQ [] (SET comment=%3) ELSE (IF [%2] NEQ [] (SET "comment="))
SET INTEGRATION=0
SET nuGetFolder=%CD%\..\src\packages
SET SKIPNUGET=0
REM Get the "is continuous integration" from the parameters
SET "isci=0"
IF [%1] NEQ [] (SET isci=1)
:processArgs
SET version=%release%
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
:: grab the first parameter as a whole eg "/action:start"
:: end if no more parameter
SET SWITCHPARSE=%1
IF [%SWITCHPARSE%] == [] goto endProcessArgs
ECHO.
ECHO Building Umbraco %version%
ECHO.
:: get switch and value
SET SWITCH=
SET VALUE=
FOR /F "tokens=1,* delims=: " %%a IN ("%SWITCHPARSE%") DO SET SWITCH=%%a& SET VALUE=%%b
:: route arg
IF '%SWITCH%'=='/release' GOTO argRelease
IF '%SWITCH%'=='-release' GOTO argRelease
IF '%SWITCH%'=='/comment' GOTO argComment
IF '%SWITCH%'=='-comment' GOTO argComment
IF '%SWITCH%'=='/integration' GOTO argIntegration
IF '%SWITCH%'=='-integration' GOTO argIntegration
IF '%SWITCH%'=='/nugetfolder' GOTO argNugetFolder
IF '%SWITCH%'=='-nugetfolder' GOTO argNugetFolder
IF '%SWITCH%'=='/skipnuget' GOTO argSkipNuget
IF '%SWITCH%'=='-skipnuget' GOTO argSkipNuget
ECHO "Invalid switch %SWITCH%"
GOTO error
:: handle each arg
:argRelease
set RELEASE=%VALUE%
SHIFT
goto processArgs
:argComment
SET COMMENT=%VALUE%
SHIFT
GOTO processArgs
:argIntegration
SET INTEGRATION=1
SHIFT
GOTO processArgs
:argNugetFolder
SET nuGetFolder=%VALUE%
SHIFT
GOTO processArgs
:argSkipNuget
SET SKIPNUGET=1
SHIFT
GOTO processArgs
:endProcessArgs
REM run
SET VERSION=%RELEASE%
IF [%COMMENT%] EQU [] (SET VERSION=%RELEASE%) ELSE (SET VERSION=%RELEASE%-%COMMENT%)
ECHO ################################################################
ECHO Building Umbraco %VERSION%
ECHO ################################################################
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
SET PATH="%MSBUILDPATH%";%PATH%
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %RELEASE%
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %release%
ECHO.
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
@@ -99,10 +35,10 @@ RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
ECHO.
ECHO Removing existing built files to make sure everything is clean as a whistle
RMDIR /Q /S _BuildOutput
DEL /F /Q UmbracoCms.*.zip 2>NUL
DEL /F /Q UmbracoExamine.*.zip 2>NUL
DEL /F /Q UmbracoCms.*.nupkg 2>NUL
DEL /F /Q webpihash.txt 2>NUL
DEL /F /Q UmbracoCms.*.zip
DEL /F /Q UmbracoExamine.*.zip
DEL /F /Q UmbracoCms.*.nupkg
DEL /F /Q webpihash.txt
ECHO.
ECHO Making sure Git is in the path so that the build can succeed
@@ -111,21 +47,11 @@ CALL InstallGit.cmd
REM Adding the default Git path so that if it's installed it can actually be found
REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might find Git,
REM but the path setting is lost due to SETLOCAL
SET PATH="C:\Program Files (x86)\Git\cmd";"C:\Program Files\Git\cmd";%PATH%
path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
ECHO.
ECHO Making sure we have a web.config
IF NOT EXIST "%CD%\..\src\Umbraco.Web.UI\web.config" COPY "%CD%\..\src\Umbraco.Web.UI\web.Template.config" "%CD%\..\src\Umbraco.Web.UI\web.config"
ECHO.
ECHO Reporting NuGet version
"%CD%\..\src\.nuget\NuGet.exe" help | findstr "^NuGet Version:"
ECHO.
ECHO Restoring NuGet packages
ECHO Into %nuGetFolder%
"%CD%\..\src\.nuget\NuGet.exe" restore "%CD%\..\src\umbraco.sln" -Verbosity Quiet -NonInteractive -PackagesDirectory "%nuGetFolder%"
IF ERRORLEVEL 1 GOTO :error
IF NOT EXIST %CD%\..\src\Umbraco.Web.UI\web.config COPY %CD%\..\src\Umbraco.Web.UI\web.Template.config %CD%\..\src\Umbraco.Web.UI\web.config
ECHO.
ECHO.
@@ -134,15 +60,13 @@ ECHO This takes a few minutes and logging is set to report warnings
ECHO and errors only so it might seems like nothing is happening for a while.
ECHO You can check the msbuild.log file for progress.
ECHO.
%MSBUILD% "Build.proj" /p:BUILD_RELEASE=%RELEASE% /p:BUILD_COMMENT=%COMMENT% /p:NugetPackagesDirectory="%nuGetFolder%" /consoleloggerparameters:Summary;ErrorsOnly /fileLogger
IF ERRORLEVEL 1 GOTO error
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /fileLogger
IF ERRORLEVEL 1 GOTO :error
ECHO.
ECHO Setting node_modules folder to hidden to prevent VS13 from crashing on it while loading the websites project
attrib +h ..\src\Umbraco.Web.UI.Client\node_modules
IF %SKIPNUGET% EQU 1 GOTO success
ECHO.
ECHO Adding Web.config transform files to the NuGet package
REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
@@ -150,25 +74,22 @@ REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO.
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %VERSION% -Symbols -Verbosity quiet
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %VERSION% -Verbosity quiet
IF ERRORLEVEL 1 GOTO error
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% -Symbols -Verbosity quiet
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version% -Verbosity quiet
IF ERRORLEVEL 1 GOTO :error
:success
ECHO.
ECHO No errors were detected!
ECHO There may still be some in the output, which you would need to investigate.
ECHO Warnings are usually normal.
ECHO.
ECHO.
GOTO :EOF
:error
ECHO.
ECHO Errors were detected!
ECHO.
REM don't pause if continuous integration else the build server waits forever
REM before cancelling the build (and, there is noone to read the output anyways)
IF %INTEGRATION% NEQ 1 PAUSE
IF isci NEQ 1 PAUSE
+20 -18
View File
@@ -1,8 +1,8 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
<!--
****************************************************
INCLUDES
INCLUDES
*****************************************************
-->
@@ -35,9 +35,9 @@
{
using (var cryptoProvider = new SHA1CryptoServiceProvider())
{
var fileHash = cryptoProvider.ComputeHash(stream);
using (TextWriter w = new StreamWriter(OutputFile, false))
{
w.WriteLine(string.Join("", fileHash.Select(b => b.ToString("x2"))));
@@ -52,9 +52,9 @@
</UsingTask>
<!--
<!--
****************************************************
VARIABLES
VARIABLES
*****************************************************
-->
@@ -102,9 +102,9 @@
<SystemFolders Include="$(WebAppFolder)Views" />
</ItemGroup>
<!--
<!--
****************************************************
TARGETS
TARGETS
*****************************************************
-->
@@ -155,7 +155,7 @@
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
</Target>
<Target Name="CreateSystemFolders" DependsOnTargets="CopyBelleBuild" Inputs="@(SystemFolders)" Outputs="%(Identity).Dummy">
<MakeDir Directories="@(SystemFolders)" />
</Target>
@@ -231,7 +231,7 @@
DestinationFiles="@(CustomLanguageFiles->'$(ConfigsFolder)Lang\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(WebConfigTransformFile)"
DestinationFiles="$(ConfigsFolder)Web.config.transform"
OverwriteReadOnlyFiles="true"
@@ -264,12 +264,14 @@
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
<!-- For UseWPP_CopyWebApplication=True see http://stackoverflow.com/questions/1983575/copywebapplication-with-web-config-transformations -->
<!-- Build the Umbraco.Web.UI project -->
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath);Verbosity=minimal" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath);Verbosity=minimal" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False">
</MSBuild>
<!-- DONE -->
<Message Text="Finished compiling projects" Importance="high" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<PropertyGroup>
<NewVersion>$(BUILD_RELEASE)</NewVersion>
@@ -305,34 +307,34 @@
ReplacementText="AssemblyFileVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
<FileUpdate
<FileUpdate
Condition="'$(BUILD_COMMENT)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_COMMENT)&quot;)"/>
<FileUpdate
<FileUpdate
Condition="'$(BUILD_COMMENT)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<FileUpdate
<FileUpdate
Condition="'$(BUILD_NIGHTLY)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_NIGHTLY)&quot;)"/>
<FileUpdate
<FileUpdate
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)&quot;)"/>
<FileUpdate
<FileUpdate
Condition="'$(BUILD_COMMENT)'=='' AND '$(BUILD_NIGHTLY)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the copyright year-->
<FileUpdate
<FileUpdate
Files="..\src\SolutionInfo.cs"
Regex="AssemblyCopyright\(&quot;Copyright © Umbraco (\d{4})&quot;\)"
ReplacementText="AssemblyCopyright(&quot;Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))&quot;)"/>
+3 -3
View File
@@ -1,18 +1,18 @@
@ECHO OFF
SETLOCAL
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
:: SETLOCAL is on, so changes to the path not persist to the actual user's path
git.exe --version
IF %ERRORLEVEL%==9009 GOTO :trydefaultpath
REM OK, DONE
GOTO :EOF
:: Git is installed, no need to to anything else
:trydefaultpath
PATH=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
git.exe --version
IF %ERRORLEVEL%==9009 GOTO :showerror
REM OK, DONE
GOTO :EOF
:: Git is installed, no need to to anything else
:showerror
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
+4 -5
View File
@@ -33,13 +33,12 @@
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.4.5, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.6.6, 5.0.0)" />
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.7.2, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.6.0, 5.0.0)" />
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
</dependencies>
</metadata>
<files>
@@ -104,4 +103,4 @@
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
</files>
</package>
</package>
+2 -1
View File
@@ -18,7 +18,7 @@
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
</dependencies>
</metadata>
<files>
@@ -44,6 +44,7 @@
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
+1
View File
@@ -71,6 +71,7 @@ if ($project) {
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll) { Remove-Item $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false }
@@ -0,0 +1 @@
<processing xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="SetAttributes(preserveExifMetaData)" preserveExifMetaData="true" />
+2 -3
View File
@@ -1,3 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
alpha038
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.5.7
Binary file not shown.
-4
View File
@@ -49,13 +49,9 @@
<Reference Include="System.Core" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
+11 -24
View File
@@ -180,16 +180,12 @@ namespace SqlCE4Umbraco
/// <returns>The return value of the command.</returns>
protected override object ExecuteScalar(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteScalar: " + commandText);
#endif
using (var cc = UseCurrentConnection)
{
return SqlCeApplicationBlock.ExecuteScalar(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters);
}
#endif
return SqlCeApplicationBlock.ExecuteScalar(ConnectionString, CommandType.Text, commandText, parameters);
}
/// <summary>
@@ -202,17 +198,12 @@ namespace SqlCE4Umbraco
/// </returns>
protected override int ExecuteNonQuery(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteNonQuery: " + commandText);
#endif
#endif
using (var cc = UseCurrentConnection)
{
return SqlCeApplicationBlock.ExecuteNonQuery(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters);
}
return SqlCeApplicationBlock.ExecuteNonQuery(ConnectionString, CommandType.Text, commandText, parameters);
}
/// <summary>
@@ -225,17 +216,13 @@ namespace SqlCE4Umbraco
/// </returns>
protected override IRecordsReader ExecuteReader(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteReader: " + commandText);
#endif
#endif
using (var cc = UseCurrentConnection)
{
return new SqlCeDataReaderHelper(SqlCeApplicationBlock.ExecuteReader(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters));
}
return new SqlCeDataReaderHelper(SqlCeApplicationBlock.ExecuteReader(ConnectionString, CommandType.Text,
commandText, parameters));
}
+93 -175
View File
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data;
using System.Diagnostics;
@@ -23,60 +25,29 @@ namespace SqlCE4Umbraco
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
{
return ExecuteScalarTry(conn, null, commandText, commandParameters);
}
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Scalar: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee);
}
}
public static object ExecuteScalar(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters)
{
try
{
return ExecuteScalarTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Scalar: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee);
}
}
public static object ExecuteScalar(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters)
{
return ExecuteScalar(conn, null, commandType, commandText, commandParameters);
}
private static object ExecuteScalarTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
object retVal;
using (var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx))
try
{
AttachParameters(cmd, commandParameters);
Debug.WriteLine("---------------------------------SCALAR-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
retVal = cmd.ExecuteScalar();
using (SqlCeConnection conn = SqlCeContextGuardian.Open(connectionString))
{
using (SqlCeCommand cmd = new SqlCeCommand(commandText, conn))
{
AttachParameters(cmd, commandParameters);
Debug.WriteLine("---------------------------------SCALAR-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
retVal = cmd.ExecuteScalar();
}
}
return retVal;
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Scalar: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
return retVal;
}
/// <summary>
@@ -95,10 +66,49 @@ namespace SqlCE4Umbraco
{
try
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
int rowsAffected;
using (SqlCeConnection conn = SqlCeContextGuardian.Open(connectionString))
{
return ExecuteNonQueryTry(conn, null, commandText, commandParameters);
// this is for multiple queries in the installer
if (commandText.Trim().StartsWith("!!!"))
{
commandText = commandText.Trim().Trim('!');
string[] commands = commandText.Split('|');
string currentCmd = String.Empty;
foreach (string cmd in commands)
{
try
{
currentCmd = cmd;
if (!String.IsNullOrWhiteSpace(cmd))
{
SqlCeCommand c = new SqlCeCommand(cmd, conn);
c.ExecuteNonQuery();
}
}
catch (Exception e)
{
Debug.WriteLine("*******************************************************************");
Debug.WriteLine(currentCmd);
Debug.WriteLine(e);
Debug.WriteLine("*******************************************************************");
}
}
return 1;
}
else
{
Debug.WriteLine("----------------------------------------------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
SqlCeCommand cmd = new SqlCeCommand(commandText, conn);
AttachParameters(cmd, commandParameters);
rowsAffected = cmd.ExecuteNonQuery();
}
}
return rowsAffected;
}
catch (Exception ee)
{
@@ -106,74 +116,6 @@ namespace SqlCE4Umbraco
}
}
public static int ExecuteNonQuery(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
return ExecuteNonQuery(conn, null, commandType, commandText, commandParameters);
}
public static int ExecuteNonQuery(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
return ExecuteNonQueryTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running NonQuery: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
private static int ExecuteNonQueryTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
// this is for multiple queries in the installer
if (commandText.Trim().StartsWith("!!!"))
{
commandText = commandText.Trim().Trim('!');
var commands = commandText.Split('|');
var currentCmd = string.Empty;
foreach (var command in commands)
{
try
{
currentCmd = command;
if (string.IsNullOrWhiteSpace(command)) continue;
var c = trx == null ? new SqlCeCommand(command, conn) : new SqlCeCommand(command, conn, trx);
c.ExecuteNonQuery();
}
catch (Exception e)
{
Debug.WriteLine("*******************************************************************");
Debug.WriteLine(currentCmd);
Debug.WriteLine(e);
Debug.WriteLine("*******************************************************************");
}
}
return 1;
}
Debug.WriteLine("----------------------------------------------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
var cmd = new SqlCeCommand(commandText, conn);
AttachParameters(cmd, commandParameters);
var rowsAffected = cmd.ExecuteNonQuery();
return rowsAffected;
}
/// <summary>
///
/// </summary>
@@ -191,8 +133,25 @@ namespace SqlCE4Umbraco
{
try
{
var conn = SqlCeContextGuardian.Open(connectionString);
return ExecuteReaderTry(conn, null, commandText, commandParameters);
Debug.WriteLine("---------------------------------READER-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
SqlCeDataReader reader;
SqlCeConnection conn = SqlCeContextGuardian.Open(connectionString);
try
{
SqlCeCommand cmd = new SqlCeCommand(commandText, conn);
AttachParameters(cmd, commandParameters);
reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
conn.Close();
throw;
}
return reader;
}
catch (Exception ee)
{
@@ -200,71 +159,30 @@ namespace SqlCE4Umbraco
}
}
public static SqlCeDataReader ExecuteReader(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
return ExecuteReader(conn, commandType, commandText, commandParameters);
}
public static SqlCeDataReader ExecuteReader(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
return ExecuteReaderTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Reader: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
private static SqlCeDataReader ExecuteReaderTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
Debug.WriteLine("---------------------------------READER-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
try
{
var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx);
AttachParameters(cmd, commandParameters);
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
conn.Close();
throw;
}
}
public static bool VerifyConnection(string connectionString)
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
bool isConnected = false;
using (SqlCeConnection conn = SqlCeContextGuardian.Open(connectionString))
{
return conn.State == ConnectionState.Open;
isConnected = conn.State == ConnectionState.Open;
}
return isConnected;
}
private static void AttachParameters(SqlCeCommand command, IEnumerable<SqlCeParameter> commandParameters)
private static void AttachParameters(SqlCeCommand command, SqlCeParameter[] commandParameters)
{
foreach (var parameter in commandParameters)
foreach (SqlCeParameter parameter in commandParameters)
{
if ((parameter.Direction == ParameterDirection.InputOutput) && (parameter.Value == null))
{
parameter.Value = DBNull.Value;
}
command.Parameters.Add(parameter);
}
}
}
}
+3 -3
View File
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2016")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha038")]
[assembly: AssemblyFileVersion("7.5.7")]
[assembly: AssemblyInformationalVersion("7.5.7")]
-39
View File
@@ -1,39 +0,0 @@
namespace Umbraco.Core
{
public static class ByteArrayExtensions
{
private static readonly char[] BytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static string ToHexString(this byte[] bytes)
{
int i = 0, p = 0, bytesLength = bytes.Length;
var chars = new char[bytesLength * 2];
while (i < bytesLength)
{
var b = bytes[i++];
chars[p++] = BytesToHexStringLookup[b / 0x10];
chars[p++] = BytesToHexStringLookup[b % 0x10];
}
return new string(chars, 0, chars.Length);
}
public static string ToHexString(this byte[] bytes, char separator, int blockSize, int blockCount)
{
int p = 0, bytesLength = bytes.Length, count = 0, size = 0;
var chars = new char[bytesLength * 2 + blockCount];
for (var i = 0; i < bytesLength; i++)
{
var b = bytes[i++];
chars[p++] = BytesToHexStringLookup[b / 0x10];
chars[p++] = BytesToHexStringLookup[b % 0x10];
if (count == blockCount) continue;
if (++size < blockSize) continue;
chars[p++] = '/';
size = 0;
count++;
}
return new string(chars, 0, chars.Length);
}
}
}
@@ -154,7 +154,7 @@ namespace Umbraco.Core.Cache
value = result.Value; // will not throw (safe lazy)
var eh = value as ExceptionHolder;
if (eh != null) throw new Exception("Exception while creating a value.", eh.Exception); // throw once!
if (eh != null) throw eh.Exception; // throw once!
return value;
}
@@ -4,8 +4,7 @@ using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ContentElement : ConfigurationElement, IContentSection
internal class ContentElement : UmbracoConfigurationElement, IContentSection
{
[ConfigurationProperty("imaging")]
internal ContentImagingElement Imaging
@@ -22,25 +21,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("ResolveUrlsFromTextString")]
internal InnerTextConfigurationElement<bool> ResolveUrlsFromTextString
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["ResolveUrlsFromTextString"],
//set the default
false);
}
get { return GetOptionalTextElement<bool>("ResolveUrlsFromTextString", false); }
}
[ConfigurationProperty("UploadAllowDirectories")]
internal InnerTextConfigurationElement<bool> UploadAllowDirectories
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["UploadAllowDirectories"],
//set the default
true);
}
get { return GetOptionalTextElement("UploadAllowDirectories", true); }
}
public IEnumerable<IContentErrorPage> Error404Collection
@@ -63,121 +50,61 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("ensureUniqueNaming")]
internal InnerTextConfigurationElement<bool> EnsureUniqueNaming
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["ensureUniqueNaming"],
//set the default
true);
}
get { return GetOptionalTextElement("ensureUniqueNaming", true); }
}
[ConfigurationProperty("TidyEditorContent")]
internal InnerTextConfigurationElement<bool> TidyEditorContent
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["TidyEditorContent"],
//set the default
false);
}
get { return GetOptionalTextElement("TidyEditorContent", false); }
}
[ConfigurationProperty("TidyCharEncoding")]
internal InnerTextConfigurationElement<string> TidyCharEncoding
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["TidyCharEncoding"],
//set the default
"UTF8");
}
get { return GetOptionalTextElement("TidyCharEncoding", "UTF8"); }
}
[ConfigurationProperty("XmlCacheEnabled")]
internal InnerTextConfigurationElement<bool> XmlCacheEnabled
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["XmlCacheEnabled"],
//set the default
true);
}
get { return GetOptionalTextElement("XmlCacheEnabled", true); }
}
[ConfigurationProperty("ContinouslyUpdateXmlDiskCache")]
internal InnerTextConfigurationElement<bool> ContinouslyUpdateXmlDiskCache
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["ContinouslyUpdateXmlDiskCache"],
//set the default
true);
}
get { return GetOptionalTextElement("ContinouslyUpdateXmlDiskCache", true); }
}
[ConfigurationProperty("XmlContentCheckForDiskChanges")]
internal InnerTextConfigurationElement<bool> XmlContentCheckForDiskChanges
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["XmlContentCheckForDiskChanges"],
//set the default
false);
}
get { return GetOptionalTextElement("XmlContentCheckForDiskChanges", false); }
}
[ConfigurationProperty("EnableSplashWhileLoading")]
internal InnerTextConfigurationElement<bool> EnableSplashWhileLoading
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["EnableSplashWhileLoading"],
//set the default
false);
}
get { return GetOptionalTextElement("EnableSplashWhileLoading", false); }
}
[ConfigurationProperty("PropertyContextHelpOption")]
internal InnerTextConfigurationElement<string> PropertyContextHelpOption
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["PropertyContextHelpOption"],
//set the default
"text");
}
get { return GetOptionalTextElement("PropertyContextHelpOption", "text"); }
}
[ConfigurationProperty("UseLegacyXmlSchema")]
internal InnerTextConfigurationElement<bool> UseLegacyXmlSchema
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["UseLegacyXmlSchema"],
//set the default
false);
}
get { return GetOptionalTextElement("UseLegacyXmlSchema", false); }
}
[ConfigurationProperty("ForceSafeAliases")]
internal InnerTextConfigurationElement<bool> ForceSafeAliases
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["ForceSafeAliases"],
//set the default
true);
}
get { return GetOptionalTextElement("ForceSafeAliases", true); }
}
[ConfigurationProperty("PreviewBadge")]
@@ -185,123 +112,63 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["PreviewBadge"],
//set the default
@"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={2}""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
return GetOptionalTextElement("PreviewBadge", @"<a id=""umbracoPreviewBadge"" style=""position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;"" href=""{0}/endPreview.aspx?redir={2}""><span style=""display:none;"">In Preview Mode - click to end</span></a>");
}
}
[ConfigurationProperty("UmbracoLibraryCacheDuration")]
internal InnerTextConfigurationElement<int> UmbracoLibraryCacheDuration
{
get
{
return new OptionalInnerTextConfigurationElement<int>(
(InnerTextConfigurationElement<int>)this["UmbracoLibraryCacheDuration"],
//set the default
1800);
}
get { return GetOptionalTextElement("UmbracoLibraryCacheDuration", 1800); }
}
[ConfigurationProperty("MacroErrors")]
internal InnerTextConfigurationElement<MacroErrorBehaviour> MacroErrors
{
get
{
return new OptionalInnerTextConfigurationElement<MacroErrorBehaviour>(
(InnerTextConfigurationElement<MacroErrorBehaviour>)this["MacroErrors"],
//set the default
MacroErrorBehaviour.Inline);
}
get { return GetOptionalTextElement("MacroErrors", MacroErrorBehaviour.Inline); }
}
[Obsolete("This is here so that if this config element exists we won't get a YSOD, it is not used whatsoever and will be removed in future versions")]
[ConfigurationProperty("DocumentTypeIconList")]
internal InnerTextConfigurationElement<IconPickerBehaviour> DocumentTypeIconList
{
get
{
return new OptionalInnerTextConfigurationElement<IconPickerBehaviour>(
(InnerTextConfigurationElement<IconPickerBehaviour>)this["DocumentTypeIconList"],
//set the default
IconPickerBehaviour.HideFileDuplicates);
}
get { return GetOptionalTextElement("DocumentTypeIconList", IconPickerBehaviour.HideFileDuplicates); }
}
[ConfigurationProperty("disallowedUploadFiles")]
internal CommaDelimitedConfigurationElement DisallowedUploadFiles
{
get
{
return new OptionalCommaDelimitedConfigurationElement(
(CommaDelimitedConfigurationElement)this["disallowedUploadFiles"],
//set the default
new[] { "ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd" });
}
get { return GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); }
}
[ConfigurationProperty("cloneXmlContent")]
internal InnerTextConfigurationElement<bool> CloneXmlContent
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["cloneXmlContent"],
//set the default
true);
}
get { return GetOptionalTextElement("cloneXmlContent", true); }
}
[ConfigurationProperty("GlobalPreviewStorageEnabled")]
internal InnerTextConfigurationElement<bool> GlobalPreviewStorageEnabled
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["GlobalPreviewStorageEnabled"],
//set the default
false);
}
get { return GetOptionalTextElement("GlobalPreviewStorageEnabled", false); }
}
[ConfigurationProperty("defaultDocumentTypeProperty")]
internal InnerTextConfigurationElement<string> DefaultDocumentTypeProperty
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["defaultDocumentTypeProperty"],
//set the default
"Textstring");
}
get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); }
}
[ConfigurationProperty("EnableInheritedDocumentTypes")]
internal InnerTextConfigurationElement<bool> EnableInheritedDocumentTypes
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>) this["EnableInheritedDocumentTypes"],
//set the default
true);
}
get { return GetOptionalTextElement("EnableInheritedDocumentTypes", true); }
}
[ConfigurationProperty("EnableInheritedMediaTypes")]
internal InnerTextConfigurationElement<bool> EnableInheritedMediaTypes
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["EnableInheritedMediaTypes"],
//set the default
true);
}
get { return GetOptionalTextElement("EnableInheritedMediaTypes", true); }
}
string IContentSection.NotificationEmailAddress
@@ -3,42 +3,24 @@ using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ContentScriptEditorElement : ConfigurationElement
internal class ContentScriptEditorElement : UmbracoConfigurationElement
{
[ConfigurationProperty("scriptFolderPath")]
internal InnerTextConfigurationElement<string> ScriptFolderPath
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["scriptFolderPath"],
//set the default
"/scripts");
}
get { return GetOptionalTextElement("scriptFolderPath", "/scripts"); }
}
[ConfigurationProperty("scriptFileTypes")]
internal OptionalCommaDelimitedConfigurationElement ScriptFileTypes
{
get
{
return new OptionalCommaDelimitedConfigurationElement(
(OptionalCommaDelimitedConfigurationElement)this["scriptFileTypes"],
//set the default
new[] { "js", "xml" });
}
get { return GetOptionalDelimitedElement("scriptFileTypes", new[] {"js", "xml"}); }
}
[ConfigurationProperty("scriptDisableEditor")]
internal InnerTextConfigurationElement<bool> ScriptEditorDisable
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>) this["scriptDisableEditor"],
//set the default
false);
}
get { return GetOptionalTextElement("scriptDisableEditor", false); }
}
}
@@ -2,7 +2,7 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ImagingAutoFillUploadFieldElement : ConfigurationElement, IImagingAutoFillUploadField
internal class ImagingAutoFillUploadFieldElement : UmbracoConfigurationElement, IImagingAutoFillUploadField
{
/// <summary>
/// Allow setting internally so we can create a default
@@ -17,49 +17,25 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("widthFieldAlias")]
internal InnerTextConfigurationElement<string> WidthFieldAlias
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["widthFieldAlias"],
//set the default
"umbracoWidth");
}
get { return GetOptionalTextElement("widthFieldAlias", "umbracoWidth"); }
}
[ConfigurationProperty("heightFieldAlias")]
internal InnerTextConfigurationElement<string> HeightFieldAlias
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["heightFieldAlias"],
//set the default
"umbracoHeight");
}
get { return GetOptionalTextElement("heightFieldAlias", "umbracoHeight"); }
}
[ConfigurationProperty("lengthFieldAlias")]
internal InnerTextConfigurationElement<string> LengthFieldAlias
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["lengthFieldAlias"],
//set the default
"umbracoBytes");
}
get { return GetOptionalTextElement("lengthFieldAlias", "umbracoBytes"); }
}
[ConfigurationProperty("extensionFieldAlias")]
internal InnerTextConfigurationElement<string> ExtensionFieldAlias
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["extensionFieldAlias"],
//set the default
"umbracoExtension");
}
get { return GetOptionalTextElement("extensionFieldAlias", "umbracoExtension"); }
}
string IImagingAutoFillUploadField.Alias
@@ -3,67 +3,37 @@ using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class LoggingElement : ConfigurationElement, ILoggingSection
internal class LoggingElement : UmbracoConfigurationElement, ILoggingSection
{
[ConfigurationProperty("autoCleanLogs")]
internal InnerTextConfigurationElement<bool> AutoCleanLogs
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["autoCleanLogs"],
//set the default
false);
}
get { return GetOptionalTextElement("autoCleanLogs", false); }
}
[ConfigurationProperty("enableLogging")]
internal InnerTextConfigurationElement<bool> EnableLogging
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["enableLogging"],
//set the default
true);
}
get { return GetOptionalTextElement("enableLogging", true); }
}
[ConfigurationProperty("enableAsyncLogging")]
internal InnerTextConfigurationElement<bool> EnableAsyncLogging
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["enableAsyncLogging"],
//set the default
true);
}
get { return GetOptionalTextElement("enableAsyncLogging", true); }
}
[ConfigurationProperty("cleaningMiliseconds")]
internal InnerTextConfigurationElement<int> CleaningMiliseconds
{
get
{
return new OptionalInnerTextConfigurationElement<int>(
(InnerTextConfigurationElement<int>)this["cleaningMiliseconds"],
//set the default
-1);
}
get { return GetOptionalTextElement("cleaningMiliseconds", -1); }
}
[ConfigurationProperty("maxLogAge")]
internal InnerTextConfigurationElement<int> MaxLogAge
{
get
{
return new OptionalInnerTextConfigurationElement<int>(
(InnerTextConfigurationElement<int>)this["maxLogAge"],
//set the default
-1);
}
get { return GetOptionalTextElement("maxLogAge", -1); }
}
[ConfigurationCollection(typeof(DisabledLogTypesCollection), AddItemName = "logTypeAlias")]
@@ -2,7 +2,7 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class NotificationsElement : ConfigurationElement
internal class NotificationsElement : UmbracoConfigurationElement
{
[ConfigurationProperty("email")]
internal InnerTextConfigurationElement<string> NotificationEmailAddress
@@ -13,13 +13,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("disableHtmlEmail")]
internal InnerTextConfigurationElement<bool> DisableHtmlEmail
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>) this["disableHtmlEmail"],
//set the default
false);
}
get { return GetOptionalTextElement("disableHtmlEmail", false); }
}
}
@@ -5,30 +5,18 @@ using System.Collections.Generic;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class RequestHandlerElement : ConfigurationElement, IRequestHandlerSection
internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSection
{
[ConfigurationProperty("useDomainPrefixes")]
public InnerTextConfigurationElement<bool> UseDomainPrefixes
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["useDomainPrefixes"],
//set the default
false);
}
get { return GetOptionalTextElement("useDomainPrefixes", false); }
}
[ConfigurationProperty("addTrailingSlash")]
public InnerTextConfigurationElement<bool> AddTrailingSlash
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["addTrailingSlash"],
//set the default
true);
}
get { return GetOptionalTextElement("addTrailingSlash", true); }
}
private UrlReplacingElement _defaultUrlReplacing;
@@ -2,66 +2,36 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class SecurityElement : ConfigurationElement, ISecuritySection
internal class SecurityElement : UmbracoConfigurationElement, ISecuritySection
{
[ConfigurationProperty("keepUserLoggedIn")]
internal InnerTextConfigurationElement<bool> KeepUserLoggedIn
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["keepUserLoggedIn"],
//set the default
true);
}
get { return GetOptionalTextElement("keepUserLoggedIn", true); }
}
[ConfigurationProperty("hideDisabledUsersInBackoffice")]
internal InnerTextConfigurationElement<bool> HideDisabledUsersInBackoffice
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["hideDisabledUsersInBackoffice"],
//set the default
false);
}
get { return GetOptionalTextElement("hideDisabledUsersInBackoffice", false); }
}
[ConfigurationProperty("allowPasswordReset")]
internal InnerTextConfigurationElement<bool> AllowPasswordReset
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["allowPasswordReset"],
//set the default
true);
}
get { return GetOptionalTextElement("allowPasswordReset", true); }
}
[ConfigurationProperty("authCookieName")]
internal InnerTextConfigurationElement<string> AuthCookieName
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["authCookieName"],
//set the default
Constants.Web.AuthCookieName);
}
get { return GetOptionalTextElement("authCookieName", Constants.Web.AuthCookieName); }
}
[ConfigurationProperty("authCookieDomain")]
internal InnerTextConfigurationElement<string> AuthCookieDomain
{
get
{
return new OptionalInnerTextConfigurationElement<string>(
(InnerTextConfigurationElement<string>)this["authCookieDomain"],
//set the default
null);
}
get { return GetOptionalTextElement<string>("authCookieDomain", null); }
}
bool ISecuritySection.KeepUserLoggedIn
@@ -3,55 +3,31 @@ using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class TemplatesElement : ConfigurationElement, ITemplatesSection
internal class TemplatesElement : UmbracoConfigurationElement, ITemplatesSection
{
[ConfigurationProperty("useAspNetMasterPages")]
internal InnerTextConfigurationElement<bool> UseAspNetMasterPages
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["useAspNetMasterPages"],
//set the default
true);
}
get { return GetOptionalTextElement("useAspNetMasterPages", true); }
}
[ConfigurationProperty("enableSkinSupport")]
internal InnerTextConfigurationElement<bool> EnableSkinSupport
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["enableSkinSupport"],
//set the default
true);
}
get { return GetOptionalTextElement("enableSkinSupport", true); }
}
[ConfigurationProperty("defaultRenderingEngine", IsRequired = true)]
internal InnerTextConfigurationElement<RenderingEngine> DefaultRenderingEngine
{
get
{
return new OptionalInnerTextConfigurationElement<RenderingEngine>(
(InnerTextConfigurationElement<RenderingEngine>)this["defaultRenderingEngine"],
//set the default
RenderingEngine.Mvc);
}
get { return GetOptionalTextElement("defaultRenderingEngine", RenderingEngine.Mvc); }
}
[Obsolete("This has no affect and will be removed in future versions")]
[ConfigurationProperty("enableTemplateFolders")]
internal InnerTextConfigurationElement<bool> EnableTemplateFolders
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)this["enableTemplateFolders"],
//set the default
false);
}
get { return GetOptionalTextElement("enableTemplateFolders", false); }
}
bool ITemplatesSection.UseAspNetMasterPages
@@ -0,0 +1,36 @@
using System.Collections.Concurrent;
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
/// <summary>
/// Base class with shared helper methods
/// </summary>
internal class UmbracoConfigurationElement : ConfigurationElement
{
/// <summary>
/// Used so the RawElement types are not re-created every time they are accessed
/// </summary>
private readonly ConcurrentDictionary<string, RawXmlConfigurationElement> _rawElements = new ConcurrentDictionary<string, RawXmlConfigurationElement>();
protected OptionalInnerTextConfigurationElement<T> GetOptionalTextElement<T>(string name, T defaultVal)
{
return (OptionalInnerTextConfigurationElement<T>) _rawElements.GetOrAdd(
name,
s => new OptionalInnerTextConfigurationElement<T>(
(InnerTextConfigurationElement<T>) this[s],
//set the default
defaultVal));
}
protected OptionalCommaDelimitedConfigurationElement GetOptionalDelimitedElement(string name, string[] defaultVal)
{
return (OptionalCommaDelimitedConfigurationElement) _rawElements.GetOrAdd(
name,
s => new OptionalCommaDelimitedConfigurationElement(
(CommaDelimitedConfigurationElement) this[name],
//set the default
defaultVal));
}
}
}
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.6.0");
private static readonly Version Version = new Version("7.5.7");
/// <summary>
/// Gets the current version of Umbraco.
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "alpha038"; } }
public static string CurrentComment { get { return ""; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
+3
View File
@@ -25,6 +25,9 @@
public const int DefaultContentListViewDataTypeId = -95;
public const int DefaultMediaListViewDataTypeId = -96;
public const int DefaultMembersListViewDataTypeId = -97;
// identifiers for lock objects
public const int ServersLock = -331;
}
public static class DatabaseProviders
+40 -101
View File
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Xml.Linq;
using Semver;
using Umbraco.Core.Configuration;
@@ -30,6 +30,7 @@ namespace Umbraco.Core
private readonly ILogger _logger;
private readonly SqlSyntaxProviders _syntaxProviders;
private bool _configured;
private readonly object _locker = new object();
private string _connectionString;
private string _providerName;
private DatabaseSchemaResult _result;
@@ -45,7 +46,7 @@ namespace Umbraco.Core
: this(factory, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[]
{
new MySqlSyntaxProvider(LoggerResolver.Current.Logger),
new SqlCeSyntaxProvider(),
new SqlCeSyntaxProvider(),
new SqlServerSyntaxProvider()
}))
{
@@ -85,60 +86,21 @@ namespace Umbraco.Core
_configured = true;
}
#if DEBUG_DATABASES
public List<UmbracoDatabase> Databases
{
get
{
var factory = _factory as DefaultDatabaseFactory;
if (factory == null) throw new NotSupportedException();
return factory.Databases;
}
}
#endif
public ISqlSyntaxProvider SqlSyntax { get; private set; }
/// <summary>
/// Gets an "ambient" database for doing CRUD operations against custom tables that resides in the Umbraco database.
/// Gets the <see cref="Database"/> object for doing CRUD operations
/// against custom tables that resides in the Umbraco database.
/// </summary>
/// <remarks>
/// <para>Should not be used for operation against standard Umbraco tables; as services should be used instead.</para>
/// <para>Gets or creates an "ambient" database that is either stored in http context + available for the whole
/// request + auto-disposed at the end of the request, or stored in call context if there is no http context - in which
/// case it *must* be explicitely disposed (which will remove it from call context).</para>
/// This should not be used for CRUD operations or queries against the
/// standard Umbraco tables! Use the Public services for that.
/// </remarks>
public virtual UmbracoDatabase Database
{
get { return _factory.CreateDatabase(); }
}
/// <summary>
/// Replaces the "ambient" database (if any) and by a new temp database.
/// </summary>
/// <remarks>
/// <para>The returned IDisposable *must* be diposed in order to properly dispose the temp database and
/// restore the original "ambient" database (if any).</para>
/// <para>This is to be used in background tasks to ensure that they have an "ambient" database which
/// will be properly removed from call context and does not interfere with anything else. In most case
/// it is not replacing anything, just temporarily installing a database in context.</para>
/// </remarks>
public virtual IDisposable UseSafeDatabase(bool force = false)
{
var factory = _factory as DefaultDatabaseFactory;
if (factory == null) throw new NotSupportedException();
if (DefaultDatabaseFactory.HasAmbientDatabase)
{
return force
? new UsingDatabase(DefaultDatabaseFactory.DetachAmbientDatabase(), factory.CreateDatabase())
: new UsingDatabase(null, null);
}
// create a new, temp, database (will be disposed with UsingDatabase)
return new UsingDatabase(null, factory.CreateDatabase());
}
/// <summary>
/// Boolean indicating whether the database has been configured
/// </summary>
@@ -248,7 +210,7 @@ namespace Umbraco.Core
// since it's been like this for quite some time
//using (var engine = new SqlCeEngine(connectionString))
//{
// engine.CreateDatabase();
// engine.CreateDatabase();
//}
}
@@ -289,14 +251,14 @@ namespace Umbraco.Core
/// <param name="password">Database Password</param>
/// <param name="databaseProvider">Type of the provider to be used (Sql, Sql Azure, Sql Ce, MySql)</param>
public void ConfigureDatabaseConnection(string server, string databaseName, string user, string password, string databaseProvider)
{
{
string providerName;
var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out providerName);
SaveConnectionString(connectionString, providerName);
Initialize(providerName);
}
public string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
{
providerName = Constants.DatabaseProviders.SqlServer;
@@ -327,18 +289,18 @@ namespace Umbraco.Core
public string GetIntegratedSecurityDatabaseConnectionString(string server, string databaseName)
{
return String.Format("Server={0};Database={1};Integrated Security=true", server, databaseName);
return String.Format("Server={0};Database={1};Integrated Security=true", server, databaseName);
}
internal string BuildAzureConnectionString(string server, string databaseName, string user, string password)
{
if (server.Contains(".") && ServerStartsWithTcp(server) == false)
server = string.Format("tcp:{0}", server);
if (server.Contains(".") == false && ServerStartsWithTcp(server))
{
string serverName = server.Contains(",")
? server.Substring(0, server.IndexOf(",", StringComparison.Ordinal))
string serverName = server.Contains(",")
? server.Substring(0, server.IndexOf(",", StringComparison.Ordinal))
: server;
var portAddition = string.Empty;
@@ -348,10 +310,10 @@ namespace Umbraco.Core
server = string.Format("{0}.database.windows.net{1}", serverName, portAddition);
}
if (ServerStartsWithTcp(server) == false)
server = string.Format("tcp:{0}.database.windows.net", server);
if (server.Contains(",") == false)
server = string.Format("{0},1433", server);
@@ -395,7 +357,7 @@ namespace Umbraco.Core
_connectionString = connectionString;
_providerName = providerName;
var fileName = IOHelper.MapPath(string.Format("{0}/web.config", SystemDirectories.Root));
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
var connectionstrings = xml.Root.DescendantsAndSelf("connectionStrings").Single();
@@ -440,7 +402,7 @@ namespace Umbraco.Core
connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
}
Initialize(providerName, connString);
}
else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false)
{
@@ -477,7 +439,7 @@ namespace Umbraco.Core
//Remove the legacy connection string, so we don't end up in a loop if something goes wrong.
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
}
else
{
@@ -496,15 +458,15 @@ namespace Umbraco.Core
{
if (_syntaxProviders != null)
{
SqlSyntax = _syntaxProviders.GetByProviderNameOrDefault(providerName);
SqlSyntax = _syntaxProviders.GetByProviderNameOrDefault(providerName);
}
else if (SqlSyntax == null)
{
throw new InvalidOperationException("No " + typeof(ISqlSyntaxProvider) + " specified or no " + typeof(SqlSyntaxProviders) + " instance specified");
}
SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax;
_configured = true;
}
catch (Exception e)
@@ -544,7 +506,7 @@ namespace Umbraco.Core
}
internal Result CreateDatabaseSchemaAndData(ApplicationContext applicationContext)
{
{
try
{
var readyForInstall = CheckReadyForInstall();
@@ -562,7 +524,7 @@ namespace Umbraco.Core
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
/*
/*
var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
if (supportsCaseInsensitiveQueries == false)
{
@@ -580,9 +542,9 @@ namespace Umbraco.Core
message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedSchemaVersion.Equals(new Version(0, 0, 0)))
{
@@ -601,9 +563,9 @@ namespace Umbraco.Core
message = "<p>Upgrading database, this may take some time...</p>";
return new Result
{
RequiresUpgrade = true,
Message = message,
Success = true,
RequiresUpgrade = true,
Message = message,
Success = true,
Percentage = "30"
};
}
@@ -631,7 +593,7 @@ namespace Umbraco.Core
_logger.Info<DatabaseContext>("Database upgrade started");
var database = new UmbracoDatabase(_connectionString, ProviderName, _logger);
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
var message = GetResultMessageForMySql();
@@ -639,32 +601,32 @@ namespace Umbraco.Core
var installedSchemaVersion = new SemVersion(schemaResult.DetermineInstalledVersion());
var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
var targetVersion = UmbracoVersion.Current;
//In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
// return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
// return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
// migrations are run for the target version
if (installedMigrationVersion == new SemVersion(new Version(0, 0, 0)) && installedSchemaVersion > new SemVersion(new Version(0, 0, 0)))
{
//set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
installedMigrationVersion = new SemVersion(targetVersion.SubtractRevision());
}
//Figure out what our current installed version is. If the web.config doesn't have a version listed, then we'll use the minimum
// version detected between the schema installed and the migrations listed in the migration table.
// version detected between the schema installed and the migrations listed in the migration table.
// If there is a version in the web.config, we'll take the minimum between the listed migration in the db and what
// is declared in the web.config.
var currentInstalledVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
//Take the minimum version between the detected schema version and the installed migration version
? new[] {installedSchemaVersion, installedMigrationVersion}.Min()
//Take the minimum version between the installed migration version and the version specified in the config
: new[] { SemVersion.Parse(GlobalSettings.ConfigurationStatus), installedMigrationVersion }.Min();
//Ok, another edge case here. If the current version is a pre-release,
// then we want to ensure all migrations for the current release are executed.
//Ok, another edge case here. If the current version is a pre-release,
// then we want to ensure all migrations for the current release are executed.
if (currentInstalledVersion.Prerelease.IsNullOrWhiteSpace() == false)
{
currentInstalledVersion = new SemVersion(currentInstalledVersion.GetVersion().SubtractRevision());
@@ -799,7 +761,7 @@ namespace Umbraco.Core
{
var datasource = dataSourcePart.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString());
var filePath = datasource.Replace("Data Source=", string.Empty);
sqlCeDatabaseExists = File.Exists(filePath);
sqlCeDatabaseExists = File.Exists(filePath);
}
}
@@ -813,28 +775,5 @@ namespace Umbraco.Core
return true;
}
private class UsingDatabase : IDisposable
{
private readonly UmbracoDatabase _orig;
private readonly UmbracoDatabase _temp;
public UsingDatabase(UmbracoDatabase orig, UmbracoDatabase temp)
{
_orig = orig;
_temp = temp;
}
public void Dispose()
{
if (_temp != null)
{
_temp.Dispose();
if (_orig != null)
DefaultDatabaseFactory.AttachAmbientDatabase(_orig);
}
GC.SuppressFinalize(this);
}
}
}
}
+1 -32
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core.Events
{
@@ -48,35 +47,5 @@ namespace Umbraco.Core.Events
if (eventHandler != null)
eventHandler(sender, args);
}
// moves the last handler that was added to an instance event, to first position
public static void PromoteLastHandler(object sender, string eventName)
{
var fieldInfo = sender.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
PromoteLastHandler(sender, fieldInfo);
}
// moves the last handler that was added to a static event, to first position
public static void PromoteLastHandler<TSender>(string eventName)
{
var fieldInfo = typeof(TSender).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
PromoteLastHandler(null, fieldInfo);
}
private static void PromoteLastHandler(object sender, FieldInfo fieldInfo)
{
var d = fieldInfo.GetValue(sender) as Delegate;
if (d == null) return;
var l = d.GetInvocationList();
var x = l[l.Length - 1];
for (var i = l.Length - 1; i > 0; i--)
l[i] = l[i - 1];
l[0] = x;
fieldInfo.SetValue(sender, Delegate.Combine(l));
}
}
}
}
-9
View File
@@ -1,9 +0,0 @@
using System;
namespace Umbraco.Core
{
public interface ICompletable : IDisposable
{
void Complete();
}
}
@@ -37,14 +37,8 @@ namespace Umbraco.Core.IO
throw new ArgumentException("Retries must be greater than zero");
}
// GetSize has been added to IFileSystem2 but not IFileSystem
// this is implementing GetSize for IFileSystem, the old way
public static long GetSize(this IFileSystem fs, string path)
{
// if we reach this point, fs is *not* IFileSystem2
// so it's not FileSystemWrapper nor shadow nor anything we know
// so... fall back to the old & inefficient method
using (var file = fs.OpenFile(path))
{
return file.Length;
+61 -193
View File
@@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using Umbraco.Core.CodeAnnotations;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.IO
@@ -11,27 +13,8 @@ namespace Umbraco.Core.IO
public class FileSystemProviderManager
{
private readonly FileSystemProvidersSection _config;
private readonly WeakSet<ShadowWrapper> _wrappers = new WeakSet<ShadowWrapper>();
// actual well-known filesystems returned by properties
private readonly IFileSystem2 _macroPartialFileSystem;
private readonly IFileSystem2 _partialViewsFileSystem;
private readonly IFileSystem2 _stylesheetsFileSystem;
private readonly IFileSystem2 _scriptsFileSystem;
private readonly IFileSystem2 _xsltFileSystem;
private readonly IFileSystem2 _masterPagesFileSystem;
private readonly IFileSystem2 _mvcViewsFileSystem;
// when shadowing is enabled, above filesystems, as wrappers
private readonly ShadowWrapper _macroPartialFileSystemWrapper;
private readonly ShadowWrapper _partialViewsFileSystemWrapper;
private readonly ShadowWrapper _stylesheetsFileSystemWrapper;
private readonly ShadowWrapper _scriptsFileSystemWrapper;
private readonly ShadowWrapper _xsltFileSystemWrapper;
private readonly ShadowWrapper _masterPagesFileSystemWrapper;
private readonly ShadowWrapper _mvcViewsFileSystemWrapper;
#region Singleton & Constructor
#region Singleton
private static readonly FileSystemProviderManager Instance = new FileSystemProviderManager();
@@ -40,224 +23,109 @@ namespace Umbraco.Core.IO
get { return Instance; }
}
#endregion
#region Constructors
internal FileSystemProviderManager()
{
_config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
_macroPartialFileSystem = new PhysicalFileSystem(SystemDirectories.MacroPartials);
_partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews);
_stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css);
_scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts);
_xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt);
_masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
_mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
_macroPartialFileSystem = _macroPartialFileSystemWrapper = new ShadowWrapper(_macroPartialFileSystem, "Views/MacroPartials");
_partialViewsFileSystem = _partialViewsFileSystemWrapper = new ShadowWrapper(_partialViewsFileSystem, "Views/Partials");
_stylesheetsFileSystem = _stylesheetsFileSystemWrapper = new ShadowWrapper(_stylesheetsFileSystem, "css");
_scriptsFileSystem = _scriptsFileSystemWrapper = new ShadowWrapper(_scriptsFileSystem, "scripts");
_xsltFileSystem = _xsltFileSystemWrapper = new ShadowWrapper(_xsltFileSystem, "xslt");
_masterPagesFileSystem = _masterPagesFileSystemWrapper = new ShadowWrapper(_masterPagesFileSystem, "masterpages");
_mvcViewsFileSystem = _mvcViewsFileSystemWrapper = new ShadowWrapper(_mvcViewsFileSystem, "Views");
// filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again
MediaFileSystem = GetFileSystemProvider<MediaFileSystem>();
_config = (FileSystemProvidersSection)ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
}
#endregion
#region Well-Known FileSystems
public IFileSystem2 MacroPartialsFileSystem { get { return _macroPartialFileSystem; } }
public IFileSystem2 PartialViewsFileSystem { get { return _partialViewsFileSystem; } }
public IFileSystem2 StylesheetsFileSystem { get { return _stylesheetsFileSystem; } }
public IFileSystem2 ScriptsFileSystem { get { return _scriptsFileSystem; } }
public IFileSystem2 XsltFileSystem { get { return _xsltFileSystem; } }
public IFileSystem2 MasterPagesFileSystem { get { return _masterPagesFileSystem; } }
public IFileSystem2 MvcViewsFileSystem { get { return _mvcViewsFileSystem; } }
public MediaFileSystem MediaFileSystem { get; private set; }
#endregion
#region Providers
/// <summary>
/// used to cache the lookup of how to construct this object so we don't have to reflect each time.
/// </summary>
private class ProviderConstructionInfo
/// <summary>
/// used to cache the lookup of how to construct this object so we don't have to reflect each time.
/// </summary>
private class ProviderConstructionInfo
{
public object[] Parameters { get; set; }
public ConstructorInfo Constructor { get; set; }
//public string ProviderAlias { get; set; }
public string ProviderAlias { get; set; }
}
private readonly ConcurrentDictionary<string, ProviderConstructionInfo> _providerLookup = new ConcurrentDictionary<string, ProviderConstructionInfo>();
private readonly ConcurrentDictionary<Type, string> _aliases = new ConcurrentDictionary<Type, string>();
private readonly ConcurrentDictionary<Type, string> _wrappedProviderLookup = new ConcurrentDictionary<Type, string>();
/// <summary>
/// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem.
/// Returns the underlying (non-typed) file system provider for the alias specified
/// </summary>
/// <param name="alias">The alias of the strongly-typed filesystem.</param>
/// <returns>The non-typed filesystem supporting the strongly-typed filesystem with the specified alias.</returns>
/// <remarks>This method should not be used directly, used <see cref="GetFileSystemProvider{TFileSystem}"/> instead.</remarks>
/// <param name="alias"></param>
/// <returns></returns>
/// <remarks>
/// It is recommended to use the typed GetFileSystemProvider method instead to get a strongly typed provider instance.
/// </remarks>
public IFileSystem GetUnderlyingFileSystemProvider(string alias)
{
// either get the constructor info from cache or create it and add to cache
//either get the constructor info from cache or create it and add to cache
var ctorInfo = _providerLookup.GetOrAdd(alias, s =>
{
// get config
var providerConfig = _config.Providers[s];
if (providerConfig == null)
throw new ArgumentException(string.Format("No provider found with alias {0}.", s));
throw new ArgumentException(string.Format("No provider found with the alias '{0}'", s));
// get the filesystem type
var providerType = Type.GetType(providerConfig.Type);
if (providerType == null)
throw new InvalidOperationException(string.Format("Could not find type {0}.", providerConfig.Type));
throw new InvalidOperationException(string.Format("Could not find type '{0}'", providerConfig.Type));
// ensure it implements IFileSystem
if (providerType.IsAssignableFrom(typeof (IFileSystem)))
throw new InvalidOperationException(string.Format("Type {0} does not implement IFileSystem.", providerType.FullName));
throw new InvalidOperationException(string.Format("The type '{0}' does not implement IFileSystem", providerConfig.Type));
// find a ctor matching the config parameters
var paramCount = providerConfig.Parameters != null ? providerConfig.Parameters.Count : 0;
var constructor = providerType.GetConstructors().SingleOrDefault(x
=> x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.AllKeys.Contains(y.Name)));
var constructor = providerType.GetConstructors()
.SingleOrDefault(x => x.GetParameters().Count() == paramCount
&& x.GetParameters().All(y => providerConfig.Parameters.AllKeys.Contains(y.Name)));
if (constructor == null)
throw new InvalidOperationException(string.Format("Type {0} has no ctor matching the {1} configuration parameter(s).", providerType.FullName, paramCount));
throw new InvalidOperationException(string.Format("Could not find constructor for type '{0}' which accepts {1} parameters", providerConfig.Type, paramCount));
var parameters = new object[paramCount];
if (providerConfig.Parameters != null) // keeps ReSharper happy
for (var i = 0; i < paramCount; i++)
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
for (var i = 0; i < paramCount; i++)
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
return new ProviderConstructionInfo
//return the new constructor info class to cache so we don't have to do this again.
return new ProviderConstructionInfo()
{
Constructor = constructor,
Parameters = parameters,
//ProviderAlias = s
ProviderAlias = s
};
});
// create the fs and return
return (IFileSystem) ctorInfo.Constructor.Invoke(ctorInfo.Parameters);
}
/// <summary>
/// Gets a strongly-typed filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <returns>A strongly-typed filesystem of the specified type.</returns>
public TFileSystem GetFileSystemProvider<TFileSystem>()
where TFileSystem : FileSystemWrapper
{
// deal with known types - avoid infinite loops!
if (typeof(TFileSystem) == typeof(MediaFileSystem) && MediaFileSystem != null)
return MediaFileSystem as TFileSystem; // else create and return
// get/cache the alias for the filesystem type
var alias = _aliases.GetOrAdd(typeof (TFileSystem), fsType =>
{
// validate the ctor
var constructor = fsType.GetConstructors().SingleOrDefault(x
=> x.GetParameters().Length == 1 && TypeHelper.IsTypeAssignableFrom<IFileSystem>(x.GetParameters().Single().ParameterType));
if (constructor == null)
throw new InvalidOperationException("Type " + fsType.FullName + " must inherit from FileSystemWrapper and have a constructor that accepts one parameter of type " + typeof(IFileSystem).FullName + ".");
// find the attribute and get the alias
var attr = (FileSystemProviderAttribute) fsType.GetCustomAttributes(typeof(FileSystemProviderAttribute), false).SingleOrDefault();
if (attr == null)
throw new InvalidOperationException("Type " + fsType.FullName + "is missing the required FileSystemProviderAttribute.");
return attr.Alias;
});
// gets the inner fs, create the strongly-typed fs wrapping the inner fs, register & return
// so we are double-wrapping here
// could be optimized by having FileSystemWrapper inherit from ShadowWrapper, maybe
var innerFs = GetUnderlyingFileSystemProvider(alias);
var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias);
var fs = (TFileSystem) Activator.CreateInstance(typeof (TFileSystem), innerFs);
_wrappers.Add(shadowWrapper); // keeping a weak reference to the wrapper
var fs = (IFileSystem)ctorInfo.Constructor.Invoke(ctorInfo.Parameters);
return fs;
}
#endregion
#region Shadow
// note
// shadowing is thread-safe, but entering and exiting shadow mode is not, and there is only one
// global shadow for the entire application, so great care should be taken to ensure that the
// application is *not* doing anything else when using a shadow.
// shadow applies to well-known filesystems *only* - at the moment, any other filesystem that would
// be created directly (via ctor) or via GetFileSystemProvider<T> is *not* shadowed.
// shadow must be enabled in an app event handler before anything else ie before any filesystem
// is actually created and used - after, it is too late - enabling shadow has a neglictible perfs
// impact.
// NO! by the time an app event handler is instanciated it is already too late, see note in ctor.
//internal void EnableShadow()
//{
// if (_mvcViewsFileSystem != null) // test one of the fs...
// throw new InvalidOperationException("Cannot enable shadow once filesystems have been created.");
// _shadowEnabled = true;
//}
public ICompletable Shadow(Guid id)
/// <summary>
/// Returns the strongly typed file system provider
/// </summary>
/// <typeparam name="TProviderTypeFilter"></typeparam>
/// <returns></returns>
public TProviderTypeFilter GetFileSystemProvider<TProviderTypeFilter>()
where TProviderTypeFilter : FileSystemWrapper
{
var typed = _wrappers.ToArray();
var wrappers = new ShadowWrapper[typed.Length + 7];
var i = 0;
while (i < typed.Length) wrappers[i] = typed[i++];
wrappers[i++] = _macroPartialFileSystemWrapper;
wrappers[i++] = _partialViewsFileSystemWrapper;
wrappers[i++] = _stylesheetsFileSystemWrapper;
wrappers[i++] = _scriptsFileSystemWrapper;
wrappers[i++] = _xsltFileSystemWrapper;
wrappers[i++] = _masterPagesFileSystemWrapper;
wrappers[i] = _mvcViewsFileSystemWrapper;
//get the alias for the type from cache or look it up and add it to the cache, then we don't have to reflect each time
var alias = _wrappedProviderLookup.GetOrAdd(typeof (TProviderTypeFilter), fsType =>
{
//validate the ctor
var constructor = fsType.GetConstructors()
.SingleOrDefault(x =>
x.GetParameters().Count() == 1 && TypeHelper.IsTypeAssignableFrom<IFileSystem>(x.GetParameters().Single().ParameterType));
if (constructor == null)
throw new InvalidOperationException("The type of " + fsType + " must inherit from FileSystemWrapper and must have a constructor that accepts one parameter of type " + typeof(IFileSystem));
return ShadowFileSystemsScope.CreateScope(id, wrappers);
}
var attr =
(FileSystemProviderAttribute)fsType.GetCustomAttributes(typeof(FileSystemProviderAttribute), false).
SingleOrDefault();
#endregion
if (attr == null)
throw new InvalidOperationException(string.Format("The provider type filter '{0}' is missing the required FileSystemProviderAttribute", typeof(FileSystemProviderAttribute).FullName));
private class WeakSet<T>
where T : class
{
private readonly HashSet<WeakReference<T>> _set = new HashSet<WeakReference<T>>();
public void Add(T item)
{
lock (_set)
{
_set.Add(new WeakReference<T>(item));
CollectLocked();
}
}
public T[] ToArray()
{
lock (_set)
{
CollectLocked();
return _set.Select(x =>
{
T target;
return x.TryGetTarget(out target) ? target : null;
}).WhereNotNull().ToArray();
}
}
private void CollectLocked()
{
_set.RemoveWhere(x =>
{
T target;
return x.TryGetTarget(out target) == false;
});
}
return attr.Alias;
});
var innerFs = GetUnderlyingFileSystemProvider(alias);
var outputFs = Activator.CreateInstance(typeof (TProviderTypeFilter), innerFs);
return (TProviderTypeFilter)outputFs;
}
}
}
+24 -31
View File
@@ -14,100 +14,93 @@ namespace Umbraco.Core.IO
///
/// This abstract class just wraps the 'real' IFileSystem object passed in to its constructor.
/// </remarks>
public abstract class FileSystemWrapper : IFileSystem2
public abstract class FileSystemWrapper : IFileSystem
{
protected FileSystemWrapper(IFileSystem wrapped)
private readonly IFileSystem _wrapped;
protected FileSystemWrapper(IFileSystem wrapped)
{
Wrapped = wrapped;
_wrapped = wrapped;
}
internal IFileSystem Wrapped { get; set; }
public IEnumerable<string> GetDirectories(string path)
public IEnumerable<string> GetDirectories(string path)
{
return Wrapped.GetDirectories(path);
return _wrapped.GetDirectories(path);
}
public void DeleteDirectory(string path)
{
Wrapped.DeleteDirectory(path);
_wrapped.DeleteDirectory(path);
}
public void DeleteDirectory(string path, bool recursive)
{
Wrapped.DeleteDirectory(path, recursive);
_wrapped.DeleteDirectory(path, recursive);
}
public bool DirectoryExists(string path)
{
return Wrapped.DirectoryExists(path);
return _wrapped.DirectoryExists(path);
}
public void AddFile(string path, Stream stream)
{
Wrapped.AddFile(path, stream);
_wrapped.AddFile(path, stream);
}
public void AddFile(string path, Stream stream, bool overrideExisting)
public void AddFile(string path, Stream stream, bool overrideIfExists)
{
Wrapped.AddFile(path, stream, overrideExisting);
_wrapped.AddFile(path, stream, overrideIfExists);
}
public IEnumerable<string> GetFiles(string path)
{
return Wrapped.GetFiles(path);
return _wrapped.GetFiles(path);
}
public IEnumerable<string> GetFiles(string path, string filter)
{
return Wrapped.GetFiles(path, filter);
return _wrapped.GetFiles(path, filter);
}
public Stream OpenFile(string path)
{
return Wrapped.OpenFile(path);
return _wrapped.OpenFile(path);
}
public void DeleteFile(string path)
{
Wrapped.DeleteFile(path);
_wrapped.DeleteFile(path);
}
public bool FileExists(string path)
{
return Wrapped.FileExists(path);
return _wrapped.FileExists(path);
}
public string GetRelativePath(string fullPathOrUrl)
{
return Wrapped.GetRelativePath(fullPathOrUrl);
return _wrapped.GetRelativePath(fullPathOrUrl);
}
public string GetFullPath(string path)
{
return Wrapped.GetFullPath(path);
return _wrapped.GetFullPath(path);
}
public string GetUrl(string path)
{
return Wrapped.GetUrl(path);
return _wrapped.GetUrl(path);
}
public DateTimeOffset GetLastModified(string path)
{
return Wrapped.GetLastModified(path);
return _wrapped.GetLastModified(path);
}
public DateTimeOffset GetCreated(string path)
{
return Wrapped.GetCreated(path);
return _wrapped.GetCreated(path);
}
// explicitely implementing - not breaking
long IFileSystem2.GetSize(string path)
{
var wrapped2 = Wrapped as IFileSystem2;
return wrapped2 == null ? Wrapped.GetSize(path) : wrapped2.GetSize(path);
}
}
}
}
+5 -14
View File
@@ -4,6 +4,9 @@ using System.IO;
namespace Umbraco.Core.IO
{
//TODO: There is no way to create a directory here without creating a file in a directory and then deleting it
//TODO: Should probably implement a rename?
public interface IFileSystem
{
IEnumerable<string> GetDirectories(string path);
@@ -13,7 +16,7 @@ namespace Umbraco.Core.IO
void DeleteDirectory(string path, bool recursive);
bool DirectoryExists(string path);
void AddFile(string path, Stream stream);
void AddFile(string path, Stream stream, bool overrideIfExists);
@@ -28,6 +31,7 @@ namespace Umbraco.Core.IO
bool FileExists(string path);
string GetRelativePath(string fullPathOrUrl);
string GetFullPath(string path);
@@ -38,17 +42,4 @@ namespace Umbraco.Core.IO
DateTimeOffset GetCreated(string path);
}
// this should be part of IFileSystem but we don't want to change the interface
public interface IFileSystem2 : IFileSystem
{
long GetSize(string path);
// TODO: implement these
//
//void CreateDirectory(string path);
//
//// move or rename, directory or file
//void Move(string source, string target);
}
}
+37 -649
View File
@@ -1,18 +1,9 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Media;
using Umbraco.Core.Media.Exif;
using Umbraco.Core.Models;
namespace Umbraco.Core.IO
{
@@ -23,662 +14,59 @@ namespace Umbraco.Core.IO
public class MediaFileSystem : FileSystemWrapper
{
private readonly IContentSection _contentConfig;
private readonly UploadAutoFillProperties _uploadAutoFillProperties;
private readonly object _folderCounterLock = new object();
private long _folderCounter;
private bool _folderCounterInitialized;
public MediaFileSystem(IFileSystem wrapped)
: this(wrapped, UmbracoConfig.For.UmbracoSettings().Content)
{
}
private static readonly Dictionary<int, string> DefaultSizes = new Dictionary<int, string>
{
{ 100, "thumb" },
{ 500, "big-thumb" }
};
public MediaFileSystem(IFileSystem wrapped)
: this(wrapped, UmbracoConfig.For.UmbracoSettings().Content, ApplicationContext.Current.ProfilingLogger.Logger)
{ }
public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig, ILogger logger)
: base(wrapped)
public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig) : base(wrapped)
{
_contentConfig = contentConfig;
_uploadAutoFillProperties = new UploadAutoFillProperties(this, logger, contentConfig);
}
internal UploadAutoFillProperties UploadAutoFillProperties { get { return _uploadAutoFillProperties; } }
// note - this is currently experimental / being developed
//public static bool UseTheNewMediaPathScheme { get; set; }
public const bool UseTheNewMediaPathScheme = false;
// none of the methods below are used in Core anymore
[Obsolete("This low-level method should NOT exist.")]
public string GetRelativePath(int propertyId, string fileName)
public string GetRelativePath(int propertyId, string fileName)
{
var sep = _contentConfig.UploadAllowDirectories
var seperator = _contentConfig.UploadAllowDirectories
? Path.DirectorySeparatorChar
: '-';
return propertyId.ToString(CultureInfo.InvariantCulture) + sep + fileName;
return propertyId.ToString(CultureInfo.InvariantCulture) + seperator + fileName;
}
[Obsolete("This low-level method should NOT exist.", false)]
public string GetRelativePath(string subfolder, string fileName)
{
var sep = _contentConfig.UploadAllowDirectories
var seperator = _contentConfig.UploadAllowDirectories
? Path.DirectorySeparatorChar
: '-';
return subfolder + sep + fileName;
return subfolder + seperator + fileName;
}
#region Media Path
/// <summary>
/// Gets the file path of a media file.
/// </summary>
/// <param name="filename">The file name.</param>
/// <param name="cuid">The unique identifier of the content/media owning the file.</param>
/// <param name="puid">The unique identifier of the property type owning the file.</param>
/// <returns>The filesystem-relative path to the media file.</returns>
/// <remarks>With the old media path scheme, this CREATES a new media path each time it is invoked.</remarks>
public string GetMediaPath(string filename, Guid cuid, Guid puid)
{
filename = Path.GetFileName(filename);
if (filename == null) throw new ArgumentException("Cannot become a safe filename.", "filename");
filename = IOHelper.SafeFileName(filename.ToLowerInvariant());
string folder;
if (UseTheNewMediaPathScheme == false)
{
// old scheme: filepath is "<int>/<filename>" OR "<int>-<filename>"
// default media filesystem maps to "~/media/<filepath>"
folder = GetNextFolder();
}
else
{
// new scheme: path is "<cuid>-<puid>/<filename>" OR "<cuid>-<puid>-<filename>"
// default media filesystem maps to "~/media/<filepath>"
// assumes that cuid and puid keys can be trusted - and that a single property type
// for a single content cannot store two different files with the same name
folder = Combine(cuid, puid).ToHexString(/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/...
}
var filepath = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories
? Path.Combine(folder, filename)
: folder + "-" + filename;
return filepath;
}
private static byte[] Combine(Guid guid1, Guid guid2)
{
var bytes1 = guid1.ToByteArray();
var bytes2 = guid2.ToByteArray();
var bytes = new byte[bytes1.Length];
for (var i = 0; i < bytes1.Length; i++)
bytes[i] = (byte)(bytes1[i] ^ bytes2[i]);
return bytes;
}
/// <summary>
/// Gets the file path of a media file.
/// </summary>
/// <param name="filename">The file name.</param>
/// <param name="prevpath">A previous file path.</param>
/// <param name="cuid">The unique identifier of the content/media owning the file.</param>
/// <param name="puid">The unique identifier of the property type owning the file.</param>
/// <returns>The filesystem-relative path to the media file.</returns>
/// <remarks>In the old, legacy, number-based scheme, we try to re-use the media folder
/// specified by <paramref name="prevpath"/>. Else, we CREATE a new one. Each time we are invoked.</remarks>
public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid)
{
if (UseTheNewMediaPathScheme || string.IsNullOrWhiteSpace(prevpath))
return GetMediaPath(filename, cuid, puid);
filename = Path.GetFileName(filename);
if (filename == null) throw new ArgumentException("Cannot become a safe filename.", "filename");
filename = IOHelper.SafeFileName(filename.ToLowerInvariant());
// old scheme, with a previous path
// prevpath should be "<int>/<filename>" OR "<int>-<filename>"
// and we want to reuse the "<int>" part, so try to find it
var sep = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories ? "/" : "-";
var pos = prevpath.IndexOf(sep, StringComparison.Ordinal);
var s = pos > 0 ? prevpath.Substring(0, pos) : null;
int ignored;
var folder = (pos > 0 && int.TryParse(s, out ignored)) ? s : GetNextFolder();
// ReSharper disable once AssignNullToNotNullAttribute
var filepath = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories
? Path.Combine(folder, filename)
: folder + "-" + filename;
return filepath;
}
/// <summary>
/// Gets the next media folder in the original number-based scheme.
/// </summary>
/// <returns></returns>
/// <remarks>Should be private, is internal for legacy FileHandlerData which is obsolete.</remarks>
internal string GetNextFolder()
{
EnsureFolderCounterIsInitialized();
return Interlocked.Increment(ref _folderCounter).ToString(CultureInfo.InvariantCulture);
}
private void EnsureFolderCounterIsInitialized()
{
lock (_folderCounterLock)
{
if (_folderCounterInitialized) return;
_folderCounter = 1000; // seed
var directories = GetDirectories("");
foreach (var directory in directories)
{
long folderNumber;
if (long.TryParse(directory, out folderNumber) && folderNumber > _folderCounter)
_folderCounter = folderNumber;
}
// note: not multi-domains ie LB safe as another domain could create directories
// while we read and parse them - don't fix, move to new scheme eventually
_folderCounterInitialized = true;
}
}
#endregion
#region Associated Media Files
/// <summary>
/// Stores a media file.
/// </summary>
/// <param name="content">The content item owning the media file.</param>
/// <param name="propertyType">The property type owning the media file.</param>
/// <param name="filename">The media file name.</param>
/// <param name="filestream">A stream containing the media bytes.</param>
/// <param name="oldpath">An optional filesystem-relative filepath to the previous media file.</param>
/// <returns>The filesystem-relative filepath to the media file.</returns>
/// <remarks>
/// <para>The file is considered "owned" by the content/propertyType.</para>
/// <para>If an <paramref name="oldpath"/> is provided then that file (and thumbnails) is deleted
/// before the new file is saved, and depending on the media path scheme, the folder
/// may be reused for the new file.</para>
/// </remarks>
public string StoreFile(IContentBase content, PropertyType propertyType, string filename, Stream filestream, string oldpath)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyType == null) throw new ArgumentNullException("propertyType");
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Null or empty.", "filename");
if (filestream == null) throw new ArgumentNullException("filestream");
// clear the old file, if any
if (string.IsNullOrWhiteSpace(oldpath) == false)
DeleteFile(oldpath, true);
// get the filepath, store the data
// use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme
var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key);
AddFile(filepath, filestream);
return filepath;
}
/// <summary>
/// Clears a media file.
/// </summary>
/// <param name="filepath">The filesystem-relative path to the media file.</param>
public new void DeleteFile(string filepath)
{
DeleteFile(filepath, true);
}
/// <summary>
/// Copies a media file.
/// </summary>
/// <param name="content">The content item owning the copy of the media file.</param>
/// <param name="propertyType">The property type owning the copy of the media file.</param>
/// <param name="sourcepath">The filesystem-relative path to the source media file.</param>
/// <returns>The filesystem-relative path to the copy of the media file.</returns>
public string CopyFile(IContentBase content, PropertyType propertyType, string sourcepath)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyType == null) throw new ArgumentNullException("propertyType");
if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Null or empty.", "sourcepath");
// ensure we have a file to copy
if (FileExists(sourcepath) == false) return null;
// get the filepath
var filename = Path.GetFileName(sourcepath);
var filepath = GetMediaPath(filename, content.Key, propertyType.Key);
this.CopyFile(sourcepath, filepath);
CopyThumbnails(sourcepath, filepath);
return filepath;
}
/// <summary>
/// Gets or creates a property for a content item.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <returns>The property.</returns>
private static Property GetProperty(IContentBase content, string propertyTypeAlias)
{
var property = content.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
if (property != null) return property;
var propertyType = content.GetContentType().CompositionPropertyTypes
.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
if (propertyType == null)
throw new Exception("No property type exists with alias " + propertyTypeAlias + ".");
property = new Property(propertyType);
content.Properties.Add(property);
return property;
}
public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream)
{
var property = GetProperty(content, propertyTypeAlias);
var svalue = property.Value as string;
var oldpath = svalue == null ? null : GetRelativePath(svalue);
var filepath = StoreFile(content, property.PropertyType, filename, filestream, oldpath);
property.Value = GetUrl(filepath);
SetUploadFile(content, property, filepath, filestream);
}
public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath)
{
var property = GetProperty(content, propertyTypeAlias);
var svalue = property.Value as string;
var oldpath = svalue == null ? null : GetRelativePath(svalue); // FIXME DELETE?
if (string.IsNullOrWhiteSpace(oldpath) == false && oldpath != filepath)
DeleteFile(oldpath, true);
property.Value = GetUrl(filepath);
using (var filestream = OpenFile(filepath))
{
SetUploadFile(content, property, filepath, filestream);
}
}
// sets a file for the FileUpload property editor
// ie generates thumbnails and populates autofill properties
private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream)
{
// check if file is an image (and supports resizing and thumbnails etc)
var extension = Path.GetExtension(filepath);
var isImage = IsImageFile(extension);
// specific stuff for images (thumbnails etc)
if (isImage)
{
using (var image = Image.FromStream(filestream))
{
// use one image for all
GenerateThumbnails(image, filepath, property.PropertyType);
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, image);
}
}
else
{
// will use filepath for extension, and filestream for length
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream);
}
}
#endregion
#region Image
/// <summary>
/// Gets a value indicating whether the file extension corresponds to an image.
/// </summary>
/// <param name="extension">The file extension.</param>
/// <returns>A value indicating whether the file extension corresponds to an image.</returns>
public bool IsImageFile(string extension)
{
if (extension == null) return false;
extension = extension.TrimStart('.');
return UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.InvariantContains(extension);
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="stream">A stream containing the image bytes.</param>
/// <returns>The dimension of the image.</returns>
/// <remarks>First try with EXIF as it is faster and does not load the entire image
/// in memory. Fallback to GDI which means loading the image in memory and thus
/// use potentially large amounts of memory.</remarks>
public Size GetDimensions(Stream stream)
{
//Try to load with exif
try
{
var jpgInfo = ImageFile.FromStream(stream);
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(stream))
{
var fileWidth = image.Width;
var fileHeight = image.Height;
return new Size(fileWidth, fileHeight);
}
}
#endregion
#region Manage thumbnails
// note: this does not find 'custom' thumbnails?
// will find _thumb and _big-thumb but NOT _custom?
public IEnumerable<string> GetThumbnails(string path)
{
var parentDirectory = Path.GetDirectoryName(path);
var extension = Path.GetExtension(path);
return GetFiles(parentDirectory)
.Where(x => x.StartsWith(path.TrimEnd(extension) + "_thumb") || x.StartsWith(path.TrimEnd(extension) + "_big-thumb"))
.ToList();
}
public void DeleteFile(string path, bool deleteThumbnails)
{
base.DeleteFile(path);
if (deleteThumbnails == false)
return;
DeleteThumbnails(path);
}
public void DeleteThumbnails(string path)
{
GetThumbnails(path)
.ForEach(x => base.DeleteFile(x));
}
public void CopyThumbnails(string sourcePath, string targetPath)
{
var targetPathBase = Path.GetDirectoryName(targetPath) ?? "";
foreach (var sourceThumbPath in GetThumbnails(sourcePath))
{
var sourceThumbFilename = Path.GetFileName(sourceThumbPath) ?? "";
var targetThumbPath = Path.Combine(targetPathBase, sourceThumbFilename);
this.CopyFile(sourceThumbPath, targetThumbPath);
}
}
#endregion
#region GenerateThumbnails
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
string preValue)
{
if (string.IsNullOrWhiteSpace(preValue))
return GenerateThumbnails(image, filepath);
var additionalSizes = new List<int>();
var sep = preValue.Contains(",") ? "," : ";";
var values = preValue.Split(new[] { sep }, StringSplitOptions.RemoveEmptyEntries);
foreach (var value in values)
{
int size;
if (int.TryParse(value, out size))
additionalSizes.Add(size);
}
return GenerateThumbnails(image, filepath, additionalSizes);
}
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
IEnumerable<int> additionalSizes = null)
{
var w = image.Width;
var h = image.Height;
var sizes = additionalSizes == null ? DefaultSizes.Keys : DefaultSizes.Keys.Concat(additionalSizes);
// start with default sizes,
// add additional sizes,
// filter out duplicates,
// filter out those that would be larger that the original image
// and create the thumbnail
return sizes
.Distinct()
.Where(x => w >= x && h >= x)
.Select(x => GenerateResized(image, filepath, DefaultSizes.ContainsKey(x) ? DefaultSizes[x] : "", x))
.ToList(); // now
}
public IEnumerable<ResizedImage> GenerateThumbnails(
Stream filestream,
string filepath,
PropertyType propertyType)
{
// get the original image from the original stream
if (filestream.CanSeek) filestream.Seek(0, 0); // fixme - what if we cannot seek?
using (var image = Image.FromStream(filestream))
{
return GenerateThumbnails(image, filepath, propertyType);
}
}
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
PropertyType propertyType)
{
// if the editor is an upload field, check for additional thumbnail sizes
// that can be defined in the prevalue for the property data type. otherwise,
// just use the default sizes.
var sizes = propertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias
? ApplicationContext.Current.Services.DataTypeService
.GetPreValuesByDataTypeId(propertyType.DataTypeDefinitionId)
.FirstOrDefault()
: string.Empty;
return GenerateThumbnails(image, filepath, sizes);
}
#endregion
#region GenerateResized - Generate at resized filepath derived from origin filepath
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight)
{
return GenerateResized(originImage, originFilepath, sizeName, maxWidthHeight, -1, -1);
}
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int fixedWidth, int fixedHeight)
{
return GenerateResized(originImage, originFilepath, sizeName, -1, fixedWidth, fixedHeight);
}
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight, int fixedWidth, int fixedHeight)
{
if (string.IsNullOrWhiteSpace(sizeName))
sizeName = "UMBRACOSYSTHUMBNAIL";
var extension = Path.GetExtension(originFilepath) ?? string.Empty;
var filebase = originFilepath.TrimEnd(extension);
var resizedFilepath = filebase + "_" + sizeName + extension;
return GenerateResizedAt(originImage, resizedFilepath, maxWidthHeight, fixedWidth, fixedHeight);
}
#endregion
#region GenerateResizedAt - Generate at specified resized filepath
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight)
{
return GenerateResizedAt(originImage, resizedFilepath, maxWidthHeight, -1, -1);
}
public ResizedImage GenerateResizedAt(Image originImage, int fixedWidth, int fixedHeight, string resizedFilepath)
{
return GenerateResizedAt(originImage, resizedFilepath, -1, fixedWidth, fixedHeight);
}
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight, int fixedWidth, int fixedHeight)
{
// target dimensions
int width, height;
// if maxWidthHeight then get ratio
if (maxWidthHeight > 0)
{
var fx = (float)originImage.Size.Width / maxWidthHeight;
var fy = (float)originImage.Size.Height / maxWidthHeight;
var f = Math.Max(fx, fy); // fit in thumbnail size
width = (int)Math.Round(originImage.Size.Width / f);
height = (int)Math.Round(originImage.Size.Height / f);
if (width == 0) width = 1;
if (height == 0) height = 1;
}
else if (fixedWidth > 0 && fixedHeight > 0)
{
width = fixedWidth;
height = fixedHeight;
}
else
{
width = height = 1;
}
// create new image with best quality settings
using (var bitmap = new Bitmap(width, height))
using (var graphics = Graphics.FromImage(bitmap))
{
// if the image size is rather large we cannot use the best quality interpolation mode
// because we'll get out of mem exceptions. So we detect how big the image is and use
// the mid quality interpolation mode when the image size exceeds our max limit.
graphics.InterpolationMode = originImage.Width > 5000 || originImage.Height > 5000
? InterpolationMode.Bilinear // mid quality
: InterpolationMode.HighQualityBicubic; // best quality
// everything else is best-quality
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
// copy the old image to the new and resize
var rect = new Rectangle(0, 0, width, height);
graphics.DrawImage(originImage, rect, 0, 0, originImage.Width, originImage.Height, GraphicsUnit.Pixel);
// copy metadata
// fixme - er... no?
// get an encoder - based upon the file type
var extension = (Path.GetExtension(resizedFilepath) ?? "").TrimStart('.').ToLowerInvariant();
var encoders = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo encoder;
switch (extension)
{
case "png":
encoder = encoders.Single(t => t.MimeType.Equals("image/png"));
break;
case "gif":
encoder = encoders.Single(t => t.MimeType.Equals("image/gif"));
break;
case "tif":
case "tiff":
encoder = encoders.Single(t => t.MimeType.Equals("image/tiff"));
break;
case "bmp":
encoder = encoders.Single(t => t.MimeType.Equals("image/bmp"));
break;
// TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
// place so left it here, but it needs to not set a codec if it doesn't know which one to pick
// Note: when fixing this: both .jpg and .jpeg should be handled as extensions
default:
encoder = encoders.Single(t => t.MimeType.Equals("image/jpeg"));
break;
}
// set compresion ratio to 90%
var encoderParams = new EncoderParameters();
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
// save the new image
using (var stream = new MemoryStream())
{
bitmap.Save(stream, encoder, encoderParams);
stream.Seek(0, 0);
if (resizedFilepath.Contains("UMBRACOSYSTHUMBNAIL"))
{
var filepath = resizedFilepath.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToInvariantString());
AddFile(filepath, stream);
if (extension != "jpg")
{
filepath = filepath.TrimEnd(extension) + "jpg";
stream.Seek(0, 0);
AddFile(filepath, stream);
}
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
stream.Seek(0, 0);
resizedFilepath = resizedFilepath.Replace("UMBRACOSYSTHUMBNAIL", width + "x" + height);
}
AddFile(resizedFilepath, stream);
}
return new ResizedImage(resizedFilepath, width, height);
}
}
#endregion
#region Inner classes
public class ResizedImage
{
public ResizedImage()
{ }
public ResizedImage(string filepath, int width, int height)
{
Filepath = filepath;
Width = width;
Height = height;
}
public string Filepath { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
#endregion
}
public IEnumerable<string> GetThumbnails(string path)
{
var parentDirectory = Path.GetDirectoryName(path);
var extension = Path.GetExtension(path);
return GetFiles(parentDirectory)
.Where(x => x.StartsWith(path.TrimEnd(extension) + "_thumb") || x.StartsWith(path.TrimEnd(extension) + "_big-thumb"))
.ToList();
}
public void DeleteFile(string path, bool deleteThumbnails)
{
DeleteFile(path);
if (deleteThumbnails == false)
return;
DeleteThumbnails(path);
}
public void DeleteThumbnails(string path)
{
GetThumbnails(path)
.ForEach(DeleteFile);
}
}
}
+56 -146
View File
@@ -6,32 +6,29 @@ using Umbraco.Core.Logging;
namespace Umbraco.Core.IO
{
public class PhysicalFileSystem : IFileSystem2
public class PhysicalFileSystem : IFileSystem
{
// the rooted, filesystem path, using directory separator chars, NOT ending with a separator
// eg "c:" or "c:\path\to\site" or "\\server\path"
private readonly string _rootPath;
// _rootPath, but with separators replaced by forward-slashes
// eg "c:" or "c:/path/to/site" or "//server/path"
// (is used in GetRelativePath)
private readonly string _rootPathFwd;
// the relative url, using url separator chars, NOT ending with a separator
// eg "" or "/Views" or "/Media" or "/<vpath>/Media" in case of a virtual path
// the ??? url, using url separator chars, NOT ending with a separator
// eg "" (?) or "/Scripts" or ???
private readonly string _rootUrl;
// virtualRoot should be "~/path/to/root" eg "~/Views"
// the "~/" is mandatory.
public PhysicalFileSystem(string virtualRoot)
{
if (virtualRoot == null) throw new ArgumentNullException("virtualRoot");
if (virtualRoot.StartsWith("~/") == false)
throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'");
_rootPath = EnsureDirectorySeparatorChar(IOHelper.MapPath(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar);
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
_rootUrl = EnsureUrlSeparatorChar(IOHelper.ResolveUrl(virtualRoot)).TrimEnd('/');
_rootPath = IOHelper.MapPath(virtualRoot);
_rootPath = EnsureDirectorySeparatorChar(_rootPath);
_rootPath = _rootPath.TrimEnd(Path.DirectorySeparatorChar);
_rootUrl = IOHelper.ResolveUrl(virtualRoot);
_rootUrl = EnsureUrlSeparatorChar(_rootUrl);
_rootUrl = _rootUrl.TrimEnd('/');
}
public PhysicalFileSystem(string rootPath, string rootUrl)
@@ -46,24 +43,22 @@ namespace Umbraco.Core.IO
throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'");
// rootPath should be... rooted, as in, it's a root path!
// but the test suite App.config cannot really "root" anything so we'll have to do it here
//var localRoot = AppDomain.CurrentDomain.BaseDirectory;
var localRoot = IOHelper.GetRootDirectorySafe();
if (Path.IsPathRooted(rootPath) == false)
{
// but the test suite App.config cannot really "root" anything so we have to do it here
var localRoot = IOHelper.GetRootDirectorySafe();
rootPath = Path.Combine(localRoot, rootPath);
}
_rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar);
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
_rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/');
rootPath = EnsureDirectorySeparatorChar(rootPath);
rootUrl = EnsureUrlSeparatorChar(rootUrl);
_rootPath = rootPath.TrimEnd(Path.DirectorySeparatorChar);
_rootUrl = rootUrl.TrimEnd('/');
}
/// <summary>
/// Gets directories in a directory.
/// </summary>
/// <param name="path">The filesystem-relative path to the directory.</param>
/// <returns>The filesystem-relative path to the directories in the directory.</returns>
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
public IEnumerable<string> GetDirectories(string path)
{
var fullPath = GetFullPath(path);
@@ -85,20 +80,11 @@ namespace Umbraco.Core.IO
return Enumerable.Empty<string>();
}
/// <summary>
/// Deletes a directory.
/// </summary>
/// <param name="path">The filesystem-relative path of the directory.</param>
public void DeleteDirectory(string path)
{
DeleteDirectory(path, false);
}
/// <summary>
/// Deletes a directory.
/// </summary>
/// <param name="path">The filesystem-relative path of the directory.</param>
/// <param name="recursive">A value indicating whether to recursively delete sub-directories.</param>
public void DeleteDirectory(string path, bool recursive)
{
var fullPath = GetFullPath(path);
@@ -115,71 +101,38 @@ namespace Umbraco.Core.IO
}
}
/// <summary>
/// Gets a value indicating whether a directory exists.
/// </summary>
/// <param name="path">The filesystem-relative path of the directory.</param>
/// <returns>A value indicating whether a directory exists.</returns>
public bool DirectoryExists(string path)
{
var fullPath = GetFullPath(path);
return Directory.Exists(fullPath);
}
/// <summary>
/// Saves a file.
/// </summary>
/// <param name="path">The filesystem-relative path of the file.</param>
/// <param name="stream">A stream containing the file data.</param>
/// <remarks>Overrides the existing file, if any.</remarks>
public void AddFile(string path, Stream stream)
{
AddFile(path, stream, true);
}
/// <summary>
/// Saves a file.
/// </summary>
/// <param name="path">The filesystem-relative path of the file.</param>
/// <param name="stream">A stream containing the file data.</param>
/// <param name="overrideExisting">A value indicating whether to override the existing file, if any.</param>
/// <remarks>If a file exists and <paramref name="overrideExisting"/> is false, an exception is thrown.</remarks>
public void AddFile(string path, Stream stream, bool overrideExisting)
public void AddFile(string path, Stream stream, bool overrideIfExists)
{
var fullPath = GetFullPath(path);
var exists = File.Exists(fullPath);
if (exists && overrideExisting == false)
if (exists && overrideIfExists == false)
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
var directory = Path.GetDirectoryName(fullPath);
if (directory == null) throw new InvalidOperationException("Could not get directory.");
Directory.CreateDirectory(directory); // ensure it exists
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // ensure it exists
if (stream.CanSeek) // fixme - what else?
if (stream.CanSeek)
stream.Seek(0, 0);
using (var destination = (Stream) File.Create(fullPath))
using (var destination = (Stream)File.Create(fullPath))
stream.CopyTo(destination);
}
/// <summary>
/// Gets files in a directory.
/// </summary>
/// <param name="path">The filesystem-relative path of the directory.</param>
/// <returns>The filesystem-relative path to the files in the directory.</returns>
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
public IEnumerable<string> GetFiles(string path)
{
return GetFiles(path, "*.*");
}
/// <summary>
/// Gets files in a directory.
/// </summary>
/// <param name="path">The filesystem-relative path of the directory.</param>
/// <param name="filter">A filter.</param>
/// <returns>The filesystem-relative path to the matching files in the directory.</returns>
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
public IEnumerable<string> GetFiles(string path, string filter)
{
var fullPath = GetFullPath(path);
@@ -201,21 +154,12 @@ namespace Umbraco.Core.IO
return Enumerable.Empty<string>();
}
/// <summary>
/// Opens a file.
/// </summary>
/// <param name="path">The filesystem-relative path to the file.</param>
/// <returns></returns>
public Stream OpenFile(string path)
{
var fullPath = GetFullPath(path);
return File.OpenRead(fullPath);
}
/// <summary>
/// Deletes a file.
/// </summary>
/// <param name="path">The filesystem-relative path to the file.</param>
public void DeleteFile(string path)
{
var fullPath = GetFullPath(path);
@@ -232,53 +176,52 @@ namespace Umbraco.Core.IO
}
}
/// <summary>
/// Gets a value indicating whether a file exists.
/// </summary>
/// <param name="path">The filesystem-relative path to the file.</param>
/// <returns>A value indicating whether the file exists.</returns>
public bool FileExists(string path)
{
var fullpath = GetFullPath(path);
return File.Exists(fullpath);
}
// beware, many things depend on how the GetRelative/AbsolutePath methods work!
/// <summary>
/// Gets the filesystem-relative path of a full path or of an url.
/// Gets the relative path.
/// </summary>
/// <param name="fullPathOrUrl">The full path or url.</param>
/// <returns>The path, relative to this filesystem's root.</returns>
/// <remarks>
/// <para>The relative path is relative to this filesystem's root, not starting with any
/// directory separator. All separators are forward-slashes.</para>
/// directory separator. If input was recognized as a url (path), then output uses url (path) separator
/// chars.</para>
/// </remarks>
public string GetRelativePath(string fullPathOrUrl)
{
// test url
var path = fullPathOrUrl.Replace('\\', '/'); // ensure url separator char
// if it starts with the root url, strip it and trim the starting slash to make it relative
// eg "/Media/1234/img.jpg" => "1234/img.jpg"
if (IOHelper.PathStartsWith(path, _rootUrl, '/'))
return path.Substring(_rootUrl.Length).TrimStart('/');
if (IOHelper.PathStartsWith(path, _rootUrl, '/')) // if it starts with the root url...
return path.Substring(_rootUrl.Length) // strip it
.TrimStart('/'); // it's relative
// if it starts with the root path, strip it and trim the starting slash to make it relative
// eg "c:/websites/test/root/Media/1234/img.jpg" => "1234/img.jpg"
if (IOHelper.PathStartsWith(path, _rootPathFwd, '/'))
return path.Substring(_rootPathFwd.Length).TrimStart('/');
// test path
path = EnsureDirectorySeparatorChar(fullPathOrUrl);
// unchanged - what else?
return path;
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) // if it starts with the root path
return path.Substring(_rootPath.Length) // strip it
.TrimStart(Path.DirectorySeparatorChar); // it's relative
// unchanged - including separators
return fullPathOrUrl;
}
/// <summary>
/// Gets the full path.
/// </summary>
/// <param name="path">The full or filesystem-relative path.</param>
/// <param name="path">The full or relative path.</param>
/// <returns>The full path.</returns>
/// <remarks>
/// <para>On the physical filesystem, the full path is the rooted (ie non-relative), safe (ie within this
/// filesystem's root) path. All separators are Path.DirectorySeparatorChar.</para>
/// filesystem's root) path. All separators are converted to Path.DirectorySeparatorChar.</para>
/// </remarks>
public string GetFullPath(string path)
{
@@ -286,82 +229,49 @@ namespace Umbraco.Core.IO
var opath = path;
path = EnsureDirectorySeparatorChar(path);
// fixme - this part should go!
// not sure what we are doing here - so if input starts with a (back) slash,
// we assume it's not a FS relative path and we try to convert it... but it
// really makes little sense?
if (path.StartsWith(Path.DirectorySeparatorChar.ToString()))
path = GetRelativePath(path);
// if not already rooted, combine with the root path
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar) == false)
path = Path.Combine(_rootPath, path);
// if already a full path, return
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
return path;
// sanitize - GetFullPath will take care of any relative
// else combine and sanitize, ie GetFullPath will take care of any relative
// segments in path, eg '../../foo.tmp' - it may throw a SecurityException
// if the combined path reaches illegal parts of the filesystem
path = Path.GetFullPath(path);
var fpath = Path.Combine(_rootPath, path);
fpath = Path.GetFullPath(fpath);
// at that point, path is within legal parts of the filesystem, ie we have
// permissions to reach that path, but it may nevertheless be outside of
// our root path, due to relative segments, so better check
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
return path;
if (IOHelper.PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar))
return fpath;
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root.");
}
/// <summary>
/// Gets the url.
/// </summary>
/// <param name="path">The filesystem-relative path.</param>
/// <returns>The url.</returns>
/// <remarks>All separators are forward-slashes.</remarks>
public string GetUrl(string path)
{
path = EnsureUrlSeparatorChar(path).Trim('/');
return _rootUrl + "/" + path;
}
/// <summary>
/// Gets the last-modified date of a directory or file.
/// </summary>
/// <param name="path">The filesystem-relative path to the directory or the file.</param>
/// <returns>The last modified date of the directory or the file.</returns>
public DateTimeOffset GetLastModified(string path)
{
var fullpath = GetFullPath(path);
return DirectoryExists(fullpath)
? new DirectoryInfo(fullpath).LastWriteTimeUtc
: new FileInfo(fullpath).LastWriteTimeUtc;
return DirectoryExists(path)
? new DirectoryInfo(GetFullPath(path)).LastWriteTimeUtc
: new FileInfo(GetFullPath(path)).LastWriteTimeUtc;
}
/// <summary>
/// Gets the created date of a directory or file.
/// </summary>
/// <param name="path">The filesystem-relative path to the directory or the file.</param>
/// <returns>The created date of the directory or the file.</returns>
public DateTimeOffset GetCreated(string path)
{
var fullpath = GetFullPath(path);
return DirectoryExists(fullpath)
? Directory.GetCreationTimeUtc(fullpath)
: File.GetCreationTimeUtc(fullpath);
}
/// <summary>
/// Gets the size of a file.
/// </summary>
/// <param name="path">The filesystem-relative path to the file.</param>
/// <returns>The file of the size, in bytes.</returns>
/// <remarks>If the file does not exist, returns -1.</remarks>
public long GetSize(string path)
{
var fullPath = GetFullPath(path);
var file = new FileInfo(fullPath);
return file.Exists ? file.Length : -1;
return DirectoryExists(path)
? Directory.GetCreationTimeUtc(GetFullPath(path))
: File.GetCreationTimeUtc(GetFullPath(path));
}
#region Helper Methods
+20
View File
@@ -0,0 +1,20 @@
namespace Umbraco.Core.IO
{
internal class ResizedImage
{
public ResizedImage()
{
}
public ResizedImage(int width, int height, string fileName)
{
Width = width;
Height = height;
FileName = fileName;
}
public int Width { get; set; }
public int Height { get; set; }
public string FileName { get; set; }
}
}
-286
View File
@@ -1,286 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Umbraco.Core.IO
{
internal class ShadowFileSystem : IFileSystem2
{
private readonly IFileSystem _fs;
private readonly IFileSystem2 _sfs;
public ShadowFileSystem(IFileSystem fs, IFileSystem2 sfs)
{
_fs = fs;
_sfs = sfs;
}
public IFileSystem Inner
{
get { return _fs; }
}
public void Complete()
{
if (_nodes == null) return;
var exceptions = new List<Exception>();
foreach (var kvp in _nodes)
{
if (kvp.Value.IsExist)
{
if (kvp.Value.IsFile)
{
try
{
using (var stream = _sfs.OpenFile(kvp.Key))
_fs.AddFile(kvp.Key, stream, true);
}
catch (Exception e)
{
exceptions.Add(new Exception("Could not save file \"" + kvp.Key + "\".", e));
}
}
}
else
{
try
{
if (kvp.Value.IsDir)
_fs.DeleteDirectory(kvp.Key, true);
else
_fs.DeleteFile(kvp.Key);
}
catch (Exception e)
{
exceptions.Add(new Exception("Could not delete " + (kvp.Value.IsDir ? "directory": "file") + " \"" + kvp.Key + "\".", e));
}
}
}
_nodes.Clear();
if (exceptions.Count == 0) return;
throw new AggregateException("Failed to apply all changes (see exceptions).", exceptions);
}
private Dictionary<string, ShadowNode> _nodes;
private Dictionary<string, ShadowNode> Nodes { get { return _nodes ?? (_nodes = new Dictionary<string, ShadowNode>()); } }
private class ShadowNode
{
public ShadowNode(bool isDelete, bool isdir)
{
IsDelete = isDelete;
IsDir = isdir;
}
public bool IsDelete { get; private set; }
public bool IsDir { get; private set; }
public bool IsExist { get { return IsDelete == false; } }
public bool IsFile { get { return IsDir == false; } }
}
private static string NormPath(string path)
{
return path.ToLowerInvariant().Replace("\\", "/");
}
// values can be "" (root), "foo", "foo/bar"...
private static bool IsChild(string path, string input)
{
if (input.StartsWith(path) == false || input.Length < path.Length + 2)
return false;
if (path.Length > 0 && input[path.Length] != '/') return false;
var pos = input.IndexOf("/", path.Length + 1, StringComparison.OrdinalIgnoreCase);
return pos < 0;
}
private static bool IsDescendant(string path, string input)
{
if (input.StartsWith(path) == false || input.Length < path.Length + 2)
return false;
return path.Length == 0 || input[path.Length] == '/';
}
public IEnumerable<string> GetDirectories(string path)
{
var normPath = NormPath(path);
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
var directories = _fs.GetDirectories(path);
return directories
.Except(shadows.Where(kvp => (kvp.Value.IsDir && kvp.Value.IsDelete) || (kvp.Value.IsFile && kvp.Value.IsExist))
.Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsDir && kvp.Value.IsExist).Select(kvp => kvp.Key))
.Distinct();
}
public void DeleteDirectory(string path)
{
DeleteDirectory(path, false);
}
public void DeleteDirectory(string path, bool recursive)
{
if (DirectoryExists(path) == false) return;
var normPath = NormPath(path);
if (recursive)
{
Nodes[normPath] = new ShadowNode(true, true);
var remove = Nodes.Where(x => IsDescendant(normPath, x.Key)).ToList();
foreach (var kvp in remove) Nodes.Remove(kvp.Key);
Delete(path, true);
}
else
{
if (Nodes.Any(x => IsChild(normPath, x.Key) && x.Value.IsExist) // shadow content
|| _fs.GetDirectories(path).Any() || _fs.GetFiles(path).Any()) // actual content
throw new InvalidOperationException("Directory is not empty.");
Nodes[path] = new ShadowNode(true, true);
var remove = Nodes.Where(x => IsChild(normPath, x.Key)).ToList();
foreach (var kvp in remove) Nodes.Remove(kvp.Key);
Delete(path, false);
}
}
private void Delete(string path, bool recurse)
{
foreach (var file in _fs.GetFiles(path))
{
Nodes[NormPath(file)] = new ShadowNode(true, false);
}
foreach (var dir in _fs.GetDirectories(path))
{
Nodes[NormPath(dir)] = new ShadowNode(true, true);
if (recurse) Delete(dir, true);
}
}
public bool DirectoryExists(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf))
return sf.IsDir && sf.IsExist;
return _fs.DirectoryExists(path);
}
public void AddFile(string path, Stream stream)
{
AddFile(path, stream, true);
}
public void AddFile(string path, Stream stream, bool overrideIfExists)
{
ShadowNode sf;
var normPath = NormPath(path);
if (Nodes.TryGetValue(normPath, out sf) && sf.IsExist && (sf.IsDir || overrideIfExists == false))
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
var parts = normPath.Split('/');
for (var i = 0; i < parts.Length - 1; i++)
{
var dirPath = string.Join("/", parts.Take(i + 1));
ShadowNode sd;
if (Nodes.TryGetValue(dirPath, out sd))
{
if (sd.IsFile) throw new InvalidOperationException("Invalid path.");
if (sd.IsDelete) Nodes[dirPath] = new ShadowNode(false, true);
}
else
{
if (_fs.DirectoryExists(dirPath)) continue;
if (_fs.FileExists(dirPath)) throw new InvalidOperationException("Invalid path.");
Nodes[dirPath] = new ShadowNode(false, true);
}
}
_sfs.AddFile(path, stream, overrideIfExists);
Nodes[normPath] = new ShadowNode(false, false);
}
public IEnumerable<string> GetFiles(string path)
{
var normPath = NormPath(path);
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
var files = _fs.GetFiles(path);
return files
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
.Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist).Select(kvp => kvp.Key))
.Distinct();
}
public IEnumerable<string> GetFiles(string path, string filter)
{
return _fs.GetFiles(path, filter);
}
public Stream OpenFile(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf))
return sf.IsDir || sf.IsDelete ? null : _sfs.OpenFile(path);
return _fs.OpenFile(path);
}
public void DeleteFile(string path)
{
if (FileExists(path) == false) return;
Nodes[NormPath(path)] = new ShadowNode(true, false);
}
public bool FileExists(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf))
return sf.IsFile && sf.IsExist;
return _fs.FileExists(path);
}
public string GetRelativePath(string fullPathOrUrl)
{
return _fs.GetRelativePath(fullPathOrUrl);
}
public string GetFullPath(string path)
{
return _fs.GetFullPath(path);
}
public string GetUrl(string path)
{
return _fs.GetUrl(path);
}
public DateTimeOffset GetLastModified(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf) == false) return _fs.GetLastModified(path);
if (sf.IsDelete) throw new InvalidOperationException("Invalid path.");
return _sfs.GetLastModified(path);
}
public DateTimeOffset GetCreated(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf) == false) return _fs.GetCreated(path);
if (sf.IsDelete) throw new InvalidOperationException("Invalid path.");
return _sfs.GetCreated(path);
}
public long GetSize(string path)
{
ShadowNode sf;
if (Nodes.TryGetValue(NormPath(path), out sf) == false)
{
// the inner filesystem (_fs) can be IFileSystem2... or just IFileSystem
// figure it out and use the most effective GetSize method
var fs2 = _fs as IFileSystem2;
return fs2 == null ? _fs.GetSize(path) : fs2.GetSize(path);
}
if (sf.IsDelete || sf.IsDir) throw new InvalidOperationException("Invalid path.");
return _sfs.GetSize(path);
}
}
}
@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using Umbraco.Core.Logging;
namespace Umbraco.Core.IO
{
internal class ShadowFileSystemsScope : ICompletable
{
// note: taking a reference to the _manager instead of using manager.Current
// to avoid using Current everywhere but really, we support only 1 scope at
// a time, not multiple scopes in case of multiple managers (not supported)
private const string ItemKey = "Umbraco.Core.IO.ShadowFileSystemsScope";
private static readonly object Locker = new object();
private readonly Guid _id;
private readonly ShadowWrapper[] _wrappers;
static ShadowFileSystemsScope()
{
SafeCallContext.Register(
() =>
{
var scope = CallContext.LogicalGetData(ItemKey);
CallContext.FreeNamedDataSlot(ItemKey);
return scope;
},
o =>
{
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException();
if (o != null) CallContext.LogicalSetData(ItemKey, o);
});
}
private ShadowFileSystemsScope(Guid id, ShadowWrapper[] wrappers)
{
LogHelper.Debug<ShadowFileSystemsScope>("Shadow " + id + ".");
_id = id;
_wrappers = wrappers;
foreach (var wrapper in _wrappers)
wrapper.Shadow(id);
}
// internal for tests + FileSystemProviderManager
// do NOT use otherwise
internal static ShadowFileSystemsScope CreateScope(Guid id, ShadowWrapper[] wrappers)
{
lock (Locker)
{
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException("Already shadowing.");
CallContext.LogicalSetData(ItemKey, ItemKey); // value does not matter
}
return new ShadowFileSystemsScope(id, wrappers);
}
internal static bool InScope
{
get { return NoScope == false; }
}
internal static bool NoScope
{
get { return CallContext.LogicalGetData(ItemKey) == null; }
}
public void Complete()
{
lock (Locker)
{
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (complete).");
var exceptions = new List<Exception>();
foreach (var wrapper in _wrappers)
{
try
{
// this may throw an AggregateException if some of the changes could not be applied
wrapper.UnShadow(true);
}
catch (AggregateException ae)
{
exceptions.Add(ae);
}
}
if (exceptions.Count > 0)
throw new AggregateException("Failed to apply all changes (see exceptions).", exceptions);
// last, & *only* if successful (otherwise we'll unshadow & cleanup as best as we can)
CallContext.FreeNamedDataSlot(ItemKey);
}
}
public void Dispose()
{
lock (Locker)
{
if (CallContext.LogicalGetData(ItemKey) == null) return;
try
{
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (abort)");
foreach (var wrapper in _wrappers)
wrapper.UnShadow(false); // should not throw
}
finally
{
// last, & always
CallContext.FreeNamedDataSlot(ItemKey);
}
}
}
}
}
-158
View File
@@ -1,158 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Umbraco.Core.IO
{
internal class ShadowWrapper : IFileSystem2
{
private readonly IFileSystem _innerFileSystem;
private readonly string _shadowPath;
private ShadowFileSystem _shadowFileSystem;
private string _shadowDir;
public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath)
{
_innerFileSystem = innerFileSystem;
_shadowPath = shadowPath;
}
internal void Shadow(Guid id)
{
// note: no thread-safety here, because ShadowFs is thread-safe due to the check
// on ShadowFileSystemsScope.None - and if None is false then we should be running
// in a single thread anyways
var virt = "~/App_Data/Shadow/" + id + "/" + _shadowPath;
_shadowDir = IOHelper.MapPath(virt);
Directory.CreateDirectory(_shadowDir);
var tempfs = new PhysicalFileSystem(virt);
_shadowFileSystem = new ShadowFileSystem(_innerFileSystem, tempfs);
}
internal void UnShadow(bool complete)
{
var shadowFileSystem = _shadowFileSystem;
var dir = _shadowDir;
_shadowFileSystem = null;
_shadowDir = null;
try
{
// this may throw an AggregateException if some of the changes could not be applied
if (complete) shadowFileSystem.Complete();
}
finally
{
// in any case, cleanup
try
{
Directory.Delete(dir, true);
dir = dir.Substring(0, dir.Length - _shadowPath.Length - 1);
if (Directory.EnumerateFileSystemEntries(dir).Any() == false)
Directory.Delete(dir, true);
}
catch
{
// ugly, isn't it? but if we cannot cleanup, bah, just leave it there
}
}
}
private IFileSystem FileSystem
{
get { return ShadowFileSystemsScope.NoScope ? _innerFileSystem : _shadowFileSystem; }
}
public IEnumerable<string> GetDirectories(string path)
{
return FileSystem.GetDirectories(path);
}
public void DeleteDirectory(string path)
{
FileSystem.DeleteDirectory(path);
}
public void DeleteDirectory(string path, bool recursive)
{
FileSystem.DeleteDirectory(path, recursive);
}
public bool DirectoryExists(string path)
{
return FileSystem.DirectoryExists(path);
}
public void AddFile(string path, Stream stream)
{
FileSystem.AddFile(path, stream);
}
public void AddFile(string path, Stream stream, bool overrideExisting)
{
FileSystem.AddFile(path, stream, overrideExisting);
}
public IEnumerable<string> GetFiles(string path)
{
return FileSystem.GetFiles(path);
}
public IEnumerable<string> GetFiles(string path, string filter)
{
return FileSystem.GetFiles(path, filter);
}
public Stream OpenFile(string path)
{
return FileSystem.OpenFile(path);
}
public void DeleteFile(string path)
{
FileSystem.DeleteFile(path);
}
public bool FileExists(string path)
{
return FileSystem.FileExists(path);
}
public string GetRelativePath(string fullPathOrUrl)
{
return FileSystem.GetRelativePath(fullPathOrUrl);
}
public string GetFullPath(string path)
{
return FileSystem.GetFullPath(path);
}
public string GetUrl(string path)
{
return FileSystem.GetUrl(path);
}
public DateTimeOffset GetLastModified(string path)
{
return FileSystem.GetLastModified(path);
}
public DateTimeOffset GetCreated(string path)
{
return FileSystem.GetCreated(path);
}
public long GetSize(string path)
{
var filesystem = FileSystem; // will be either a ShadowFileSystem OR the actual underlying IFileSystem
// and the underlying filesystem can be IFileSystem2... or just IFileSystem
// figure it out and use the most effective GetSize method
var filesystem2 = filesystem as IFileSystem2;
return filesystem2 == null ? filesystem.GetSize(path) : filesystem2.GetSize(path);
}
}
}
+1 -17
View File
@@ -96,23 +96,7 @@ namespace Umbraco.Core.IO
}
}
public static string PartialViews
{
get
{
return MvcViews + "/Partials/";
}
}
public static string MacroPartials
{
get
{
return MvcViews + "/MacroPartials/";
}
}
public static string Media
{
get
+69 -31
View File
@@ -1,7 +1,13 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Media;
@@ -80,15 +86,15 @@ namespace Umbraco.Core.IO
private void Initialize()
{
Filename = _fs.GetFileName(Path);
var ext = _fs.GetExtension(Path);
Extension = string.IsNullOrEmpty(ext) == false
? ext.TrimStart('.').ToLowerInvariant()
Extension = string.IsNullOrEmpty(_fs.GetExtension(Path)) == false
? _fs.GetExtension(Path).Substring(1).ToLowerInvariant()
: "";
Url = _fs.GetUrl(Path);
Exists = _fs.FileExists(Path);
if (Exists == false)
{
LogHelper.Warn<UmbracoMediaFile>("The media file doesn't exist: " + Path);
}
}
public bool Exists { get; private set; }
@@ -111,15 +117,27 @@ namespace Umbraco.Core.IO
{
get
{
if (_length.HasValue) return _length.Value;
_length = Exists ? _fs.GetSize(Path) : -1;
if (_length == null)
{
if (Exists)
{
_length = _fs.GetSize(Path);
}
else
{
_length = -1;
}
}
return _length.Value;
}
}
public bool SupportsResizing
{
get { return _fs.IsImageFile(Extension); }
get
{
return UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.InvariantContains(Extension);
}
}
public string GetFriendlyName()
@@ -129,49 +147,67 @@ namespace Umbraco.Core.IO
public Size GetDimensions()
{
if (_size.HasValue) return _size.Value;
if (_fs.FileExists(Path))
if (_size == null)
{
EnsureFileSupportsResizing();
using (var fs = _fs.OpenFile(Path))
if (_fs.FileExists(Path))
{
_size = _fs.GetDimensions(fs);
EnsureFileSupportsResizing();
using (var fs = _fs.OpenFile(Path))
{
_size = ImageHelper.GetDimensions(fs);
}
}
else
{
_size = new Size(-1, -1);
}
}
else
{
_size = new Size(-1, -1);
}
return _size.Value;
}
public string Resize(int width, int height)
{
if (Exists == false) return string.Empty;
if (Exists)
{
EnsureFileSupportsResizing();
EnsureFileSupportsResizing();
var filepath = Resize(width, height, -1, string.Empty);
return _fs.GetUrl(filepath);
var fileNameThumb = DoResize(width, height, -1, string.Empty);
return _fs.GetUrl(fileNameThumb);
}
return string.Empty;
}
public string Resize(int maxWidthHeight, string fileNameAddition)
{
if (Exists == false) return string.Empty;
if (Exists)
{
EnsureFileSupportsResizing();
EnsureFileSupportsResizing();
var filepath = Resize(-1, -1, maxWidthHeight, fileNameAddition);
return _fs.GetUrl(filepath);
var fileNameThumb = DoResize(-1, -1, maxWidthHeight, fileNameAddition);
return _fs.GetUrl(fileNameThumb);
}
return string.Empty;
}
private string Resize(int width, int height, int maxWidthHeight, string sizeName)
private string DoResize(int width, int height, int maxWidthHeight, string fileNameAddition)
{
using (var filestream = _fs.OpenFile(Path))
using (var image = Image.FromStream(filestream))
using (var fs = _fs.OpenFile(Path))
{
return _fs.GenerateResized(image, Path, sizeName, maxWidthHeight, width, height).Filepath;
using (var image = Image.FromStream(fs))
{
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
: string.Format("{0}_{1}." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumbnail = maxWidthHeight == -1
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
: ImageHelper.GenerateThumbnail(image, maxWidthHeight, fileNameThumb, Extension, _fs);
return thumbnail.FileName;
}
}
}
@@ -180,5 +216,7 @@ namespace Umbraco.Core.IO
if (SupportsResizing == false)
throw new InvalidOperationException(string.Format("The file {0} is not an image, so can't get dimensions", Filename));
}
}
}
@@ -3,11 +3,7 @@ using System.Linq;
namespace Umbraco.Core.Logging
{
/// <summary>
/// Implements <see cref="ILogger"/> on top of System.Diagnostics.Debug.
/// </summary>
/// <remarks>Useful for tests.</remarks>
public class DebugDiagnosticsLogger : ILogger
internal class DebugDiagnosticsLogger : ILogger
{
public void Error(Type callingType, string message, Exception exception)
{
@@ -1,9 +1,9 @@
namespace Umbraco.Core.Logging
{
using System;
using System.Runtime.CompilerServices;
using System;
using System.Runtime.CompilerServices;
using ImageProcessor.Common.Exceptions;
using ImageProcessor.Common.Exceptions;
namespace Umbraco.Core.Logging
{
/// <summary>
/// A logger for explicitly logging ImageProcessor exceptions.
-21
View File
@@ -1,21 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
namespace Umbraco.Core.Media
{
public static class ImageExtensions
{
/// <summary>
/// Gets the MIME type of an image.
/// </summary>
/// <param name="image">The image.</param>
/// <returns>The MIME type of the image.</returns>
public static string GetMimeType(this Image image)
{
var format = image.RawFormat;
var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid);
return codec.MimeType;
}
}
}
+261
View File
@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Media.Exif;
namespace Umbraco.Core.Media
{
/// <summary>
/// A helper class used for imaging
/// </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;
var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid);
return codec.MimeType;
}
/// <summary>
/// Creates the thumbnails if the image is larger than all of the specified ones.
/// </summary>
/// <param name="fs"></param>
/// <param name="fileName"></param>
/// <param name="extension"></param>
/// <param name="originalImage"></param>
/// <param name="additionalThumbSizes"></param>
/// <returns></returns>
internal static IEnumerable<ResizedImage> GenerateMediaThumbnails(
IFileSystem fs,
string fileName,
string extension,
Image originalImage,
IEnumerable<int> additionalThumbSizes)
{
var result = new List<ResizedImage>();
var allSizesDictionary = new Dictionary<int, string> { { 100, "thumb" }, { 500, "big-thumb" } };
//combine the static dictionary with the additional sizes with only unique values
var allSizes = allSizesDictionary.Select(kv => kv.Key)
.Union(additionalThumbSizes.Where(x => x > 0).Distinct());
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s] : "");
foreach (var s in sizesDictionary)
{
var size = s.Key;
var name = s.Value;
if (originalImage.Width >= size && originalImage.Height >= size)
{
result.Add(Resize(fs, fileName, extension, size, name, originalImage));
}
}
return result;
}
/// <summary>
/// Performs an image resize
/// </summary>
/// <param name="fileSystem"></param>
/// <param name="path"></param>
/// <param name="extension"></param>
/// <param name="maxWidthHeight"></param>
/// <param name="fileNameAddition"></param>
/// <param name="originalImage"></param>
/// <returns></returns>
private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
{
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)))
: string.Format("{0}_{1}." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumb = GenerateThumbnail(
originalImage,
maxWidthHeight,
fileNameThumb,
extension,
fileSystem);
return thumb;
}
internal static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, string thumbnailFileName, string extension, IFileSystem fs)
{
return GenerateThumbnail(image, maxWidthHeight, -1, -1, thumbnailFileName, extension, fs);
}
internal static ResizedImage GenerateThumbnail(Image image, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs)
{
return GenerateThumbnail(image, -1, fixedWidth, fixedHeight, thumbnailFileName, extension, fs);
}
private static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs)
{
// Generate thumbnail
float f = 1;
if (maxWidthHeight >= 0)
{
var fx = (float)image.Size.Width / maxWidthHeight;
var fy = (float)image.Size.Height / maxWidthHeight;
// must fit in thumbnail size
f = Math.Max(fx, fy);
}
//depending on if we are doing fixed width resizing or not.
fixedWidth = (maxWidthHeight > 0) ? image.Width : fixedWidth;
fixedHeight = (maxWidthHeight > 0) ? image.Height : fixedHeight;
var widthTh = (int)Math.Round(fixedWidth / f);
var heightTh = (int)Math.Round(fixedHeight / f);
// fixes for empty width or height
if (widthTh == 0)
widthTh = 1;
if (heightTh == 0)
heightTh = 1;
// Create new image with best quality settings
using (var bp = new Bitmap(widthTh, heightTh))
{
using (var g = Graphics.FromImage(bp))
{
//if the image size is rather large we cannot use the best quality interpolation mode
// because we'll get out of mem exceptions. So we'll detect how big the image is and use
// the mid quality interpolation mode when the image size exceeds our max limit.
if (image.Width > 5000 || image.Height > 5000)
{
//use mid quality
g.InterpolationMode = InterpolationMode.Bilinear;
}
else
{
//use best quality
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
// Copy the old image to the new and resized
var rect = new Rectangle(0, 0, widthTh, heightTh);
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
// Copy metadata
var imageEncoders = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo codec;
switch (extension.ToLower())
{
case "png":
codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
break;
case "gif":
codec = imageEncoders.Single(t => t.MimeType.Equals("image/gif"));
break;
case "tif":
case "tiff":
codec = imageEncoders.Single(t => t.MimeType.Equals("image/tiff"));
break;
case "bmp":
codec = imageEncoders.Single(t => t.MimeType.Equals("image/bmp"));
break;
// TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
// place so left it here, but it needs to not set a codec if it doesn't know which one to pick
// Note: when fixing this: both .jpg and .jpeg should be handled as extensions
default:
codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
break;
}
// Set compresion ratio to 90%
var ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
// Save the new image using the dimensions of the image
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
using (var ms = new MemoryStream())
{
bp.Save(ms, codec, ep);
ms.Seek(0, 0);
fs.AddFile(predictableThumbnailName, ms);
fs.AddFile(predictableThumbnailNameJpg, ms);
}
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
using (var ms = new MemoryStream())
{
bp.Save(ms, codec, ep);
ms.Seek(0, 0);
fs.AddFile(newFileName, ms);
}
return new ResizedImage(widthTh, heightTh, newFileName);
}
}
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.IO;
namespace Umbraco.Core.Media
{
/// <summary>
/// Internal singleton to handle the numbering of subfolders within the Media-folder.
/// When this class is initiated it will look for numbered subfolders and select the highest number,
/// which will be the start point for the naming of the next subfolders. If no subfolders exists
/// then the starting point will be 1000, ie. /media/1000/koala.jpg
/// </summary>
internal class MediaSubfolderCounter
{
#region Singleton
private long _numberedFolder = 1000;//Default starting point
private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim();
private static readonly Lazy<MediaSubfolderCounter> Lazy = new Lazy<MediaSubfolderCounter>(() => new MediaSubfolderCounter());
public static MediaSubfolderCounter Current { get { return Lazy.Value; } }
private MediaSubfolderCounter()
{
var folders = new List<long>();
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
var directories = fs.GetDirectories("");
foreach (var directory in directories)
{
long dirNum;
if (long.TryParse(directory, out dirNum))
{
folders.Add(dirNum);
}
}
var last = folders.OrderBy(x => x).LastOrDefault();
if(last != default(long))
_numberedFolder = last;
}
#endregion
/// <summary>
/// Returns an increment of the numbered media subfolders.
/// </summary>
/// <returns>A <see cref="System.Int64"/> value</returns>
public long Increment()
{
using (new ReadLock(ClearLock))
{
_numberedFolder = _numberedFolder + 1;
return _numberedFolder;
}
}
}
}
@@ -1,220 +0,0 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
namespace Umbraco.Core.Media
{
/// <summary>
/// Provides methods to manage auto-fill properties for upload fields.
/// </summary>
internal class UploadAutoFillProperties
{
private readonly ILogger _logger;
private readonly MediaFileSystem _mediaFileSystem;
private readonly IContentSection _contentSettings;
public UploadAutoFillProperties(MediaFileSystem mediaFileSystem, ILogger logger, IContentSection contentSettings)
{
_mediaFileSystem = mediaFileSystem;
_logger = logger;
_contentSettings = contentSettings;
}
/// <summary>
/// Gets the auto-fill configuration for a specified property alias.
/// </summary>
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <returns>The auto-fill configuration for the specified property alias, or null.</returns>
public IImagingAutoFillUploadField GetConfig(string propertyTypeAlias)
{
var autoFillConfigs = _contentSettings.ImageAutoFillProperties;
return autoFillConfigs == null ? null : autoFillConfigs.FirstOrDefault(x => x.Alias == propertyTypeAlias);
}
/// <summary>
/// Resets the auto-fill properties of a content item, for a specified property alias.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property type alias.</param>
public void Reset(IContentBase content, string propertyTypeAlias)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyTypeAlias == null) throw new ArgumentNullException("propertyTypeAlias");
// get the config, no config = nothing to do
var autoFillConfig = GetConfig(propertyTypeAlias);
if (autoFillConfig == null) return; // nothing
// reset
Reset(content, autoFillConfig);
}
/// <summary>
/// Resets the auto-fill properties of a content item, for a specified auto-fill configuration.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="autoFillConfig">The auto-fill configuration.</param>
public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
ResetProperties(content, autoFillConfig);
}
/// <summary>
/// Populates the auto-fill properties of a content item.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
public void Populate(IContentBase content, string propertyTypeAlias, string filepath)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyTypeAlias == null) throw new ArgumentNullException("propertyTypeAlias");
// no property = nothing to do
if (content.Properties.Contains(propertyTypeAlias) == false) return;
// get the config, no config = nothing to do
var autoFillConfig = GetConfig(propertyTypeAlias);
if (autoFillConfig == null) return; // nothing
// populate
Populate(content, autoFillConfig, filepath);
}
/// <summary>
/// Populates the auto-fill properties of a content item.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
/// <param name="filestream">The stream containing the file data.</param>
/// <param name="image">The file data as an image object.</param>
public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, Image image = null)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyTypeAlias == null) throw new ArgumentNullException("propertyTypeAlias");
// no property = nothing to do
if (content.Properties.Contains(propertyTypeAlias) == false) return;
// get the config, no config = nothing to do
var autoFillConfig = GetConfig(propertyTypeAlias);
if (autoFillConfig == null) return; // nothing
// populate
Populate(content, autoFillConfig, filepath, filestream, image);
}
/// <summary>
/// Populates the auto-fill properties of a content item, for a specified auto-fill configuration.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="autoFillConfig">The auto-fill configuration.</param>
/// <param name="filepath">The filesystem path to the uploaded file.</param>
/// <remarks>The <paramref name="filepath"/> parameter is the path relative to the filesystem.</remarks>
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
// no file = reset, file = auto-fill
if (filepath.IsNullOrWhiteSpace())
{
ResetProperties(content, autoFillConfig);
}
else
{
// if anything goes wrong, just reset the properties
try
{
using (var filestream = _mediaFileSystem.OpenFile(filepath))
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
var size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null;
SetProperties(content, autoFillConfig, size, filestream.Length, extension);
}
}
catch (Exception ex)
{
_logger.Error(typeof(UploadAutoFillProperties), "Could not populate upload auto-fill properties for file \""
+ filepath + "\".", ex);
ResetProperties(content, autoFillConfig);
}
}
}
/// <summary>
/// Populates the auto-fill properties of a content item.
/// </summary>
/// <param name="content">The content item.</param>
/// <param name="autoFillConfig"></param>
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
/// <param name="filestream">The stream containing the file data.</param>
/// <param name="image">The file data as an image object.</param>
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, Image image = null)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
// no file = reset, file = auto-fill
if (filepath.IsNullOrWhiteSpace() || filestream == null)
{
ResetProperties(content, autoFillConfig);
}
else
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
Size? size;
if (image == null)
size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null;
else
size = new Size(image.Width, image.Height);
SetProperties(content, autoFillConfig, size, filestream.Length, extension);
}
}
private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
content.Properties[autoFillConfig.WidthFieldAlias].Value = size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty;
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
content.Properties[autoFillConfig.HeightFieldAlias].Value = size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty;
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
content.Properties[autoFillConfig.LengthFieldAlias].Value = length;
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
content.Properties[autoFillConfig.ExtensionFieldAlias].Value = extension;
}
private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
content.Properties[autoFillConfig.WidthFieldAlias].Value = string.Empty;
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
content.Properties[autoFillConfig.HeightFieldAlias].Value = string.Empty;
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
content.Properties[autoFillConfig.LengthFieldAlias].Value = string.Empty;
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
content.Properties[autoFillConfig.ExtensionFieldAlias].Value = string.Empty;
}
}
}
+129 -82
View File
@@ -1,15 +1,25 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Media;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Strings;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
@@ -319,7 +329,7 @@ namespace Umbraco.Core.Models
{
if (property.Value is string)
{
var value = (string) property.Value;
var value = (string)property.Value;
property.Value = value.ToValidXmlString();
}
}
@@ -394,8 +404,7 @@ namespace Umbraco.Core.Models
return content.Properties
.Where(property => propertyGroup.PropertyTypes
.Select(propertyType => propertyType.Id)
.Contains(property.PropertyTypeId))
.OrderBy(x => x.PropertyType.SortOrder);
.Contains(property.PropertyTypeId));
}
/// <summary>
@@ -434,116 +443,154 @@ namespace Umbraco.Core.Models
}
}
public static IContentTypeComposition GetContentType(this IContentBase contentBase)
{
if (contentBase == null) throw new ArgumentNullException("contentBase");
var content = contentBase as IContent;
if (content != null) return content.ContentType;
var media = contentBase as IMedia;
if (media != null) return media.ContentType;
var member = contentBase as IMember;
if (member != null) return member.ContentType;
throw new NotSupportedException("Unsupported IContentBase implementation: " + contentBase.GetType().FullName + ".");
}
#region SetValue for setting file contents
/// <summary>
/// Stores and sets an uploaded HttpPostedFileBase as a property value.
/// Sets and uploads the file from a HttpPostedFileBase object as the property value
/// </summary>
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="value">The uploaded <see cref="HttpPostedFileBase"/>.</param>
/// <param name="content"><see cref="IContentBase"/> to add property value to</param>
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
/// <param name="value">The <see cref="HttpPostedFileBase"/> containing the file that will be uploaded</param>
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value)
{
// ensure we get the filename without the path in IE in intranet mode
// Ensure we get the filename without the path in IE in intranet mode
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
var filename = value.FileName;
var pos = filename.LastIndexOf(@"\", StringComparison.InvariantCulture);
if (pos > 0)
filename = filename.Substring(pos + 1);
var fileName = value.FileName;
if (fileName.LastIndexOf(@"\") > 0)
fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1);
// strip any directory info
pos = filename.LastIndexOf(IOHelper.DirSepChar);
if (pos > 0)
filename = filename.Substring(pos + 1);
var name =
IOHelper.SafeFileName(
fileName.Substring(fileName.LastIndexOf(IOHelper.DirSepChar) + 1,
fileName.Length - fileName.LastIndexOf(IOHelper.DirSepChar) - 1)
.ToLower());
// get a safe filename - should this be done by MediaHelper?
filename = IOHelper.SafeFileName(filename);
if (string.IsNullOrWhiteSpace(filename)) return;
filename = filename.ToLower(); // fixme - er... why?
FileSystemProviderManager.Current.MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream);
if (string.IsNullOrEmpty(name) == false)
SetFileOnContent(content, propertyTypeAlias, name, value.InputStream);
}
/// <summary>
/// Stores and sets an uploaded HttpPostedFile as a property value.
/// Sets and uploads the file from a HttpPostedFile object as the property value
/// </summary>
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="value">The uploaded <see cref="HttpPostedFile"/>.</param>
/// <param name="content"><see cref="IContentBase"/> to add property value to</param>
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
/// <param name="value">The <see cref="HttpPostedFile"/> containing the file that will be uploaded</param>
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value)
{
SetValue(content, propertyTypeAlias, (HttpPostedFileBase) new HttpPostedFileWrapper(value));
SetValue(content, propertyTypeAlias, (HttpPostedFileBase)new HttpPostedFileWrapper(value));
}
/// <summary>
/// Stores and sets an uploaded HttpPostedFileWrapper as a property value.
/// Sets and uploads the file from a HttpPostedFileWrapper object as the property value
/// </summary>
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="value">The uploaded <see cref="HttpPostedFileWrapper"/>.</param>
/// <param name="content"><see cref="IContentBase"/> to add property value to</param>
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
/// <param name="value">The <see cref="HttpPostedFileWrapper"/> containing the file that will be uploaded</param>
[Obsolete("There is no reason for this overload since HttpPostedFileWrapper inherits from HttpPostedFileBase")]
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value)
{
SetValue(content, propertyTypeAlias, (HttpPostedFileBase) value);
SetValue(content, propertyTypeAlias, (HttpPostedFileBase)value);
}
/// <summary>
/// Stores and sets a file as a property value.
/// Sets and uploads the file from a <see cref="Stream"/> as the property value
/// </summary>
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="filename">The name of the file.</param>
/// <param name="filestream">A stream containing the file data.</param>
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
/// you need to store the file by yourself using Store and then figure out
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream)
/// <param name="content"><see cref="IContentBase"/> to add property value to</param>
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
/// <param name="fileName">Name of the file</param>
/// <param name="fileStream"><see cref="Stream"/> to save to disk</param>
public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName, Stream fileStream)
{
if (filename == null || filestream == null) return;
var name = IOHelper.SafeFileName(fileName);
// get a safe filename - should this be done by MediaHelper?
filename = IOHelper.SafeFileName(filename);
if (string.IsNullOrWhiteSpace(filename)) return;
filename = filename.ToLower(); // fixme - er... why?
FileSystemProviderManager.Current.MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream);
if (string.IsNullOrEmpty(name) == false && fileStream != null)
SetFileOnContent(content, propertyTypeAlias, name, fileStream);
}
/// <summary>
/// Stores a file.
/// </summary>
/// <param name="content"><see cref="IContentBase"/>A content item.</param>
/// <param name="propertyTypeAlias">The property alias.</param>
/// <param name="filename">The name of the file.</param>
/// <param name="filestream">A stream containing the file data.</param>
/// <param name="filepath">The original file path, if any.</param>
/// <returns>The path to the file, relative to the media filesystem.</returns>
/// <remarks>
/// <para>Does NOT set the property value, so one should probably store the file and then do
/// something alike: property.Value = MediaHelper.FileSystem.GetUrl(filepath).</para>
/// <para>The original file path is used, in the old media file path scheme, to try and reuse
/// the "folder number" that was assigned to the previous file referenced by the property,
/// if any.</para>
/// </remarks>
public static string StoreFile(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string filepath)
private static void SetFileOnContent(IContentBase content, string propertyTypeAlias, string filename, Stream fileStream)
{
var propertyType = content.GetContentType()
.CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + ".");
return FileSystemProviderManager.Current.MediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath);
var property = content.Properties.FirstOrDefault(x => x.Alias == propertyTypeAlias);
if (property == null)
return;
//TODO: ALl of this naming logic needs to be put into the ImageHelper and then we need to change FileUploadPropertyValueEditor to do the same!
var numberedFolder = MediaSubfolderCounter.Current.Increment();
var fileName = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories
? Path.Combine(numberedFolder.ToString(CultureInfo.InvariantCulture), filename)
: numberedFolder + "-" + filename;
var extension = Path.GetExtension(filename).Substring(1).ToLowerInvariant();
//the file size is the length of the stream in bytes
var fileSize = fileStream.Length;
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
fs.AddFile(fileName, fileStream);
//Check if file supports resizing and create thumbnails
var supportsResizing = UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.InvariantContains(extension);
//the config section used to auto-fill properties
IImagingAutoFillUploadField uploadFieldConfigNode = null;
//Check for auto fill of additional properties
if (UmbracoConfig.For.UmbracoSettings().Content.ImageAutoFillProperties != null)
{
uploadFieldConfigNode = UmbracoConfig.For.UmbracoSettings().Content.ImageAutoFillProperties
.FirstOrDefault(x => x.Alias == propertyTypeAlias);
}
if (supportsResizing)
{
//get the original image from the original stream
if (fileStream.CanSeek) fileStream.Seek(0, 0);
using (var originalImage = Image.FromStream(fileStream))
{
var additionalSizes = new List<int>();
//Look up Prevalues for this upload datatype - if it is an upload datatype - get additional configured sizes
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)
{
//Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId
var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId);
var thumbnailSizes = values.FirstOrDefault();
//Additional thumbnails configured as prevalues on the DataType
if (thumbnailSizes != null)
{
foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries))
{
int thumbSize;
if (thumb != "" && int.TryParse(thumb, out thumbSize))
{
additionalSizes.Add(thumbSize);
}
}
}
}
ImageHelper.GenerateMediaThumbnails(fs, fileName, extension, originalImage, additionalSizes);
//while the image is still open, we'll check if we need to auto-populate the image properties
if (uploadFieldConfigNode != null)
{
content.SetValue(uploadFieldConfigNode.WidthFieldAlias, originalImage.Width.ToString(CultureInfo.InvariantCulture));
content.SetValue(uploadFieldConfigNode.HeightFieldAlias, originalImage.Height.ToString(CultureInfo.InvariantCulture));
}
}
}
//if auto-fill is true, then fill the remaining, non-image properties
if (uploadFieldConfigNode != null)
{
content.SetValue(uploadFieldConfigNode.LengthFieldAlias, fileSize.ToString(CultureInfo.InvariantCulture));
content.SetValue(uploadFieldConfigNode.ExtensionFieldAlias, extension);
}
//Set the value of the property to that of the uploaded file's url
property.Value = fs.GetUrl(fileName);
}
#endregion
@@ -16,11 +16,6 @@ namespace Umbraco.Core.Models.Editors
/// </remarks>
public class ContentPropertyData
{
public ContentPropertyData(object value, PreValueCollection preValues)
: this(value, preValues, new Dictionary<string, object>())
{
}
public ContentPropertyData(object value, PreValueCollection preValues, IDictionary<string, object> additionalData)
{
Value = value;
@@ -33,9 +28,6 @@ namespace Umbraco.Core.Models.Editors
/// </summary>
public object Value { get; private set; }
/// <summary>
/// The pre-value collection for the content property
/// </summary>
public PreValueCollection PreValues { get; private set; }
/// <summary>
+3 -6
View File
@@ -113,10 +113,8 @@ namespace Umbraco.Core.Models.EntityBase
/// </summary>
internal virtual void AddingEntity()
{
if (IsPropertyDirty("CreateDate") == false || _createDate == default(DateTime))
CreateDate = DateTime.Now;
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
UpdateDate = CreateDate;
CreateDate = DateTime.Now;
UpdateDate = DateTime.Now;
}
/// <summary>
@@ -124,8 +122,7 @@ namespace Umbraco.Core.Models.EntityBase
/// </summary>
internal virtual void UpdatingEntity()
{
if (IsPropertyDirty("UpdateDate") == false || _updateDate == default(DateTime))
UpdateDate = DateTime.Now;
UpdateDate = DateTime.Now;
}
/// <summary>
@@ -1,4 +1,3 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -12,9 +11,6 @@ namespace Umbraco.Core.Models
[DataMember]
int Id { get; set; }
[DataMember]
Guid Key { get; set; }
/// <summary>
/// Gets or sets the Alias of the Property
/// </summary>
-7
View File
@@ -1,7 +0,0 @@
namespace Umbraco.Core.Models
{
public interface IXsltFile : IFile
{
}
}
+5 -4
View File
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Umbraco.Core.IO;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Strings;
@@ -24,12 +27,11 @@ namespace Umbraco.Core.Models
_addedProperties = new List<string>();
_removedProperties = new List<string>();
}
/// <summary>
/// Creates an item with pre-filled properties
/// </summary>
/// <param name="id"></param>
/// <param name="key"></param>
/// <param name="useInEditor"></param>
/// <param name="cacheDuration"></param>
/// <param name="alias"></param>
@@ -41,11 +43,10 @@ namespace Umbraco.Core.Models
/// <param name="cacheByMember"></param>
/// <param name="dontRender"></param>
/// <param name="scriptPath"></param>
public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath)
public Macro(int id, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath)
: this()
{
Id = id;
Key = key;
UseInEditor = useInEditor;
CacheDuration = cacheDuration;
Alias = alias.ToCleanString(CleanStringType.Alias);
+2 -17
View File
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models
{
public MacroProperty()
{
_key = Guid.NewGuid();
}
/// <summary>
@@ -30,7 +30,6 @@ namespace Umbraco.Core.Models
_alias = alias;
_name = name;
_sortOrder = sortOrder;
_key = Guid.NewGuid();
//try to get the new mapped parameter editor
var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false);
@@ -46,18 +45,16 @@ namespace Umbraco.Core.Models
/// Ctor for creating an existing property
/// </summary>
/// <param name="id"></param>
/// <param name="key"></param>
/// <param name="alias"></param>
/// <param name="name"></param>
/// <param name="sortOrder"></param>
/// <param name="editorAlias"></param>
internal MacroProperty(int id, Guid key, string @alias, string name, int sortOrder, string editorAlias)
internal MacroProperty(int id, string @alias, string name, int sortOrder, string editorAlias)
{
_id = id;
_alias = alias;
_name = name;
_sortOrder = sortOrder;
_key = key;
//try to get the new mapped parameter editor
var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false);
@@ -69,7 +66,6 @@ namespace Umbraco.Core.Models
_editorAlias = editorAlias;
}
private Guid _key;
private string _alias;
private string _name;
private int _sortOrder;
@@ -80,7 +76,6 @@ namespace Umbraco.Core.Models
private class PropertySelectors
{
public readonly PropertyInfo KeySelector = ExpressionHelper.GetPropertyInfo<MacroProperty, Guid>(x => x.Key);
public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<MacroProperty, string>(x => x.Alias);
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<MacroProperty, string>(x => x.Name);
public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<MacroProperty, int>(x => x.SortOrder);
@@ -88,16 +83,6 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo PropertyTypeSelector = ExpressionHelper.GetPropertyInfo<MacroProperty, string>(x => x.EditorAlias);
}
/// <summary>
/// Gets or sets the Key of the Property
/// </summary>
[DataMember]
public Guid Key
{
get { return _key; }
set { SetPropertyValueAndDetectChanges(value, ref _key, Ps.Value.KeySelector); }
}
/// <summary>
/// Gets or sets the Alias of the Property
/// </summary>
+51 -33
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -8,56 +9,73 @@ using Umbraco.Core.PropertyEditors.ValueConverters;
namespace Umbraco.Core.Models
{
public static class MediaExtensions
internal static class MediaExtensions
{
/// <summary>
/// Gets the url of a media item.
/// Hack: we need to put this in a real place, this is currently just used to render the urls for a media item in the back office
/// </summary>
/// <returns></returns>
public static string GetUrl(this IMedia media, string propertyAlias, ILogger logger)
{
var propertyType = media.PropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyAlias));
if (propertyType == null) return string.Empty;
var val = media.Properties[propertyType];
if (val == null) return string.Empty;
var jsonString = val.Value as string;
if (jsonString == null) return string.Empty;
if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)
return jsonString;
if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias)
if (propertyType != null)
{
if (jsonString.DetectIsJson() == false)
return jsonString;
try
var val = media.Properties[propertyType];
if (val != null)
{
var json = JsonConvert.DeserializeObject<JObject>(jsonString);
if (json["src"] != null)
return json["src"].Value<string>();
}
catch (Exception ex)
{
logger.Error<ImageCropperValueConverter>("Could not parse the string " + jsonString + " to a json object", ex);
return string.Empty;
var jsonString = val.Value as string;
if (jsonString != null)
{
if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.ImageCropperAlias)
{
if (jsonString.DetectIsJson())
{
try
{
var json = JsonConvert.DeserializeObject<JObject>(jsonString);
if (json["src"] != null)
{
return json["src"].Value<string>();
}
}
catch (Exception ex)
{
logger.Error<ImageCropperValueConverter>("Could not parse the string " + jsonString + " to a json object", ex);
return string.Empty;
}
}
else
{
return jsonString;
}
}
else if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)
{
return jsonString;
}
//hrm, without knowing what it is, just adding a string here might not be very nice
}
}
}
// hrm, without knowing what it is, just adding a string here might not be very nice
return string.Empty;
}
/// <summary>
/// Gets the urls of a media item.
/// Hack: we need to put this in a real place, this is currently just used to render the urls for a media item in the back office
/// </summary>
/// <returns></returns>
public static string[] GetUrls(this IMedia media, IContentSection contentSection, ILogger logger)
{
return contentSection.ImageAutoFillProperties
.Select(field => media.GetUrl(field.Alias, logger))
.Where(link => string.IsNullOrWhiteSpace(link) == false)
.ToArray();
var links = new List<string>();
var autoFillProperties = contentSection.ImageAutoFillProperties.ToArray();
if (autoFillProperties.Any())
{
links.AddRange(
autoFillProperties
.Select(field => media.GetUrl(field.Alias, logger))
.Where(link => link.IsNullOrWhiteSpace() == false));
}
return links.ToArray();
}
}
}
-29
View File
@@ -1,29 +0,0 @@
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoLock")]
[PrimaryKey("id")]
[ExplicitColumns]
internal class LockDto
{
public LockDto()
{
Value = 1;
}
[Column("id")]
[PrimaryKeyColumn(Name = "PK_umbracoLock")]
public int Id { get; set; }
[Column("value")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public int Value { get; set; }
[Column("name")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(64)]
public string Name { get; set; }
}
}
+1 -6
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
@@ -14,10 +13,6 @@ namespace Umbraco.Core.Models.Rdbms
[PrimaryKeyColumn]
public int Id { get; set; }
[Column("uniqueId")]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsMacro_UniqueId")]
public Guid UniqueId { get; set; }
[Column("macroUseInEditor")]
[Constraint(Default = "0")]
public bool UseInEditor { get; set; }
@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
@@ -12,12 +11,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("id")]
[PrimaryKeyColumn]
public int Id { get; set; }
// important to use column name != cmsMacro.uniqueId (fix in v8)
[Column("uniquePropertyId")]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsMacroProperty_UniquePropertyId")]
public Guid UniqueId { get; set; }
[Column("editorAlias")]
public string EditorAlias { get; set; }
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoDeployChecksum")]
[PrimaryKey("id")]
[ExplicitColumns]
internal class UmbracoDeployChecksumDto
{
[Column("id")]
[PrimaryKeyColumn(Name = "PK_umbracoDeployChecksum")]
public int Id { get; set; }
[Column("entityType")]
[Length(32)]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoDeployChecksum", ForColumns = "entityType,entityGuid,entityPath")]
public string EntityType { get; set; }
[Column("entityGuid")]
[NullSetting(NullSetting = NullSettings.Null)]
public Guid EntityGuid { get; set; }
[Column("entityPath")]
[Length(256)]
[NullSetting(NullSetting = NullSettings.Null)]
public string EntityPath { get; set; }
[Column("localChecksum")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(32)]
public string LocalChecksum { get; set; }
[Column("compositeChecksum")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(32)]
public string CompositeChecksum { get; set; }
}
}
@@ -0,0 +1,26 @@
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoDeployDependency")]
[ExplicitColumns]
internal class UmbracoDeployDependencyDto
{
[Column("sourceId")]
[PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_umbracoDeployDependency", OnColumns = "sourceId, targetId")]
[ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id1")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public int SourceId { get; set; }
[Column("targetId")]
[ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id2")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public int TargetId { get; set; }
[Column("mode")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public int Mode { get; set; }
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ using Umbraco.Core.Strings;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a Template file.
/// Represents a Template file
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
-51
View File
@@ -1,51 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a Template file that can have its content on disk.
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class TemplateOnDisk : Template
{
/// <summary>
/// Initializes a new instance of the <see cref="TemplateOnDisk"/> class.
/// </summary>
/// <param name="name">The name of the template.</param>
/// <param name="alias">The alias of the template.</param>
public TemplateOnDisk(string name, string alias)
: base(name, alias)
{
IsOnDisk = true;
}
/// <summary>
/// Gets or sets a value indicating whether the content is on disk already.
/// </summary>
public bool IsOnDisk { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <remarks>
/// <para>Getting the content while the template is "on disk" throws,
/// the template must be saved before its content can be retrieved.</para>
/// <para>Setting the content means it is not "on disk" anymore, and the
/// template becomes (and behaves like) a normal template.</para>
/// </remarks>
public override string Content
{
get
{
return IsOnDisk ? string.Empty : base.Content;
}
set
{
base.Content = value;
IsOnDisk = false;
}
}
}
}
@@ -1,15 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI.WebControls;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Models
{
internal static class UmbracoEntityExtensions
{
/// <summary>
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static void ValidatePathWithException(this NodeDto entity)
{
//don't validate if it's empty and it has no id
if (entity.NodeId == default(int) && entity.Path.IsNullOrWhiteSpace())
return;
if (entity.Path.IsNullOrWhiteSpace())
throw new InvalidDataException(string.Format("The content item {0} has an empty path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (pathParts.Length < 2)
{
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
}
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
{
//the 2nd last id in the path must be it's parent id
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
}
}
/// <summary>
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool ValidatePath(this IUmbracoEntity entity)
{
//don't validate if it's empty and it has no id
if (entity.HasIdentity == false && entity.Path.IsNullOrWhiteSpace())
return true;
if (entity.Path.IsNullOrWhiteSpace())
return false;
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (pathParts.Length < 2)
{
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
return false;
}
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
{
//the 2nd last id in the path must be it's parent id
return false;
}
return true;
}
/// <summary>
/// This will validate the entity's path and if it's invalid it will fix it, if fixing is required it will recursively
/// check and fix all ancestors if required.
/// </summary>
/// <param name="entity"></param>
/// <param name="logger"></param>
/// <param name="getParent">A callback specified to retrieve the parent entity of the entity</param>
/// <param name="update">A callback specified to update a fixed entity</param>
public static void EnsureValidPath<T>(this T entity,
ILogger logger,
Func<T, T> getParent,
Action<T> update)
where T: IUmbracoEntity
{
if (entity.HasIdentity == false)
throw new InvalidOperationException("Could not ensure the entity path, the entity has not been assigned an identity");
if (entity.ValidatePath() == false)
{
logger.Warn(typeof(UmbracoEntityExtensions), string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.Id, entity.Path, entity.ParentId));
if (entity.ParentId == -1)
{
entity.Path = string.Concat("-1,", entity.Id);
//path changed, update it
update(entity);
}
else
{
var parent = getParent(entity);
if (parent == null)
throw new NullReferenceException("Could not ensure path for entity " + entity.Id + " could not resolve it's parent " + entity.ParentId);
//the parent must also be valid!
parent.EnsureValidPath(logger, getParent, update);
entity.Path = string.Concat(parent.Path, ",", entity.Id);
//path changed, update it
update(entity);
}
}
}
public static bool HasChildren(this IUmbracoEntity entity)
{
-32
View File
@@ -1,32 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a XSLT file
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class XsltFile : File, IXsltFile
{
public XsltFile(string path)
: this(path, (Func<File, string>) null)
{ }
internal XsltFile(string path, Func<File, string> getFileContent)
: base(path, getFileContent)
{ }
/// <summary>
/// Indicates whether the current entity has an identity, which in this case is a path/name.
/// </summary>
/// <remarks>
/// Overrides the default Entity identity check.
/// </remarks>
public override bool HasIdentity
{
get { return string.IsNullOrEmpty(Path) == false; }
}
}
}
@@ -29,24 +29,20 @@ namespace Umbraco.Core.Packaging
/// </remarks>
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(IEnumerable<byte[]> assemblys, out string[] errorReport)
{
// beware! when toying with domains, use a safe call context!
using (new SafeCallContext())
var appDomain = GetTempAppDomain();
var type = typeof(PackageBinaryInspector);
try
{
var appDomain = GetTempAppDomain();
var type = typeof(PackageBinaryInspector);
try
{
var value = (PackageBinaryInspector) appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
return result;
}
finally
{
AppDomain.Unload(appDomain);
}
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
return result;
}
finally
{
AppDomain.Unload(appDomain);
}
}
@@ -82,7 +78,7 @@ namespace Umbraco.Core.Packaging
/// <summary>
/// Performs the assembly scanning
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
@@ -111,7 +107,7 @@ namespace Umbraco.Core.Packaging
/// <summary>
/// Performs the assembly scanning
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T"></typeparam>
/// <param name="dllPath"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
@@ -158,7 +154,7 @@ namespace Umbraco.Core.Packaging
//get the list of assembly names to compare below
var loadedNames = loaded.Select(x => x.GetName().Name).ToArray();
//Then load each referenced assembly into the context
foreach (var a in loaded)
{
@@ -174,7 +170,7 @@ namespace Umbraco.Core.Packaging
}
catch (FileNotFoundException)
{
//if an exception occurs it means that a referenced assembly could not be found
//if an exception occurs it means that a referenced assembly could not be found
errors.Add(
string.Concat("This package references the assembly '",
assemblyName.Name,
@@ -183,7 +179,7 @@ namespace Umbraco.Core.Packaging
}
catch (Exception ex)
{
//if an exception occurs it means that a referenced assembly could not be found
//if an exception occurs it means that a referenced assembly could not be found
errors.Add(
string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '",
assemblyName.Name,
@@ -201,7 +197,7 @@ namespace Umbraco.Core.Packaging
{
//now we need to see if they contain any type 'T'
var reflectedAssembly = a;
try
{
var found = reflectedAssembly.GetExportedTypes()
@@ -214,8 +210,8 @@ namespace Umbraco.Core.Packaging
}
catch (Exception ex)
{
//This is a hack that nobody can seem to get around, I've read everything and it seems that
// this is quite a common thing when loading types into reflection only load context, so
//This is a hack that nobody can seem to get around, I've read everything and it seems that
// this is quite a common thing when loading types into reflection only load context, so
// we're just going to ignore this specific one for now
var typeLoadEx = ex as TypeLoadException;
if (typeLoadEx != null)
@@ -236,7 +232,7 @@ namespace Umbraco.Core.Packaging
LogHelper.Error<PackageBinaryInspector>("An error occurred scanning package assemblies", ex);
}
}
}
errorReport = errors.ToArray();
@@ -256,7 +252,7 @@ namespace Umbraco.Core.Packaging
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
if (contractType == null)
{
throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies");
@@ -285,8 +281,12 @@ namespace Umbraco.Core.Packaging
PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe
};
// create new domain with full trust
return AppDomain.CreateDomain(appName, AppDomain.CurrentDomain.Evidence, domainSetup, new PermissionSet(PermissionState.Unrestricted));
//create new domain with full trust
return AppDomain.CreateDomain(
appName,
AppDomain.CurrentDomain.Evidence,
domainSetup,
new PermissionSet(PermissionState.Unrestricted));
}
}
}
@@ -1,11 +0,0 @@
// ReSharper disable once CheckNamespace
namespace Umbraco.Core
{
static partial class Constants
{
public static class Locks
{
public const int Servers = -331;
}
}
}
@@ -1,171 +0,0 @@
#if DEBUG_DATABASES
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Umbraco.Core.Persistence
{
internal static class DatabaseDebugHelper
{
private const int CommandsSize = 100;
private static readonly Queue<Tuple<string, WeakReference<IDbCommand>>> Commands = new Queue<Tuple<string, WeakReference<IDbCommand>>>();
public static void SetCommand(IDbCommand command, string context)
{
var prof = command as StackExchange.Profiling.Data.ProfiledDbCommand;
if (prof != null) command = prof.InternalCommand;
lock (Commands)
{
Commands.Enqueue(Tuple.Create(context, new WeakReference<IDbCommand>(command)));
while (Commands.Count > CommandsSize) Commands.Dequeue();
}
}
public static string GetCommandContext(IDbCommand command)
{
lock (Commands)
{
var tuple = Commands.FirstOrDefault(x =>
{
IDbCommand c;
return x.Item2.TryGetTarget(out c) && c == command;
});
return tuple == null ? "?" : tuple.Item1;
}
}
public static string GetReferencedObjects(IDbConnection con)
{
var prof = con as StackExchange.Profiling.Data.ProfiledDbConnection;
if (prof != null) con = prof.InnerConnection;
var ceCon = con as System.Data.SqlServerCe.SqlCeConnection;
if (ceCon != null) return null; // "NotSupported: SqlCE";
var dbCon = con as DbConnection;
return dbCon == null
? "NotSupported: " + con.GetType()
: GetReferencedObjects(dbCon);
}
public static string GetReferencedObjects(DbConnection con)
{
var t = con.GetType();
var field = t.GetField("_innerConnection", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) throw new Exception("panic: _innerConnection (" + t + ").");
var innerConnection = field.GetValue(con);
var tin = innerConnection.GetType();
var fi = con is System.Data.SqlClient.SqlConnection
? tin.BaseType.BaseType.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic)
: tin.BaseType.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic);
if (fi == null)
//return "";
throw new Exception("panic: referenceCollection.");
var rc = fi.GetValue(innerConnection);
if (rc == null)
//return "";
throw new Exception("panic: innerCollection.");
field = rc.GetType().BaseType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) throw new Exception("panic: items.");
var items = field.GetValue(rc);
var prop = items.GetType().GetProperty("Length", BindingFlags.Instance | BindingFlags.Public);
if (prop == null) throw new Exception("panic: Length.");
var count = Convert.ToInt32(prop.GetValue(items, null));
var miGetValue = items.GetType().GetMethod("GetValue", new[] { typeof(int) });
if (miGetValue == null) throw new Exception("panic: GetValue.");
if (count == 0) return null;
StringBuilder result = null;
var hasb = false;
for (var i = 0; i < count; i++)
{
var referencedObj = miGetValue.Invoke(items, new object[] { i });
var hasTargetProp = referencedObj.GetType().GetProperty("HasTarget");
if (hasTargetProp == null) throw new Exception("panic: HasTarget");
var hasTarget = Convert.ToBoolean(hasTargetProp.GetValue(referencedObj, null));
if (hasTarget == false) continue;
if (hasb == false)
{
result = new StringBuilder();
result.AppendLine("ReferencedItems");
hasb = true;
}
//var inUseProp = referencedObj.GetType().GetProperty("InUse");
//if (inUseProp == null) throw new Exception("panic: InUse.");
//var inUse = Convert.ToBoolean(inUseProp.GetValue(referencedObj, null));
var inUse = "?";
var targetProp = referencedObj.GetType().GetProperty("Target");
if (targetProp == null) throw new Exception("panic: Target.");
var objTarget = targetProp.GetValue(referencedObj, null);
result.AppendFormat("\tDiff.Item id=\"{0}\" inUse=\"{1}\" type=\"{2}\" hashCode=\"{3}\"" + Environment.NewLine,
i, inUse, objTarget.GetType(), objTarget.GetHashCode());
DbCommand cmd = null;
if (objTarget is DbDataReader)
{
//var rdr = objTarget as DbDataReader;
try
{
cmd = objTarget.GetType().GetProperty("Command", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(objTarget, null) as DbCommand;
}
catch (Exception e)
{
result.AppendFormat("\t\tObjTarget: DbDataReader, Exception: {0}" + Environment.NewLine, e);
}
}
else if (objTarget is DbCommand)
{
cmd = objTarget as DbCommand;
}
if (cmd == null)
{
result.AppendFormat("\t\tObjTarget: {0}" + Environment.NewLine, objTarget.GetType());
continue;
}
result.AppendFormat("\t\tCommand type=\"{0}\" hashCode=\"{1}\"" + Environment.NewLine,
cmd.GetType(), cmd.GetHashCode());
var context = GetCommandContext(cmd);
result.AppendFormat("\t\t\tContext: {0}" + Environment.NewLine, context);
var properties = cmd.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var pi in properties)
{
if (pi.PropertyType.IsPrimitive || pi.PropertyType == typeof(string))
result.AppendFormat("\t\t\t{0}: {1}" + Environment.NewLine, pi.Name, pi.GetValue(cmd, null));
if (pi.PropertyType != typeof (DbConnection) || pi.Name != "Connection") continue;
var con1 = pi.GetValue(cmd, null) as DbConnection;
result.AppendFormat("\t\t\tConnection type=\"{0}\" state=\"{1}\" hashCode=\"{2}\"" + Environment.NewLine,
con1.GetType(), con1.State, con1.GetHashCode());
var propertiesCon = con1.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var picon in propertiesCon)
{
if (picon.PropertyType.IsPrimitive || picon.PropertyType == typeof(string))
result.AppendFormat("\t\t\t\t{0}: {1}" + Environment.NewLine, picon.Name, picon.GetValue(con1, null));
}
}
}
return result == null ? null : result.ToString();
}
}
}
#endif
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence
{
ValidateDatabase(database);
database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id",
database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id",
new { @id = nodeId });
}
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence
{
ValidateDatabase(database);
database.ExecuteScalar<int>("SELECT value FROM umbracoLock WHERE id=@id",
database.ExecuteScalar<int>("SELECT sortOrder FROM umbracoNode WHERE id=@id",
new { @id = nodeId });
}
}
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Web;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Persistence
@@ -20,24 +19,13 @@ namespace Umbraco.Core.Persistence
private readonly ILogger _logger;
public string ConnectionString { get; private set; }
public string ProviderName { get; private set; }
//very important to have ThreadStatic:
// see: http://issues.umbraco.org/issue/U4-2172
[ThreadStatic]
private static volatile UmbracoDatabase _nonHttpInstance;
// NO! see notes in v8 HybridAccessorBase
//[ThreadStatic]
//private static volatile UmbracoDatabase _nonHttpInstance;
private const string ItemKey = "Umbraco.Core.Persistence.DefaultDatabaseFactory";
private static UmbracoDatabase NonContextValue
{
get { return (UmbracoDatabase) CallContext.LogicalGetData(ItemKey); }
set
{
if (value == null) CallContext.FreeNamedDataSlot(ItemKey);
else CallContext.LogicalSetData(ItemKey, value);
}
}
private static readonly object Locker = new object();
private static readonly object Locker = new object();
/// <summary>
/// Constructor accepting custom connection string
@@ -46,12 +34,9 @@ namespace Umbraco.Core.Persistence
/// <param name="logger"></param>
public DefaultDatabaseFactory(string connectionStringName, ILogger logger)
{
if (logger == null) throw new ArgumentNullException("logger");
if (logger == null) throw new ArgumentNullException("logger");
Mandate.ParameterNotNullOrEmpty(connectionStringName, "connectionStringName");
//if (NonContextValue != null) throw new Exception("NonContextValue is not null.");
_connectionStringName = connectionStringName;
_connectionStringName = connectionStringName;
_logger = logger;
}
@@ -63,264 +48,68 @@ namespace Umbraco.Core.Persistence
/// <param name="logger"></param>
public DefaultDatabaseFactory(string connectionString, string providerName, ILogger logger)
{
if (logger == null) throw new ArgumentNullException("logger");
if (logger == null) throw new ArgumentNullException("logger");
Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString");
Mandate.ParameterNotNullOrEmpty(providerName, "providerName");
//if (NonContextValue != null) throw new Exception("NonContextValue is not null.");
ConnectionString = connectionString;
ConnectionString = connectionString;
ProviderName = providerName;
_logger = logger;
}
public UmbracoDatabase CreateDatabase()
{
UmbracoDatabase database;
//no http context, create the singleton global object
if (HttpContext.Current == null)
{
if (_nonHttpInstance == null)
{
lock (Locker)
{
//double check
if (_nonHttpInstance == null)
{
_nonHttpInstance = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
? new UmbracoDatabase(ConnectionString, ProviderName, _logger)
: new UmbracoDatabase(_connectionStringName, _logger);
}
}
}
return _nonHttpInstance;
}
// gets or creates a database, using either the call context (if no http context) or
// the current request context (http context) to store it. once done using the database,
// it should be disposed - which will remove it from whatever context it is currently
// stored in. this is automatic with http context because UmbracoDatabase implements
// IDisposeOnRequestEnd, but NOT with call context.
if (HttpContext.Current == null)
{
database = NonContextValue;
if (database == null)
{
lock (Locker)
{
database = NonContextValue;
if (database == null)
{
database = CreateDatabaseInstance(ContextOwner.CallContext);
NonContextValue = database;
}
#if DEBUG_DATABASES
else
{
Log("Get lcc", database);
}
#endif
}
}
#if DEBUG_DATABASES
else
{
Log("Get lcc", database);
}
#endif
return database;
}
if (HttpContext.Current.Items.Contains(typeof (DefaultDatabaseFactory)) == false)
{
database = CreateDatabaseInstance(ContextOwner.HttpContext);
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory), database);
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
#if DEBUG_DATABASES
Log("Get ctx", database);
#endif
}
return database;
//we have an http context, so only create one per request
if (HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory)) == false)
{
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory),
string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
? new UmbracoDatabase(ConnectionString, ProviderName, _logger)
: new UmbracoDatabase(_connectionStringName, _logger));
}
return (UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
}
// called by UmbracoDatabase when disposed, so that the factory can de-list it from context
internal void OnDispose(UmbracoDatabase disposing)
{
var value = disposing;
switch (disposing.ContextOwner)
{
case ContextOwner.CallContext:
value = NonContextValue;
break;
case ContextOwner.HttpContext:
value = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
break;
}
if (value != null && value.InstanceId != disposing.InstanceId) throw new Exception("panic: wrong db.");
switch (disposing.ContextOwner)
{
case ContextOwner.CallContext:
NonContextValue = null;
#if DEBUG_DATABASES
Log("Clr lcc", disposing);
#endif
break;
case ContextOwner.HttpContext:
HttpContext.Current.Items.Remove(typeof(DefaultDatabaseFactory));
#if DEBUG_DATABASES
Log("Clr ctx", disposing);
#endif
break;
}
disposing.ContextOwner = ContextOwner.None;
#if DEBUG_DATABASES
_databases.Remove(value);
#endif
}
#if DEBUG_DATABASES
// helps identifying when non-httpContext databases are created by logging the stack trace
private void LogCallContextStack()
{
var trace = Environment.StackTrace;
if (trace.IndexOf("ScheduledPublishing") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Scheduled Publishing");
else if (trace.IndexOf("TouchServerTask") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Server Registration");
else if (trace.IndexOf("LogScrubber") > 0)
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: Log Scrubber");
else
LogHelper.Debug<DefaultDatabaseFactory>("CallContext: " + Environment.StackTrace);
}
private readonly List<UmbracoDatabase> _databases = new List<UmbracoDatabase>();
// helps identifying database leaks by keeping track of all instances
public List<UmbracoDatabase> Databases { get { return _databases; } }
private static void Log(string message, UmbracoDatabase database)
{
LogHelper.Debug<DefaultDatabaseFactory>(message + " (" + (database == null ? "" : database.InstanceSid) + ").");
}
#endif
internal enum ContextOwner
{
None,
HttpContext,
CallContext
}
internal UmbracoDatabase CreateDatabaseInstance(ContextOwner contextOwner)
{
var database = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
? new UmbracoDatabase(ConnectionString, ProviderName, _logger)
: new UmbracoDatabase(_connectionStringName, _logger);
database.ContextOwner = contextOwner;
database.DatabaseFactory = this;
//database.EnableSqlTrace = true;
#if DEBUG_DATABASES
Log("Create " + contextOwner, database);
if (contextOwner == ContextOwner.CallContext)
LogCallContextStack();
_databases.Add(database);
#endif
return database;
}
protected override void DisposeResources()
protected override void DisposeResources()
{
UmbracoDatabase database;
if (HttpContext.Current == null)
{
database = NonContextValue;
#if DEBUG_DATABASES
Log("Release lcc", database);
#endif
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
#if DEBUG_DATABASES
Log("Release ctx", database);
#endif
}
if (database != null) database.Dispose(); // removes it from call context
}
if (HttpContext.Current == null)
{
_nonHttpInstance.Dispose();
}
else
{
if (HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory)))
{
((UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)]).Dispose();
}
}
}
// during tests, the thread static var can leak between tests
// this method provides a way to force-reset the variable
internal void ResetForTests()
internal void ResetForTests()
{
var value = NonContextValue;
if (value != null) value.Dispose();
NonContextValue = null;
}
#region SafeCallContext
// see notes in SafeCallContext - need to do this since we are using
// the logical call context...
static DefaultDatabaseFactory()
{
SafeCallContext.Register(DetachAmbientDatabase, AttachAmbientDatabase);
}
// gets a value indicating whether there is an ambient database
internal static bool HasAmbientDatabase
{
get
{
return HttpContext.Current == null
? NonContextValue != null
: HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null;
}
if (_nonHttpInstance == null) return;
_nonHttpInstance.Dispose();
_nonHttpInstance = null;
}
// detaches the current database
// ie returns the database and remove it from whatever is "context"
internal static UmbracoDatabase DetachAmbientDatabase()
{
UmbracoDatabase database;
if (HttpContext.Current == null)
{
database = NonContextValue;
NonContextValue = null;
}
else
{
database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)];
HttpContext.Current.Items.Remove(typeof (DefaultDatabaseFactory));
}
if (database != null) database.ContextOwner = ContextOwner.None;
return database;
}
// attach a current database
// ie assign it to whatever is "context"
// throws if there already is a database
internal static void AttachAmbientDatabase(object o)
{
var database = o as UmbracoDatabase;
if (o != null && database == null) throw new ArgumentException("Not an UmbracoDatabase.", "o");
var ambient = DetachAmbientDatabase();
if (ambient != null) ambient.Dispose();
if (HttpContext.Current == null)
{
//if (NonContextValue != null) throw new InvalidOperationException();
if (database == null) return;
NonContextValue = database;
database.ContextOwner = ContextOwner.CallContext;
}
else
{
//if (HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null) throw new InvalidOperationException();
if (database == null) return;
HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = database;
database.ContextOwner = ContextOwner.HttpContext;
}
}
#endregion
}
}
}
@@ -7,9 +7,12 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class MacroFactory
{
#region Implementation of IEntityFactory<Language,LanguageDto>
public IMacro BuildEntity(MacroDto dto)
{
var model = new Macro(dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.Python);
var model = new Macro(dto.Id, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.Python);
try
{
@@ -17,7 +20,7 @@ namespace Umbraco.Core.Persistence.Factories
foreach (var p in dto.MacroPropertyDtos)
{
model.Properties.Add(new MacroProperty(p.Id, p.UniqueId, p.Alias, p.Name, p.SortOrder, p.EditorAlias));
model.Properties.Add(new MacroProperty(p.Id, p.Alias, p.Name, p.SortOrder, p.EditorAlias));
}
//on initial construction we don't want to have dirty properties tracked
@@ -33,9 +36,8 @@ namespace Umbraco.Core.Persistence.Factories
public MacroDto BuildDto(IMacro entity)
{
var dto = new MacroDto
{
UniqueId = entity.Key,
var dto = new MacroDto()
{
Alias = entity.Alias,
CacheByPage = entity.CacheByPage,
CachePersonalized = entity.CacheByMember,
@@ -56,6 +58,8 @@ namespace Umbraco.Core.Persistence.Factories
return dto;
}
#endregion
private List<MacroPropertyDto> BuildPropertyDtos(IMacro entity)
{
var list = new List<MacroPropertyDto>();
@@ -63,7 +67,6 @@ namespace Umbraco.Core.Persistence.Factories
{
var text = new MacroPropertyDto
{
UniqueId = p.Key,
Alias = p.Alias,
Name = p.Name,
Macro = entity.Id,
@@ -9,10 +9,9 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class MemberTypeReadOnlyFactory
{
public IMemberType BuildEntity(MemberTypeReadOnlyDto dto, out bool needsSaving)
public IMemberType BuildEntity(MemberTypeReadOnlyDto dto)
{
var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
needsSaving = false;
var memberType = new MemberType(dto.ParentId);
@@ -48,12 +47,6 @@ namespace Umbraco.Core.Persistence.Factories
{
if (dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue;
// beware!
// means that we can return a memberType "from database" that has some property types
// that do *not* come from the database and therefore are incomplete eg have no key,
// no id, no dataTypeDefinitionId - ouch! - better notify caller of the situation
needsSaving = true;
//Add the standard PropertyType to the current list
propertyTypes.Add(standardPropertyType.Value);
@@ -33,12 +33,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
CreateUmbracNodeData();
}
if (tableName.Equals("umbracoLock"))
{
CreateUmbracoLockData();
}
if (tableName.Equals("cmsContentType"))
if(tableName.Equals("cmsContentType"))
{
CreateCmsContentTypeData();
}
@@ -146,12 +141,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1038, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1038", SortOrder = 2, UniqueId = new Guid("1251c96c-185c-4e9b-93f4-b48205573cbd"), Text = "Simple Editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1042, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1042", SortOrder = 2, UniqueId = new Guid("0a452bd5-83f9-4bc3-8403-1286e13fb77e"), Text = "Macro Container", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
}
private void CreateUmbracoLockData()
{
// all lock objects
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.Servers, Name = "Servers" });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.ServersLock, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1," + Constants.System.ServersLock, SortOrder = 1, UniqueId = new Guid("0AF5E610-A310-4B6F-925F-E928D5416AF7"), Text = "LOCK: Servers", NodeObjectType = Constants.ObjectTypes.LockObjectGuid, CreateDate = DateTime.Now });
}
private void CreateCmsContentTypeData()
@@ -82,10 +82,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{43, typeof (CacheInstructionDto)},
{44, typeof (ExternalLoginDto)},
{45, typeof (MigrationDto)},
//46, removed: UmbracoDeployChecksumDto
//47, removed: UmbracoDeployDependencyDto
{48, typeof (RedirectUrlDto) },
{49, typeof (LockDto) }
{46, typeof (UmbracoDeployChecksumDto)},
{47, typeof (UmbracoDeployDependencyDto)},
{48, typeof (RedirectUrlDto) }
};
#endregion
@@ -136,12 +136,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(7, 4, 0);
}
//if the error indicates a problem with the column cmsMacroProperty.uniquePropertyId then it is not version 7.6 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMacroProperty,uniquePropertyId"))))
{
return new Version(7, 5, 0);
}
return UmbracoVersion.Current;
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations
Logger = logger;
}
public IMigrationContext Context { get; internal set; }
internal IMigrationContext Context;
public abstract void Up();
public abstract void Down();
@@ -6,7 +6,6 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.ForeignKey;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Index;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Table;
using Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -28,24 +27,6 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create
_databaseProviders = databaseProviders;
}
public void Table<T>()
{
var tableDefinition = DefinitionFactory.GetTableDefinition(_sqlSyntax, typeof(T));
AddSql(_sqlSyntax.Format(tableDefinition));
AddSql(_sqlSyntax.FormatPrimaryKey(tableDefinition));
foreach (var sql in _sqlSyntax.Format(tableDefinition.ForeignKeys))
AddSql(sql);
foreach (var sql in _sqlSyntax.Format(tableDefinition.Indexes))
AddSql(sql);
}
private void AddSql(string sql)
{
var expression = new ExecuteSqlStatementExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { SqlStatement = sql };
_context.Expressions.Add(expression);
}
public ICreateTableWithColumnSyntax Table(string tableName)
{
var expression = new CreateTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
@@ -8,8 +8,6 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create
{
public interface ICreateBuilder : IFluentSyntax
{
void Table<T>();
ICreateTableWithColumnSyntax Table(string tableName);
ICreateColumnOnTableSyntax Column(string columnName);
@@ -1,39 +0,0 @@
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive
{
[Migration("7.5.5", 101, GlobalSettings.UmbracoMigrationName)]
public class AddLockObjects : MigrationBase
{
public AddLockObjects(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
EnsureLockObject(Constants.Locks.Servers, "Servers");
}
public override void Down()
{
// not implemented
}
private void EnsureLockObject(int id, string name)
{
Execute.Code(db =>
{
var exists = db.Exists<LockDto>(id);
if (exists) return string.Empty;
// be safe: delete old umbracoNode lock objects if any
db.Execute("DELETE FROM umbracoNode WHERE id=@id;", new { id });
// then create umbracoLock object
db.Execute("INSERT umbracoLock (id, name, value) VALUES (@id, @name, 1);", new { id, name });
return string.Empty;
});
}
}
}
@@ -1,32 +0,0 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive
{
[Migration("7.5.5", 100, GlobalSettings.UmbracoMigrationName)]
public class AddLockTable : MigrationBase
{
public AddLockTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains("umbracoLock") == false)
{
Create.Table("umbracoLock")
.WithColumn("id").AsInt32().PrimaryKey("PK_umbracoLock")
.WithColumn("value").AsInt32().NotNullable()
.WithColumn("name").AsString(64).NotNullable();
}
}
public override void Down()
{
// not implemented
}
}
}
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer
// for some reason it was not, so it was created during migrations but not during
// new installs, so for ppl that upgrade, make sure they have it
EnsureLockObject(Constants.Locks.Servers, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
}
public override void Down()
@@ -1,75 +0,0 @@
using System;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)]
public class AddMacroUniqueIdColumn : MigrationBase
{
public AddMacroUniqueIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals("cmsMacro") && x.ColumnName.InvariantEquals("uniqueId")) == false)
{
Create.Column("uniqueId").OnTable("cmsMacro").AsGuid().Nullable();
Execute.Code(UpdateMacroGuids);
Alter.Table("cmsMacro").AlterColumn("uniqueId").AsGuid().NotNullable();
Create.Index("IX_cmsMacro_UniqueId").OnTable("cmsMacro").OnColumn("uniqueId")
.Ascending()
.WithOptions().NonClustered()
.WithOptions().Unique();
}
if (columns.Any(x => x.TableName.InvariantEquals("cmsMacroProperty") && x.ColumnName.InvariantEquals("uniquePropertyId")) == false)
{
Create.Column("uniquePropertyId").OnTable("cmsMacroProperty").AsGuid().Nullable();
Execute.Code(UpdateMacroPropertyGuids);
Alter.Table("cmsMacroProperty").AlterColumn("uniquePropertyId").AsGuid().NotNullable();
Create.Index("IX_cmsMacroProperty_UniquePropertyId").OnTable("cmsMacroProperty").OnColumn("uniquePropertyId")
.Ascending()
.WithOptions().NonClustered()
.WithOptions().Unique();
}
}
private static string UpdateMacroGuids(Database database)
{
var updates = database.Query<dynamic>("SELECT id, macroAlias FROM cmsMacro")
.Select(macro => Tuple.Create((int) macro.id, ((string) macro.macroAlias).ToGuid()))
.ToList();
foreach (var update in updates)
database.Execute("UPDATE cmsMacro set uniqueId=@guid WHERE id=@id", new { guid = update.Item2, id = update.Item1 });
return string.Empty;
}
private static string UpdateMacroPropertyGuids(Database database)
{
var updates = database.Query<dynamic>(@"SELECT cmsMacroProperty.id id, macroPropertyAlias propertyAlias, cmsMacro.macroAlias macroAlias
FROM cmsMacroProperty
JOIN cmsMacro ON cmsMacroProperty.macro=cmsMacro.id")
.Select(prop => Tuple.Create((int) prop.id, ((string) prop.macroAlias + "____" + (string) prop.propertyAlias).ToGuid()))
.ToList();
foreach (var update in updates)
database.Execute("UPDATE cmsMacroProperty set uniquePropertyId=@guid WHERE id=@id", new { guid = update.Item2, id = update.Item1 });
return string.Empty;
}
public override void Down()
{
throw new NotImplementedException();
}
}
}
@@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)]
public class RemoveUmbracoDeployTables : MigrationBase
{
public RemoveUmbracoDeployTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
// there are two versions of umbracoDeployDependency,
// 1. one created by 7.4 and never used, we need to remove it (has a sourceId column)
// 2. one created by Deploy itself, we need to keep it (has a sourceUdi column)
if (tables.InvariantContains("umbracoDeployDependency"))
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals("umbracoDeployDependency") && x.ColumnName.InvariantEquals("sourceId")))
Delete.Table("umbracoDeployDependency");
}
// always remove umbracoDeployChecksum
if (tables.InvariantContains("umbracoDeployChecksum"))
Delete.Table("umbracoDeployChecksum");
}
public override void Down()
{
throw new NotImplementedException();
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
Create.Column("isMaster").OnTable("umbracoServer").AsBoolean().NotNullable().WithDefaultValue(0);
}
EnsureLockObject(Constants.Locks.Servers, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
}
public override void Down()
+7 -14
View File
@@ -181,7 +181,7 @@ namespace Umbraco.Core.Persistence
CommonConstruct();
}
public enum DBType
internal enum DBType
{
SqlServer,
SqlServerCE,
@@ -190,9 +190,7 @@ namespace Umbraco.Core.Persistence
Oracle,
SQLite
}
private DBType _dbType = DBType.SqlServer;
public DBType DatabaseType { get { return _dbType; } }
DBType _dbType = DBType.SqlServer;
// Common initialization
private void CommonConstruct()
@@ -226,18 +224,13 @@ namespace Umbraco.Core.Persistence
// Automatically close one open shared connection
public void Dispose()
{
Dispose(true);
// Automatically close one open connection reference
// (Works with KeepConnectionAlive and manually opening a shared connection)
CloseSharedConnection();
}
protected virtual void Dispose(bool disposing)
{
// Automatically close one open connection reference
// (Works with KeepConnectionAlive and manually opening a shared connection)
CloseSharedConnection();
}
// Set to true to keep the first opened connection alive until this object is disposed
public bool KeepConnectionAlive { get; set; }
// Set to true to keep the first opened connection alive until this object is disposed
public bool KeepConnectionAlive { get; set; }
// Open a connection (can be nested)
public void OpenSharedConnection()
@@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -29,6 +27,7 @@ namespace Umbraco.Core.Persistence
return sql.From(sqlSyntax.GetQuotedTableName(tableName));
}
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate)
{
var expresionist = new PocoToSqlExpressionVisitor<T>();
@@ -36,6 +35,13 @@ namespace Umbraco.Core.Persistence
return sql.Where(whereExpression, expresionist.GetSqlParameters());
}
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate, ISqlSyntaxProvider sqlSyntax)
{
var expresionist = new PocoToSqlExpressionVisitor<T>(sqlSyntax);
var whereExpression = expresionist.Visit(predicate);
return sql.Where(whereExpression, expresionist.GetSqlParameters());
}
private static string GetFieldName<T>(Expression<Func<T, object>> fieldSelector, ISqlSyntaxProvider sqlSyntax)
{
var field = ExpressionHelper.FindProperty(fieldSelector) as PropertyInfo;
@@ -59,26 +65,6 @@ namespace Umbraco.Core.Persistence
return sql.Where(fieldName + " IN (@values)", new { values });
}
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
public static Sql WhereAnyIn<TDto>(this Sql sql, Expression<Func<TDto, object>>[] fieldSelectors, IEnumerable values)
{
return sql.WhereAnyIn(fieldSelectors, values, SqlSyntaxContext.SqlSyntaxProvider);
}
public static Sql WhereAnyIn<TDto>(this Sql sql, Expression<Func<TDto, object>>[] fieldSelectors, IEnumerable values, ISqlSyntaxProvider sqlSyntax)
{
var fieldNames = fieldSelectors.Select(x => GetFieldName(x, sqlSyntax)).ToArray();
var sb = new StringBuilder();
sb.Append("(");
for (var i = 0; i < fieldNames.Length; i++)
{
if (i > 0) sb.Append(" OR ");
sb.Append(fieldNames[i] + " IN (@values)");
}
sb.Append(")");
return sql.Where(sb.ToString(), new { values });
}
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember)
{
@@ -14,6 +14,14 @@ namespace Umbraco.Core.Persistence.Querying
{
private readonly Database.PocoData _pd;
public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider)
: base(syntaxProvider)
{
}
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
public PocoToSqlExpressionVisitor()
: base(SqlSyntaxContext.SqlSyntaxProvider)
{
@@ -3,6 +3,48 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Relators
{
//internal class TaskUserRelator
//{
// internal TaskDto Current;
// internal TaskDto Map(TaskDto a, UserDto p)
// {
// // Terminating call. Since we can return null from this function
// // we need to be ready for PetaPoco to callback later with null
// // parameters
// if (a == null)
// return Current;
// // Is this the same TaskDto as the current one we're processing
// if (Current != null && Current.Id == a.Id)
// {
// // Yes, set the user
// Current.MacroPropertyDtos.Add(p);
// // Return null to indicate we're not done with this Macro yet
// return null;
// }
// // This is a different Macro to the current one, or this is the
// // first time through and we don't have one yet
// // Save the current Macro
// var prev = Current;
// // Setup the new current Macro
// Current = a;
// Current.MacroPropertyDtos = new List<MacroPropertyDto>();
// //this can be null since we are doing a left join
// if (p.Alias != null)
// {
// Current.MacroPropertyDtos.Add(p);
// }
// // Return the now populated previous Macro (or null if first time through)
// return prev;
// }
//}
internal class MacroPropertyRelator
{
internal MacroDto Current;
@@ -342,6 +342,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
//Update entity with correct values
@@ -445,8 +446,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
if (entity.IsPropertyDirty("UpdateDate") == false || entity.UpdateDate == default(DateTime))
entity.UpdateDate = DateTime.Now;
entity.UpdateDate = DateTime.Now;
}
//Ensure unique name on the same level
@@ -480,6 +480,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Updates the (base) node data - umbracoNode
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
nodeDto.ValidatePathWithException();
var o = Database.Update(nodeDto);
//Only update this DTO if the contentType has actually changed
@@ -217,20 +217,10 @@ namespace Umbraco.Core.Persistence.Repositories
protected string GetFileContent(string filename)
{
if (FileSystem.FileExists(filename) == false)
return null;
try
using (var stream = FileSystem.OpenFile(filename))
using (var reader = new StreamReader(stream, Encoding.UTF8, true))
{
using (var stream = FileSystem.OpenFile(filename))
using (var reader = new StreamReader(stream, Encoding.UTF8, true))
{
return reader.ReadToEnd();
}
}
catch
{
return null; // deal with race conds
return reader.ReadToEnd();
}
}
@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
internal interface IMacroRepository : IRepositoryQueryable<int, IMacro>, IReadRepository<Guid, IMacro>
internal interface IMacroRepository : IRepositoryQueryable<int, IMacro>
{
//IEnumerable<IMacro> GetAll(params string[] aliases);
@@ -1,5 +1,4 @@
using System.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -8,7 +7,5 @@ namespace Umbraco.Core.Persistence.Repositories
void AddFolder(string folderPath);
void DeleteFolder(string folderPath);
bool ValidatePartialView(IPartialView partialView);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);
}
}
@@ -1,12 +1,9 @@
using System.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IScriptRepository : IRepository<string, Script>
{
bool ValidateScript(Script script);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);
}
}
@@ -1,4 +1,4 @@
using System.IO;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
@@ -6,7 +6,5 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IStylesheetRepository : IRepository<string, Stylesheet>
{
bool ValidateStylesheet(Stylesheet stylesheet);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);
}
}
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
@@ -58,19 +57,5 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="template"><see cref="ITemplate"/> to validate</param>
/// <returns>True if Script is valid, otherwise false</returns>
bool ValidateTemplate(ITemplate template);
/// <summary>
/// Gets the content of a template as a stream.
/// </summary>
/// <param name="filepath">The filesystem path to the template.</param>
/// <returns>The content of the template.</returns>
Stream GetFileContentStream(string filepath);
/// <summary>
/// Sets the content of a template.
/// </summary>
/// <param name="filepath">The filesystem path to the template.</param>
/// <param name="content">The content of the template.</param>
void SetFileContent(string filepath, Stream content);
}
}
@@ -1,12 +0,0 @@
using System.IO;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IXsltFileRepository : IRepository<string, XsltFile>
{
bool ValidateXsltFile(XsltFile xsltFile);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);
}
}
@@ -26,17 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
return GetBySql(sql);
}
public IMacro Get(Guid id)
{
var sql = GetBaseQuery().Where("uniqueId=@Id", new { Id = id });
return GetBySql(sql);
}
private IMacro GetBySql(Sql sql)
{
var macroDto = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql).FirstOrDefault();
if (macroDto == null)
return null;
@@ -51,28 +41,28 @@ namespace Umbraco.Core.Persistence.Repositories
return entity;
}
public IEnumerable<IMacro> GetAll(params Guid[] ids)
{
return ids.Length > 0 ? ids.Select(Get) : GetAllNoIds();
}
public bool Exists(Guid id)
{
return Get(id) != null;
}
protected override IEnumerable<IMacro> PerformGetAll(params int[] ids)
{
return ids.Length > 0 ? ids.Select(Get) : GetAllNoIds();
}
if (ids.Any())
{
return PerformGetAllOnIds(ids);
}
private IEnumerable<IMacro> GetAllNoIds()
{
var sql = GetBaseQuery(false);
return ConvertFromDtos(Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql))
.ToArray();// we don't want to re-iterate again!
}
private IEnumerable<IMacro> PerformGetAllOnIds(params int[] ids)
{
if (ids.Any() == false) yield break;
foreach (var id in ids)
{
yield return Get(id);
}
}
private IEnumerable<IMacro> ConvertFromDtos(IEnumerable<MacroDto> dtos)
{
var factory = new MacroFactory();
@@ -134,7 +124,7 @@ namespace Umbraco.Core.Persistence.Repositories
var list = new List<string>
{
"DELETE FROM cmsMacroProperty WHERE macro = @Id",
"DELETE FROM cmsMacro WHERE id = @Id"
"DELETE FROM cmsMacro WHERE id = @Id"
};
return list;
}
@@ -156,7 +146,7 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var propDto in dto.MacroPropertyDtos)
{
// need to set the id explicitly here
//need to set the id explicitly here
propDto.Macro = id;
var propId = Convert.ToInt32(Database.Insert(propDto));
entity.Properties[propDto.Alias].Id = propId;
@@ -175,60 +165,71 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(dto);
//update the properties if they've changed
var macro = (Macro) entity;
var macro = (Macro)entity;
if (macro.IsPropertyDirty("Properties") || macro.Properties.Any(x => x.IsDirty()))
{
var ids = dto.MacroPropertyDtos.Where(x => x.Id > 0).Select(x => x.Id).ToArray();
if (ids.Length > 0)
Database.Delete<MacroPropertyDto>("WHERE macro=@macro AND id NOT IN (@ids)", new { macro = dto.Id, ids });
else
Database.Delete<MacroPropertyDto>("WHERE macro=@macro", new { macro = dto.Id });
// detect new aliases, replace with temp aliases
// this ensures that we don't have collisions, ever
var aliases = new Dictionary<string, string>();
foreach (var propDto in dto.MacroPropertyDtos)
//now we need to delete any props that have been removed
foreach (var propAlias in macro.RemovedProperties)
{
var prop = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
if (prop == null) throw new Exception("oops: property.");
if (propDto.Id == 0 || prop.IsPropertyDirty("Alias"))
{
var tempAlias = Guid.NewGuid().ToString("N").Substring(0, 8);
aliases[tempAlias] = propDto.Alias;
propDto.Alias = tempAlias;
}
//delete the property
Database.Delete<MacroPropertyDto>("WHERE macro=@macroId AND macroPropertyAlias=@propAlias",
new { macroId = macro.Id, propAlias = propAlias });
}
// insert or update existing properties, with temp aliases
//for any that exist on the object, we need to determine if we need to update or insert
foreach (var propDto in dto.MacroPropertyDtos)
{
if (propDto.Id == 0)
if (macro.AddedProperties.Contains(propDto.Alias))
{
// insert
propDto.Id = Convert.ToInt32(Database.Insert(propDto));
macro.Properties[aliases[propDto.Alias]].Id = propDto.Id;
//we need to insert since this was added and re-assign the new id
var propId = Convert.ToInt32(Database.Insert(propDto));
macro.Properties[propDto.Alias].Id = propId;
}
else
{
// update
var property = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
if (property == null) throw new Exception("oops: property.");
if (property.IsDirty())
Database.Update(propDto);
//This will only work if the Alias hasn't been changed
if (macro.Properties.ContainsKey(propDto.Alias))
{
//only update if it's dirty
if (macro.Properties[propDto.Alias].IsDirty())
{
Database.Update(propDto);
}
}
else
{
var property = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
if (property != null && property.IsDirty())
{
Database.Update(propDto);
}
}
}
}
// replace the temp aliases with the real ones
foreach (var propDto in dto.MacroPropertyDtos)
{
if (aliases.ContainsKey(propDto.Alias) == false) continue;
propDto.Alias = aliases[propDto.Alias];
Database.Update(propDto);
}
}
entity.ResetDirtyProperties();
}
//public IEnumerable<IMacro> GetAll(params string[] aliases)
//{
// if (aliases.Any())
// {
// var q = new Query<IMacro>();
// foreach (var alias in aliases)
// {
// q.Where(macro => macro.Alias == alias);
// }
// var wheres = string.Join(" OR ", q.WhereClauses());
// }
// else
// {
// return GetAll(new int[] {});
// }
//}
}
}
@@ -324,6 +324,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
//Update entity with correct values
@@ -397,6 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Updates the (base) node data - umbracoNode
var nodeDto = dto.ContentDto.NodeDto;
nodeDto.ValidatePathWithException();
var o = Database.Update(nodeDto);
//Only update this DTO if the contentType has actually changed
@@ -300,19 +300,13 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="dtos"></param>
/// <returns></returns>
private IEnumerable<IMemberType> BuildFromDtos(List<MemberTypeReadOnlyDto> dtos)
private static IEnumerable<IMemberType> BuildFromDtos(List<MemberTypeReadOnlyDto> dtos)
{
if (dtos == null || dtos.Any() == false)
return Enumerable.Empty<IMemberType>();
var factory = new MemberTypeReadOnlyFactory();
return dtos.Select(x =>
{
bool needsSaving;
var memberType = factory.BuildEntity(x, out needsSaving);
if (needsSaving) PersistUpdatedItem(memberType);
return memberType;
}).ToList();
return dtos.Select(factory.BuildEntity);
}
/// <summary>

Some files were not shown because too many files have changed in this diff Show More