Compare commits

..

83 Commits

Author SHA1 Message Date
Sebastiaan Janssen 7ffb73e467 Updates Examine dependency 2015-04-23 16:45:24 +02:00
Sebastiaan Janssen 57c3665d0f Show different ReadMe for fresh install vs. upgrade installs 2015-04-23 16:01:23 +02:00
Sebastiaan Janssen 8037f929aa Ignore more slow tests 2015-04-23 15:35:42 +02:00
Sebastiaan Janssen fec3e57ed6 Ignore tests that don't run to completion on slower build server 2015-04-23 15:19:18 +02:00
Sebastiaan Janssen 5106ffab97 Mark as Release Candidate 2015-04-23 15:00:12 +02:00
Sebastiaan Janssen 9f569171c3 Suppress error (it's useless information) 2015-04-23 14:59:13 +02:00
Sebastiaan Janssen d607041ca4 U4-6450 Creating Partial View Macro without 'Create Macro' selected still tries to create a macro
#U4-6450 Fixed
2015-04-23 14:55:57 +02:00
Sebastiaan Janssen b3daf7f55d Merge branch 'dev-v7-xmllock' into dev-v7 2015-04-23 14:23:23 +02:00
Sebastiaan Janssen a77d3bc4ac Adds some more defaults tests 2015-04-23 14:22:42 +02:00
Stephan c4aa87d73d Backport Xml concurrency fixes 2015-04-13 19:12:03 +02:00
Stephan 57909489cb AsyncLock - import 2015-04-13 19:12:00 +02:00
Shannon c78b371965 updates the BackgroundTaskRunnerAwaiter so that it exits if the runner is not running when it is ctor'd, adds diags. 2015-04-09 17:59:34 +10:00
Shannon c525be3e44 Completes: U4-6474 Update to latest examine v0.1.62 2015-04-09 15:33:58 +10:00
Shannon cc804bd3bd removes duplicate test 2015-04-08 17:57:00 +10:00
Shannon 24dc8309f1 sets ConfigureAwait(false) for XmlCacheFilePersister await calls and BackgroundTaskRunner await calls since these are always on background threads, no synchronizing with any context. 2015-04-08 17:15:21 +10:00
Shannon ce81f9b1b1 we'll try these tests again 2015-04-08 17:08:06 +10:00
Shannon 3f70761d49 this should fix one of the tests 2015-04-08 17:01:38 +10:00
Shannon dee45146a4 applies a max timout for BackgroundTaskRunnerTests 2015-04-08 16:55:34 +10:00
Shannon b35397b464 uses property type ctor overload to accept an alias which prob should have been there all along since in most cases you need an alias to create one (same prob goes for property groups, etc... but oh well). Adds a couple tests for case insensitive comparisons in PropertyCollection 2015-04-08 16:33:16 +10:00
Shannon 147cc92ac5 Updates BackgroundTaskRunner & tests: removes the need for most Thread.Sleep which is error prone, this is done by updating the GetAwaiter() method to be a custom awaiter so we can await the entire background task running operations. We then add a property called CurrentThraedingTask which then allows awaiting the current Task instance. Adds error handling to all event handlers for the task runner. Changes events to be TypedEventHandlers 2015-04-08 16:28:42 +10:00
Shannon 3230fce76a fixes lock on PropertyCollection, should just be a normal lock 2015-04-08 11:47:16 +10:00
Shannon 1d22c1e81e fixes get hash code on Entity base class 2015-04-08 11:47:00 +10:00
Shannon ece2e2359e Updates PropertyGroup's equality comparisons to: check the base comparison, then check for a case insensitive comparison with name, fixes GetHashCode to be consistent with the Equals method. 2015-04-08 11:31:14 +10:00
Shannon 8cbe53f49d Updates PropertyType to have a ctor overload accepting an alias when one already exists. Updates it's equality comparisons to: check the base comparison, then check for a case insensitive comparison with alias, fixes GetHashCode to be consistent with the Equals method. 2015-04-08 11:30:35 +10:00
Shannon Deminick cac3ddc3c8 Merge pull request #666 from JimBobSquarePants/dev-v7
Remove Content Service Setter case sensitivity
2015-04-08 10:07:34 +10:00
Shannon 0eac50ceba Fixes: U4-6506 Grid config in package.manifest doesn't allow for multiple grid editors and adds tests 2015-04-07 17:24:35 +10:00
Shannon d5e735ac14 Fixes singleton accessors and adds ctor which would allow unit testability. 2015-04-07 12:07:54 +10:00
Shannon Deminick 8d841d3537 Merge pull request #667 from AndyButland/u4-1733
U4-1733 - Added overloads to RedirectToUmbracoPage methods to allow passing of a querystring
2015-04-07 11:05:05 +10:00
Sebastiaan Janssen 5f175874b2 Fixes tests and adds a few 2015-04-02 17:52:59 +02:00
Sebastiaan Janssen fa053426c3 Don't disableFindContentByIdPath when in preview mode 2015-04-02 17:16:56 +02:00
AndyButland 437e7f0ffc Removed unused usings from previous commit 2015-04-01 23:29:43 +02:00
AndyButland afa6c35bb5 Added overloads to RedirectToUmbracoPage methods to allow passing of a querystring 2015-04-01 23:27:20 +02:00
James South 8d00dbf648 Remove Content Service Setter case sensitivity
Makes property alias key checking case-insensitive bringing behaviour in
line with IPublishedContent getters.
2015-04-01 12:37:28 +01:00
Stephan cd26a1ad21 U4-6479 - fix GetCulture for unpublished content + add tests 2015-04-01 12:59:01 +02:00
Shannon 437ab4aaf1 Fixes installer permission steps - this cannot just arbitrarily write to the umbraco xml cache file 2015-04-01 17:44:27 +11:00
Shannon 051e9366e5 updates js undefined check 2015-04-01 13:39:54 +11:00
Shannon Deminick 46c6503320 Merge pull request #665 from magnuskragelund/dev-v7-U4-6469-mntp-referencing-deleted-nodes
fixes bug when trying to render a deleted node in multinode tree picker
2015-04-01 13:36:18 +11:00
Shannon 1cf2fc7870 updates sln docs and removes pdf test bits 2015-04-01 11:22:10 +11:00
Shannon cf1c38dcaa fixes build 2015-03-31 17:04:32 +11:00
Shannon 73d7cd4856 updates sln file to remove the PDF proj and removes the sln folder containing umbraco examine libs 2015-03-31 14:27:18 +11:00
Shannon 1b87fd074c Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-31 13:49:39 +11:00
Shannon 33f21e0528 Updates build and solution to exclude UmbracoExamine.PDF, removes files for UmbracoExamine.PDF, updates links to license in nuspec files 2015-03-31 13:49:28 +11:00
Sebastiaan Janssen 0b5a2452c2 U4-6476 Examine Issue: The CancellationTokenSource has been disposed.
#U4-6476 Fixed Due in version 7.2.5
2015-03-30 14:38:59 +02:00
Shannon cc8962f090 Removes the old umbracoexamine.azure libs 2015-03-30 20:07:26 +11:00
Shannon f6b9fdba72 Removes old docs folder 2015-03-30 20:06:51 +11:00
Shannon e0effec3b4 fixes pdf build to only have it's required files in the zip 2015-03-30 19:13:13 +11:00
Shannon 76a2f433d9 simplifies build since we don't have the examine azure stuff 2015-03-30 18:42:47 +11:00
Shannon a2bfca4d3a Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-30 17:40:46 +11:00
Shannon fb24becd0d completes: U4-6468 Move all 'Strategies' in the Web project that performs Core data operations to the Core project 2015-03-30 17:40:36 +11:00
Shannon 58857c5d83 Moves publishing strategies into CacheRefresherEventHandler where the event handlers are supposed to exist. 2015-03-30 17:36:54 +11:00
Shannon 7a31d6cae3 better exception logging for examine 2015-03-30 17:23:28 +11:00
Shannon Deminick 021bb6d367 Merge pull request #661 from kgiszewski/dev-v7
Fix for U4-6462
2015-03-30 16:27:19 +11:00
Shannon Deminick 0742108ec1 Merge pull request #662 from AndyButland/u4-6412
U4-6412 - Ensures footer breadcrumb in content and media returned in correct order
2015-03-30 10:44:08 +11:00
Magnus Kragelund 1c3a7b95fc fixes bug when trying to render a deleted node in multinode tree picker 2015-03-27 11:02:35 +01:00
Shannon fda72b2acc changes the MaxWaitMilliseconds tp 30000 to be consistent with the 7.3 branch 2015-03-27 15:19:31 +11:00
Shannon ec742d1f66 removes the usages of ILogger and ProfilerLogger since this is not for 7.3.0, will need to re-fix for 7.3 branch. 2015-03-27 14:56:12 +11:00
Shannon 4bdac1475f fixes log scrub interval with ms with updated background tasks 2015-03-27 14:52:38 +11:00
Stephan 58ce04e26b cleanup 2015-03-27 14:49:31 +11:00
Shannon 4c3de920c6 Removes the 'else' so that 'wait' is always checked. 2015-03-27 14:49:31 +11:00
Stephan be37026587 refactor latched background tasks, now use a task for xml
Conflicts:
	src/Umbraco.Web.UI/config/ClientDependency.config
2015-03-27 14:49:25 +11:00
Stephan a73b7a5849 refactor Scheduler to use new BackgroundTaskRunner capabilities
Conflicts:
	src/Umbraco.Web/Scheduling/Scheduler.cs
2015-03-27 14:48:47 +11:00
Stephan b7436dc55f refactor BackgroundTaskRunner 2015-03-27 14:47:42 +11:00
Shannon bc068b201d Updates BackgroundTaskRunner to ensure canceled or skipped tasks are disposed, updated tests to reflect 2015-03-27 14:47:42 +11:00
Shannon 912b01c9aa Updates BackgroundTaskRunner to support more complex options including the ability to only execute the last/final task in the queue. Added tests to support, updated the 'content' object to use this option so that only the last task in the queue will execute so that file persisting doesn't get queued but the correctly queued data will be written. 2015-03-27 14:47:42 +11:00
Shannon a82035061c Updates BackgroundTaskRunner to support async operations. Updates the content xml cache file persisting to use a IBackgroundTask instead of the strange queuing for persistence - which was super strange because in many cases another request thread will actually be the thread that is persisting the xml file than the request thread that requested it created. This implementation is far better, the xml file will be persisted on a non request thread and will handle multiple documents being published at the same time guaranteeing that the latest published version is the one persisted. The file persistence is also web aware (due to how BackgroundTaskRunner works) so during app shutdown the file will still be written if it's currently being processed. 2015-03-27 14:47:41 +11:00
AndyButland 0c107fd869 Ensures footer breadcrumb in content and media returned in correct order 2015-03-25 21:52:39 +01:00
kgiszewski 6419f301f7 Fix for U4-6462 2015-03-25 16:30:47 -04:00
Sebastiaan Janssen e775471013 Set disableFindContentByIdPath=false for local debugging of Umb source (the release config was already correct) 2015-03-25 17:08:53 +01:00
Shannon Deminick cc109d1ed0 Merge pull request #660 from perosb/U4-6444
Fix for U4-6444
2015-03-25 09:16:49 +11:00
Sebastiaan Janssen 9dfccd86fd U4-2499 Remove the possibility for accessing pages with nodeid's or give a redirect option
#U4-2499 Fixed Due in version 7.2.5
2015-03-24 20:09:48 +01:00
Per Osbeck ffaf1f96b9 Fix for U4-6444 2015-03-24 09:28:53 +01:00
Shannon Deminick aa3c1d645b Merge pull request #659 from robertjf/dev-v7
Issue#U4-6448 typo fix in pageIndex Mandate checks
2015-03-24 12:45:35 +11:00
Robert Foster c6b393f2e8 typo fix in pageIndex Mandate checks 2015-03-24 12:30:28 +11:00
Shannon Deminick 344d909e6b Merge pull request #657 from webprofijt/packageinstaller-spelling
Fix some spelling mistakes in package installer
2015-03-23 09:45:56 +11:00
Arnoud de Vries e3925f18ef Fix some spelling mistakes in package installer 2015-03-22 16:29:53 +01:00
Sebastiaan Janssen 53a4d063e0 U4-464 The embedded passwordStrength.js file is GPL-licensed
#U4-464 Fixed Due in version 7.2.5
2015-03-22 13:13:51 +01:00
Sebastiaan Janssen 907e5e7ca4 Bump version 2015-03-20 11:25:59 +01:00
Sebastiaan Janssen 94e281e951 Merge branch '7.2.4' into dev-v7 2015-03-20 11:23:30 +01:00
Sebastiaan Janssen 8c4eba7837 "It's simple until you make it complicated"
- So now this logic has been un-complicated: look for the web.config file, if it exists and has a umbracoConfigurationStatus appSetting then don't overwrite the web.config, the user is upgrading umbraco and config transforms can do their thing. Otherwise, copy in a fresh web.config with all the required Umbraco bits.
2015-03-20 10:16:53 +01:00
Shannon 08a2ec6883 Fixes: U4-6436 Localization in the back office doesn't work immediately after app restart 2015-03-20 18:15:31 +11:00
Shannon b02e0c0232 Fixes: U4-6433 Usercontrol loaded after package installation not executing 2015-03-20 17:47:33 +11:00
Shannon fff0151b62 Updates build process to: Ensure the AssemblyFileVersion is set to the real umbraco version, AssemblyInformationalVersion is set to the real umbraco version including the pre-release name if there is one, AssemblyCopyright has the correct year. 2015-03-20 09:31:14 +11:00
Sebastiaan Janssen 90fdbb007e Bump version 2015-03-19 14:45:00 +01:00
141 changed files with 4283 additions and 3641 deletions
+1 -2
View File
@@ -23,7 +23,7 @@ ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %rel
ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
SET nuGetFolder=%CD%\..\src\packages\
..\src\.nuget\NuGet.exe sources Remove -Name MyGetUmbracoCore
..\src\.nuget\NuGet.exe sources Remove -Name MyGetUmbracoCore >NUL
..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder%
@@ -56,7 +56,6 @@ REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version%
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version%
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoExamine.PDF.nuspec
IF ERRORLEVEL 1 GOTO :showerror
+89 -112
View File
@@ -1,4 +1,3 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
@@ -14,18 +13,18 @@
<Import Project="..\tools\UmbracoMSBuildTasks\Umbraco.MSBuild.Tasks.Targets" />
<Import Project="..\tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputFile ParameterType="System.String" Required="true" />
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputFile ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Linq" />
<Using Namespace="System.Security.Cryptography" />
<Code Type="Fragment" Language="cs">
<![CDATA[
<Using Namespace="System.IO" />
<Using Namespace="System.Linq" />
<Using Namespace="System.Security.Cryptography" />
<Code Type="Fragment" Language="cs">
<![CDATA[
using (var ms = new MemoryStream())
{
foreach (var item in InputFiles)
@@ -48,11 +47,11 @@
}
}
]]>
</Code>
</Code>
</Task>
</UsingTask>
</UsingTask>
<!--
****************************************************
VARIABLES
@@ -76,23 +75,19 @@
<BuildZipFileName>UmbracoCms$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileName>
<BuildZipFileNameBin>UmbracoCms.AllBinaries$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameBin>
<BuildZipFileNameWebPi>UmbracoCms.WebPI$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameWebPi>
<BuildZipExaminePDFName>UmbracoExamine.PDF.0.6.0.zip</BuildZipExaminePDFName>
<IncludeSymbols>False</IncludeSymbols>
<BuildFolderRelativeToProjects>..\..\build\$(BuildFolder)</BuildFolderRelativeToProjects>
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
<SolutionBinFolder>$(BuildFolder)bin\</SolutionBinFolder>
<WebAppFolder>$(BuildFolder)WebApp\</WebAppFolder>
<WebPiFolder>$(BuildFolder)WebPi\</WebPiFolder>
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
<SolutionBinFolderRelativeToProjects>$(BuildFolderRelativeToProjects)bin\</SolutionBinFolderRelativeToProjects>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)bin\</SolutionBinFolderAbsolutePath>
<WebAppFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebApp\</WebAppFolderRelativeToProjects>
<WebAppFolderAbsolutePath>$(BuildFolderAbsolutePath)WebApp\</WebAppFolderAbsolutePath>
<WebPiFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebPi\</WebPiFolderRelativeToProjects>
<WebPiFolderAbsolutePath>$(BuildFolderAbsolutePath)WebPi\</WebPiFolderAbsolutePath>
<ExaminePDFPath>$(BuildFolderAbsolutePath)UmbracoExamine.PDF\</ExaminePDFPath>
<ExamineAzurePath>$(BuildFolderAbsolutePath)UmbracoExamine.Azure\</ExamineAzurePath>
<ExaminePDFAzurePath>$(BuildFolderAbsolutePath)UmbracoExamine.PDF.Azure\</ExaminePDFAzurePath>
</PropertyGroup>
<ItemGroup>
@@ -114,16 +109,16 @@
*****************************************************
-->
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
<Message Text="Build finished" />
</Target>
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
<ItemGroup>
</Target>
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
<ItemGroup>
<WebPiFile Include="$(BuildZipFileNameWebPi)" />
</ItemGroup>
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
</ItemGroup>
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
</Target>
<Target Name="CleanUp" DependsOnTargets="ZipWebPiApp">
@@ -153,7 +148,7 @@
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameWebPi)%22 %22$(WebPiFolderAbsolutePath)*%22" />
</Target>
<Target Name="ZipWebApp" DependsOnTargets="ZipUmbracoExaminePDF" >
<Target Name="ZipWebApp" DependsOnTargets="CreateSystemFolders" >
<Message Text="Starting to zip to $(buildDate)-$(BuildZipFileName)" Importance="high" />
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22 -x!dotLess.Core.dll" />
@@ -162,14 +157,6 @@
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
</Target>
<Target Name="ZipUmbracoExaminePDF" DependsOnTargets="CreateSystemFolders" >
<Message Text="Starting to zip to $(buildDate)-$(BuildZipExaminePDFName)" Importance="high" />
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipExaminePDFName)%22 %22$(ExaminePDFPath)*%22" />
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipExaminePDFName)" Importance="high" />
</Target>
<Target Name="CreateSystemFolders" DependsOnTargets="CopyBelleBuild" Inputs="@(SystemFolders)" Outputs="%(Identity).Dummy">
<MakeDir Directories="@(SystemFolders)" />
</Target>
@@ -177,14 +164,14 @@
<Target Name="CopyBelleBuild" DependsOnTargets="CopyLibraries" >
<ItemGroup>
<BelleFiles Include="..\src\Umbraco.Web.UI.Client\build\belle\**\*.*" Exclude="..\src\Umbraco.Web.UI.Client\build\belle\index.html" />
</ItemGroup>
<Copy SourceFiles="@(BelleFiles)"
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</ItemGroup>
<Copy SourceFiles="@(BelleFiles)"
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<Target Name="CopyLibraries" DependsOnTargets="OffsetTimestamps" >
<!-- Copy SQL CE -->
<ItemGroup>
@@ -217,7 +204,7 @@
</Target>
<!-- Copy the transformed web.config file to the root -->
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
<ItemGroup>
<WebConfigFile Include="..\src\Umbraco.Web.UI\web.$(BuildConfiguration).Config.transformed" />
</ItemGroup>
@@ -226,24 +213,24 @@
DestinationFiles="$(WebAppFolder)Web.config"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<!-- Copy the config files and rename them to *.config.transform -->
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
</Target>
<!-- Copy the config files and rename them to *.config.transform -->
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
<ItemGroup>
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
</ItemGroup>
<Copy SourceFiles="@(ConfigFiles)"
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(WebConfigTransformFile)"
DestinationFiles="$(ConfigsFolder)Web.config.transform"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
</ItemGroup>
<Copy SourceFiles="@(ConfigFiles)"
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(WebConfigTransformFile)"
DestinationFiles="$(ConfigsFolder)Web.config.transform"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<!-- Copy the xml documentation to the bin folder -->
@@ -261,66 +248,31 @@
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
<ItemGroup>
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
<ItemGroup>
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
</ItemGroup>
<RemoveDir Directories="@(PresentationFolderToDelete)" />
<RemoveDir Directories="@(PresentationFolderToDelete)" />
</Target>
<Target Name="CompileProjects" DependsOnTargets="CompileExternalExamineProjects">
<Target Name="CompileProjects" DependsOnTargets="SetVersionNumber">
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
<!-- For UseWPP_CopyWebApplication=True see http://stackoverflow.com/questions/1983575/copywebapplication-with-web-config-transformations -->
<!-- Build the Umbraco.Web.UI project -->
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath)" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False">
</MSBuild>
<!-- DONE -->
<Message Text="Finished compiling projects" Importance="high" />
</Target>
<Target Name="CompileExternalExamineProjects" DependsOnTargets="SetVersionNumber">
<Message Text="Building externally shipped examine projects" Importance="high" />
<!-- First remove old files from the examine builds -->
<ItemGroup>
<FilesToDeleteBeforeBuild Include="$(ExaminePDFPath)*;$(ExamineAzurePath)*"/>
</ItemGroup>
<Delete Files="@(FilesToDeleteBeforeBuild)" />
<!--
Build the UmbracoExamine.PDF.Azure library ... this builds the 3 projects that we want to bundle separately so no need to build each one separately .
We'll just build this one and then manually copy the DLLs to where we want them.
-->
<MSBuild Projects="..\src\UmbracoExamine.PDF.Azure\UmbracoExamine.PDF.Azure.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(ExaminePDFAzurePath);" Targets="Clean;Build;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False">
</MSBuild>
<ItemGroup>
<ExaminePDFFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.pdf;$(ExaminePDFAzurePath)**\UmbracoExamine.PDF.xml;$(ExaminePDFAzurePath)**\itextsharp.dll" />
<ExamineAzureFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.pdb;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.xml" />
<ExaminePDFAzureFiles Include="$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.dll;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.pdb;$(ExaminePDFAzurePath)**\UmbracoExamine.Azure.PDF.xml" />
</ItemGroup>
<!-- Now copy the built files to their correct deployment folders-->
<Copy SourceFiles="@(ExaminePDFFiles)"
DestinationFolder="$(ExaminePDFPath)"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(ExamineAzureFiles)"
DestinationFolder="$(ExamineAzurePath)"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<!-- Then just delete the files we don't want to ship in the UmbracoExamine.Azure.PDF folder -->
<ItemGroup>
<FilesToDeleteAfterBuild Include="$(ExaminePDFAzurePath)*" Exclude="$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.dll;$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.pdb;$(ExaminePDFAzurePath)UmbracoExamine.PDF.Azure.xml"/>
</ItemGroup>
<Delete Files="@(FilesToDeleteAfterBuild)" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<PropertyGroup>
<NewVersion>$(BUILD_RELEASE)</NewVersion>
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
</PropertyGroup>
<!-- Match & replace 3 and 4 digit version numbers and -beta (if it's there) -->
<PropertyGroup>
<NewVersion>$(BUILD_RELEASE)</NewVersion>
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
</PropertyGroup>
<!-- Match & replace 3 and 4 digit version numbers and -beta (if it's there) -->
<FileUpdate
Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Regex="(\d+)\.(\d+)\.(\d+)(.(\d+))?"
@@ -329,10 +281,35 @@
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Regex="CurrentComment { get { return &quot;(.+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)&quot;"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
Namespaces="&lt;Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
Query="//x:UmbracoVersion"
Value="$(NewVersion)" />
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
<FileUpdate
Files="..\src\SolutionInfo.cs"
Regex="AssemblyFileVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyFileVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
<FileUpdate
Condition="'$(BUILD_COMMENT)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_COMMENT)&quot;)"/>
<FileUpdate
Condition="'$(BUILD_COMMENT)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the copyright year-->
<FileUpdate
Condition="'$(BUILD_COMMENT)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyCopyright\(&quot;Copyright © Umbraco (\d{4})&quot;\)"
ReplacementText="AssemblyCopyright(&quot;Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))&quot;)"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
Namespaces="&lt;Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
Query="//x:UmbracoVersion"
Value="$(NewVersion)" />
</Target>
</Project>
@@ -6,7 +6,7 @@
<title>Umbraco Cms Core All Binaries</title>
<authors>Morten Christensen</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
+1 -1
View File
@@ -6,7 +6,7 @@
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
+2 -2
View File
@@ -6,7 +6,7 @@
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
@@ -32,7 +32,7 @@
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
<dependency id="Examine" version="[0.1.60, 1.0.0)" />
<dependency id="Examine" version="[0.1.62, 1.0.0)" />
<dependency id="ImageProcessor" version="[1.9.5, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.3.1, 5.0.0)" />
</dependencies>
+2 -1
View File
@@ -6,7 +6,7 @@
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
@@ -34,6 +34,7 @@
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
<file src="tools\install.ps1" target="tools\install.ps1" />
<file src="tools\Readme.txt" target="tools\Readme.txt" />
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
+1 -1
View File
@@ -5,7 +5,7 @@
<version>0.7.0</version>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://umbraco.codeplex.com/license</licenseUrl>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
-10
View File
@@ -8,21 +8,11 @@
----------------------------------------------------
A note about running Umbraco from Visual Studio.
Don't forget to build!
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
We've done our best to transform your configuration files but in case something is not quite right: remember we
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
sure to do a compare and merge with the NuGetBackup folder.
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
+33
View File
@@ -0,0 +1,33 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
Don't forget to build!
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
We've done our best to transform your configuration files but in case something is not quite right: remember we
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
sure to do a compare and merge with the NuGetBackup folder.
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.org:
http://our.umbraco.org/contribute/releases
- Umbraco
+10 -3
View File
@@ -73,13 +73,20 @@ if ($project) {
{
$packageWebConfigSource = Join-Path $rootPath "UmbracoFiles\Web.config"
Copy-Item $packageWebConfigSource $destinationWebConfig -Force
}
}
$installFolder = Join-Path $projectDestinationPath "Install"
if(Test-Path $installFolder) {
Remove-Item $installFolder -Force -Recurse -Confirm:$false
}
# Open readme.txt file
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
# Open appropriate readme
if($copyWebconfig -eq $true)
{
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
}
else
{
$DTE.ItemOperations.OpenFile($toolsPath + '\ReadmeUpgrade.txt')
}
}
+2 -1
View File
@@ -1,2 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.2.4
7.2.5
RC
-13
View File
@@ -1,13 +0,0 @@
Umbraco CMS version 4.5 is licensed under the OSI approved MIT License:
Copyright (c) 2007-2010 umbraco I/S.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Umbraco versions prior to 4.5 were licensed under a combination of MIT and a propriatary UI license. The license history can be found here:
http://umbraco.codeplex.com/license
-33
View File
@@ -1,33 +0,0 @@
Umbraco - the simple, flexible and friendly ASP.NET CMS
=======================================================
For the first time on the Microsoft platform a free user and developer friendly cms that makes it quick and easy to create websites - or a breeze to build complex web applications. umbraco got award-winning integration capabilities and supports your ASP.NET User and Custom Controls out of the box. It's a developers dream and your users will love it too. Used by more than 57.000 active websites including Heinz.com, Peugeot.com, NAIAS.com and Microsofts documentinteropinitiative.org website you can be sure that the technology is proven, stable and scales.
More info at http://umbraco.com
Exploring the repository
========================
Most contributors will work on their own fork and should pick the branch in which they need their fix. For more information see:
http://our.umbraco.org/contribute/guidelines-for-core-contribution
Exploring the source
====================
The Umbraco source code is never required for you to start building sites. If you are not using the source code for debugging purposes or to contribute to the source then please download the binaries listed at http://umbraco.codeplex.com
With that said, the Umbraco solution can be opened in Visual Studio 2010 with SP1 installed or Visual Studio 2012.
Contributing
============
Umbraco is Open Source which means you can contribute to make it great!
Read all about it at http://our.umbraco.org/contribute
Forums
======
We have a forum running on http://our.umbraco.org for friendly community support.
Documentation
=============
Our documentation section provides help on installing and using Umbraco including API reference documentation: http://our.umbraco.org/documentation
Submitting Issues
=================
Another way you can contribute to Umbraco is by providing issue reports, for information on how to submit an issue report refer to our online guide at http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature
+6 -3
View File
@@ -1,8 +1,8 @@
using System.Reflection;
using System.Reflection;
using System.Resources;
[assembly: AssemblyCompany("umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2012")]
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -10,3 +10,6 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5-RC")]
+198
View File
@@ -0,0 +1,198 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Core
{
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
//
// notes:
// - this is NOT a reader/writer lock
// - this is NOT a recursive lock
//
internal class AsyncLock
{
private readonly SemaphoreSlim _semaphore;
private readonly Semaphore _semaphore2;
private readonly IDisposable _releaser;
private readonly Task<IDisposable> _releaserTask;
public AsyncLock()
: this (null)
{ }
public AsyncLock(string name)
{
// WaitOne() waits until count > 0 then decrements count
// Release() increments count
// initial count: the initial count value
// maximum count: the max value of count, and then Release() throws
if (string.IsNullOrWhiteSpace(name))
{
// anonymous semaphore
// use one unique releaser, that will not release the semaphore when finalized
// because the semaphore is destroyed anyway if the app goes down
_semaphore = new SemaphoreSlim(1, 1); // create a local (to the app domain) semaphore
_releaser = new SemaphoreSlimReleaser(_semaphore);
_releaserTask = Task.FromResult(_releaser);
}
else
{
// named semaphore
// use dedicated releasers, that will release the semaphore when finalized
// because the semaphore is system-wide and we cannot leak counts
_semaphore2 = new Semaphore(1, 1, name); // create a system-wide named semaphore
}
}
private IDisposable CreateReleaser()
{
// for anonymous semaphore, use the unique releaser, else create a new one
return _semaphore != null
? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore)
: (IDisposable)new NamedSemaphoreReleaser(_semaphore2);
}
public Task<IDisposable> LockAsync()
{
var wait = _semaphore != null
? _semaphore.WaitAsync()
: WaitOneAsync(_semaphore2);
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
: wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public IDisposable Lock()
{
if (_semaphore != null)
_semaphore.Wait();
else
_semaphore2.WaitOne();
return _releaser ?? CreateReleaser(); // anonymous vs named
}
public IDisposable Lock(int millisecondsTimeout)
{
var entered = _semaphore != null
? _semaphore.Wait(millisecondsTimeout)
: _semaphore2.WaitOne(millisecondsTimeout);
if (entered == false)
throw new TimeoutException("Failed to enter the lock within timeout.");
return _releaser ?? CreateReleaser(); // anonymous vs named
}
// note - before making those classes some structs, read
// about "impure methods" and mutating readonly structs...
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
{
private readonly Semaphore _semaphore;
internal NamedSemaphoreReleaser(Semaphore semaphore)
{
_semaphore = semaphore;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
// critical
_semaphore.Release();
}
// we WANT to release the semaphore because it's a system object
// ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
// which means that the finalizer "should" run in all situations
// however... that can fail with System.ObjectDisposedException because the
// underlying handle was closed... because we cannot guarantee that the semaphore
// is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
// which should keep it around and then we free the handle?
// so... I'm not sure this is safe really...
~NamedSemaphoreReleaser()
{
Dispose(false);
}
}
private class SemaphoreSlimReleaser : IDisposable
{
private readonly SemaphoreSlim _semaphore;
internal SemaphoreSlimReleaser(SemaphoreSlim semaphore)
{
_semaphore = semaphore;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
// normal
_semaphore.Release();
}
}
~SemaphoreSlimReleaser()
{
Dispose(false);
}
}
// http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
// http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
// F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
// version below should be OK
private static Task WaitOneAsync(WaitHandle handle)
{
var tcs = new TaskCompletionSource<object>();
var callbackHandleInitLock = new object();
lock (callbackHandleInitLock)
{
RegisteredWaitHandle callbackHandle = null;
// ReSharper disable once RedundantAssignment
callbackHandle = ThreadPool.RegisterWaitForSingleObject(
handle,
(state, timedOut) =>
{
tcs.SetResult(null);
// we take a lock here to make sure the outer method has completed setting the local variable callbackHandle.
lock (callbackHandleInitLock)
{
// ReSharper disable once PossibleNullReferenceException
// ReSharper disable once AccessToModifiedClosure
callbackHandle.Unregister(null);
}
},
/*state:*/ null,
/*millisecondsTimeOutInterval:*/ Timeout.Infinite,
/*executeOnlyOnce:*/ true);
}
return tcs.Task;
}
}
}
@@ -8,6 +8,8 @@
bool DisableAlternativeTemplates { get; }
bool DisableFindContentByIdPath { get; }
string UrlProviderMode { get; }
}
@@ -21,6 +21,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return (bool) base["disableAlternativeTemplates"]; }
}
[ConfigurationProperty("disableFindContentByIdPath", DefaultValue = "false")]
public bool DisableFindContentByIdPath
{
get { return (bool) base["disableFindContentByIdPath"]; }
}
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
public string UrlProviderMode
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.2.4");
private static readonly Version Version = new Version("7.2.5");
/// <summary>
/// Gets the current version of Umbraco.
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return ""; } }
public static string CurrentComment { get { return "RC"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
+9 -18
View File
@@ -192,73 +192,64 @@ namespace Umbraco.Core
{
{
Comments,
new PropertyType(PropertyEditors.TextboxMultipleAlias, DataTypeDatabaseType.Ntext, true)
new PropertyType(PropertyEditors.TextboxMultipleAlias, DataTypeDatabaseType.Ntext, true, Comments)
{
Alias = Comments,
Name = CommentsLabel
}
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Integer, true, FailedPasswordAttempts)
{
Alias = FailedPasswordAttempts,
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true)
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true, IsApproved)
{
Alias = IsApproved,
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true)
new PropertyType(PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, true, IsLockedOut)
{
Alias = IsLockedOut,
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastLockoutDate)
{
Alias = LastLockoutDate,
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastLoginDate)
{
Alias = LastLoginDate,
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, true, LastPasswordChangeDate)
{
Alias = LastPasswordChangeDate,
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true, PasswordAnswer)
{
Alias = PasswordAnswer,
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true)
new PropertyType(PropertyEditors.NoEditAlias, DataTypeDatabaseType.Nvarchar, true, PasswordQuestion)
{
Alias = PasswordQuestion,
Name = PasswordQuestionLabel
}
}
@@ -0,0 +1,35 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Serialization;
namespace Umbraco.Core.Manifest
{
/// <summary>
/// Ensures that virtual paths are taken care of
/// </summary>
internal class GridEditorConverter : JsonCreationConverter<GridEditor>
{
protected override GridEditor Create(Type objectType, JObject jObject)
{
return new GridEditor();
}
protected override void Deserialize(JObject jObject, GridEditor target, JsonSerializer serializer)
{
base.Deserialize(jObject, target, serializer);
if (target.View.IsNullOrWhiteSpace() == false && target.View.StartsWith("~/"))
{
target.View = IOHelper.ResolveUrl(target.View);
}
if (target.Render.IsNullOrWhiteSpace() == false && target.Render.StartsWith("~/"))
{
target.Render = IOHelper.ResolveUrl(target.Render);
}
}
}
}
+20 -3
View File
@@ -39,7 +39,8 @@ namespace Umbraco.Core.Manifest
internal static IEnumerable<GridEditor> GetGridEditors(JArray jsonEditors)
{
return JsonConvert.DeserializeObject<IEnumerable<GridEditor>>(
jsonEditors.ToString());
jsonEditors.ToString(),
new GridEditorConverter());
}
/// <summary>
@@ -209,14 +210,30 @@ namespace Umbraco.Core.Manifest
}
}
}
//replace virtual paths for each property editor
if (deserialized["gridEditors"] != null)
{
foreach (JObject p in deserialized["gridEditors"])
{
if (p["view"] != null)
{
ReplaceVirtualPaths(p["view"]);
}
if (p["render"] != null)
{
ReplaceVirtualPaths(p["render"]);
}
}
}
var manifest = new PackageManifest()
{
JavaScriptInitialize = jConfig,
StylesheetInitialize = cssConfig,
PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(),
ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(),
GridEditors = propEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray()
ParameterEditors = paramEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(),
GridEditors = gridEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray()
};
result.Add(manifest);
}
+1 -1
View File
@@ -436,7 +436,7 @@ namespace Umbraco.Core.Models
return;
}
var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
if (propertyType == null)
{
throw new Exception(String.Format("No PropertyType exists with the supplied alias: {0}", propertyTypeAlias));
+10 -4
View File
@@ -17,7 +17,6 @@ namespace Umbraco.Core.Models.EntityBase
public abstract class Entity : TracksChangesEntityBase, IEntity, IRememberBeingDirty, ICanBeDirty
{
private bool _hasIdentity;
private int? _hash;
private int _id;
private Guid _key;
private DateTime _createDate;
@@ -175,6 +174,7 @@ namespace Umbraco.Core.Models.EntityBase
}
}
//TODO: Make this NOT virtual or even exist really!
public virtual bool SameIdentityAs(IEntity other)
{
if (ReferenceEquals(null, other))
@@ -195,11 +195,13 @@ namespace Umbraco.Core.Models.EntityBase
return SameIdentityAs(other);
}
//TODO: Make this NOT virtual or even exist really!
public virtual Type GetRealType()
{
return GetType();
}
//TODO: Make this NOT virtual or even exist really!
public virtual bool SameIdentityAs(Entity other)
{
if (ReferenceEquals(null, other))
@@ -226,9 +228,13 @@ namespace Umbraco.Core.Models.EntityBase
public override int GetHashCode()
{
if (!_hash.HasValue)
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
return _hash.Value;
unchecked
{
var hashCode = HasIdentity.GetHashCode();
hashCode = (hashCode * 397) ^ Id;
hashCode = (hashCode * 397) ^ GetType().GetHashCode();
return hashCode;
}
}
public virtual object DeepClone()
+12 -8
View File
@@ -16,12 +16,14 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
{
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
private readonly object _addLocker = new object();
internal Action OnAdd;
internal Func<Property, bool> ValidateAdd { get; set; }
internal PropertyCollection()
{}
: base(StringComparer.InvariantCultureIgnoreCase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PropertyCollection"/> class with a delegate responsible for validating the addition of <see cref="Property"/> instances.
@@ -29,11 +31,13 @@ namespace Umbraco.Core.Models
/// <param name="validationCallback">The validation callback.</param>
/// <remarks></remarks>
internal PropertyCollection(Func<Property, bool> validationCallback)
: this()
{
ValidateAdd = validationCallback;
}
public PropertyCollection(IEnumerable<Property> properties)
: this()
{
Reset(properties);
}
@@ -78,7 +82,7 @@ namespace Umbraco.Core.Models
internal new void Add(Property item)
{
using (new WriteLock(_addLocker))
lock (_addLocker)
{
var key = GetKeyForItem(item);
if (key != null)
@@ -113,14 +117,14 @@ namespace Umbraco.Core.Models
/// <remarks></remarks>
public new bool Contains(string propertyTypeAlias)
{
return this.Any(x => x.Alias == propertyTypeAlias);
return base.Contains(propertyTypeAlias);
}
public int IndexOfKey(string key)
{
for (var i = 0; i < this.Count; i++)
{
if (this[i].Alias == key)
if (this[i].Alias.InvariantEquals(key))
{
return i;
}
@@ -145,7 +149,7 @@ namespace Umbraco.Core.Models
{
get
{
return this.FirstOrDefault(x => x.Alias == propertyType.Alias);
return this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyType.Alias));
}
}
@@ -165,7 +169,7 @@ namespace Umbraco.Core.Models
/// <param name="propertyTypes">List of PropertyType</param>
protected internal void EnsurePropertyTypes(IEnumerable<PropertyType> propertyTypes)
{
if(/*!this.Any() &&*/ propertyTypes != null)
if (/*!this.Any() &&*/ propertyTypes != null)
{
foreach (var propertyType in propertyTypes)
{
@@ -180,7 +184,7 @@ namespace Umbraco.Core.Models
/// <param name="propertyTypes">List of PropertyType</param>
protected internal void EnsureCleanPropertyTypes(IEnumerable<PropertyType> propertyTypes)
{
if(propertyTypes != null)
if (propertyTypes != null)
{
//Remove PropertyTypes that doesn't exist in the list of new PropertyTypes
var aliases = this.Select(p => p.Alias).Except(propertyTypes.Select(x => x.Alias)).ToList();
+6 -11
View File
@@ -120,27 +120,22 @@ namespace Umbraco.Core.Models
public bool Equals(PropertyGroup other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
if (base.Equals(other)) return true;
//Check whether the PropertyGroup's properties are equal.
return Id.Equals(other.Id) && Name.Equals(other.Name);
return Name.InvariantEquals(other.Name);
}
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashName = Name == null ? 0 : Name.GetHashCode();
int baseHash = base.GetHashCode();
//Get hash code for the Id field.
int hashId = Id.GetHashCode();
//Get hash code for the Alias field.
int nameHash = Name.ToLowerInvariant().GetHashCode();
//Calculate the hash code for the product.
return hashName ^ hashId;
return baseHash ^ nameHash;
}
}
+44 -18
View File
@@ -38,11 +38,21 @@ namespace Umbraco.Core.Models
_propertyEditorAlias = dataTypeDefinition.PropertyEditorAlias;
_dataTypeDatabaseType = dataTypeDefinition.DatabaseType;
}
public PropertyType(IDataTypeDefinition dataTypeDefinition, string propertyTypeAlias)
: this(dataTypeDefinition)
{
SetAlias(propertyTypeAlias);
}
internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
: this(propertyEditorAlias, dataTypeDatabaseType, false)
{
PropertyEditorAlias = propertyEditorAlias;
}
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, string propertyTypeAlias)
: this(propertyEditorAlias, dataTypeDatabaseType, false, propertyTypeAlias)
{
}
/// <summary>
@@ -58,6 +68,21 @@ namespace Umbraco.Core.Models
_dataTypeDatabaseType = dataTypeDatabaseType;
}
/// <summary>
/// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is.
/// </summary>
/// <param name="propertyEditorAlias"></param>
/// <param name="dataTypeDatabaseType"></param>
/// <param name="isExplicitDbType"></param>
/// <param name="propertyTypeAlias"></param>
internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType, string propertyTypeAlias)
{
_isExplicitDbType = isExplicitDbType;
_propertyEditorAlias = propertyEditorAlias;
_dataTypeDatabaseType = dataTypeDatabaseType;
SetAlias(propertyTypeAlias);
}
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Name);
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Alias);
private static readonly PropertyInfo DescriptionSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Description);
@@ -96,15 +121,9 @@ namespace Umbraco.Core.Models
get { return _alias; }
set
{
//NOTE: WE are doing this because we don't want to do a ToSafeAlias when the alias is the special case of
// being prefixed with Constants.PropertyEditors.InternalGenericPropertiesPrefix
// which is used internally
SetPropertyValueAndDetectChanges(o =>
{
_alias = value.StartsWith(Constants.PropertyEditors.InternalGenericPropertiesPrefix)
? value
: value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
SetAlias(value);
return _alias;
}, _alias, AliasSelector);
}
@@ -284,6 +303,17 @@ namespace Umbraco.Core.Models
}
}
private void SetAlias(string value)
{
//NOTE: WE are doing this because we don't want to do a ToSafeAlias when the alias is the special case of
// being prefixed with Constants.PropertyEditors.InternalGenericPropertiesPrefix
// which is used internally
_alias = value.StartsWith(Constants.PropertyEditors.InternalGenericPropertiesPrefix)
? value
: value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
}
/// <summary>
/// Create a new Property object from a "raw" database value.
/// </summary>
@@ -431,26 +461,22 @@ namespace Umbraco.Core.Models
public bool Equals(PropertyType other)
{
//Check whether the compared object is null.
if (ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (ReferenceEquals(this, other)) return true;
if (base.Equals(other)) return true;
//Check whether the PropertyType's properties are equal.
return Alias.Equals(other.Alias) && Name.Equals(other.Name);
return Alias.InvariantEquals(other.Alias);
}
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashName = Name == null ? 0 : Name.GetHashCode();
int baseHash = base.GetHashCode();
//Get hash code for the Alias field.
int hashAlias = Alias.GetHashCode();
int hashAlias = Alias.ToLowerInvariant().GetHashCode();
//Calculate the hash code for the product.
return hashName ^ hashAlias;
return baseHash ^ hashAlias;
}
public override object DeepClone()
@@ -105,9 +105,9 @@ namespace Umbraco.Core.Persistence.Factories
propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success)
propDbType.Success,
typeDto.Alias)
{
Alias = typeDto.Alias,
DataTypeDefinitionId = typeDto.DataTypeId,
Description = typeDto.Description,
Id = typeDto.Id.Value,
@@ -158,9 +158,9 @@ namespace Umbraco.Core.Persistence.Factories
propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success)
propDbType.Success,
typeDto.Alias)
{
Alias = typeDto.Alias,
DataTypeDefinitionId = typeDto.DataTypeId,
Description = typeDto.Description,
HelpText = typeDto.HelpText,
@@ -66,7 +66,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, DataTypeDatabaseType dbType, string propertyTypeAlias)
{
return new PropertyType(propertyEditorAlias, dbType);
return new PropertyType(propertyEditorAlias, dbType, propertyTypeAlias);
}
protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
@@ -451,8 +451,7 @@ AND umbracoNode.id <> @id",
var list = new List<PropertyType>();
foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false))
{
var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse<DataTypeDatabaseType>(true), dto.Alias);
propType.Alias = dto.Alias;
var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse<DataTypeDatabaseType>(true), dto.Alias);
propType.DataTypeDefinitionId = dto.DataTypeId;
propType.Description = dto.Description;
propType.Id = dto.Id;
@@ -1041,10 +1040,9 @@ AND umbracoNode.id <> @id",
.Select(group => new PropertyGroup(new PropertyTypeCollection(
result
.Where(row => row.pgId == group.GroupId && row.ptId != null)
.Select(row => new PropertyType(row.dtPropEdAlias, Enum<DataTypeDatabaseType>.Parse(row.dtDbType))
.Select(row => new PropertyType(row.dtPropEdAlias, Enum<DataTypeDatabaseType>.Parse(row.dtDbType), row.ptAlias)
{
//fill in the rest of the property type properties
Alias = row.ptAlias,
Description = row.ptDesc,
DataTypeDefinitionId = row.dtId,
Id = row.ptId,
@@ -1070,10 +1068,9 @@ AND umbracoNode.id <> @id",
.Where(x => x.pgId == null)
//filter based on the current content type
.Where(x => x.contentTypeId == currId)
.Select(row => new PropertyType(row.dtPropEdAlias, Enum<DataTypeDatabaseType>.Parse(row.dtDbType))
.Select(row => new PropertyType(row.dtPropEdAlias, Enum<DataTypeDatabaseType>.Parse(row.dtDbType), row.ptAlias)
{
//fill in the rest of the property type properties
Alias = row.ptAlias,
Description = row.ptDesc,
DataTypeDefinitionId = row.dtId,
Id = row.ptId,
@@ -258,7 +258,8 @@ namespace Umbraco.Core.Persistence.Repositories
return new PropertyType(propertyEditorAlias, propDbType.Result,
//This flag tells the property type that it has an explicit dbtype and that it cannot be changed
// which is what we want for the built-in properties.
propDbType.Success);
propDbType.Success,
propertyTypeAlias);
}
/// <summary>
+2 -2
View File
@@ -500,7 +500,7 @@ namespace Umbraco.Core.Services
public IEnumerable<IContent> GetPagedChildren(int id, int pageIndex, int pageSize, out int totalChildren,
string orderBy, Direction orderDirection, string filter = "")
{
Mandate.ParameterCondition(pageIndex >= 0, "pageSize");
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
@@ -530,7 +530,7 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
Mandate.ParameterCondition(pageIndex >= 0, "pageSize");
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
+2 -2
View File
@@ -408,7 +408,7 @@ namespace Umbraco.Core.Services
public IEnumerable<IMedia> GetPagedChildren(int id, int pageIndex, int pageSize, out int totalChildren,
string orderBy, Direction orderDirection, string filter = "")
{
Mandate.ParameterCondition(pageIndex >= 0, "pageSize");
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork()))
{
@@ -437,7 +437,7 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
{
Mandate.ParameterCondition(pageIndex >= 0, "pageSize");
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
using (var repository = _repositoryFactory.CreateMediaRepository(_uowProvider.GetUnitOfWork()))
{
+6 -12
View File
@@ -1221,44 +1221,38 @@ namespace Umbraco.Core.Services
Name = "Membership",
Id = --identity
};
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, Constants.Conventions.Member.Comments)
{
Alias = Constants.Conventions.Member.Comments,
Name = Constants.Conventions.Member.CommentsLabel,
SortOrder = 0,
Id = --identity
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, Constants.Conventions.Member.IsApproved)
{
Alias = Constants.Conventions.Member.IsApproved,
Name = Constants.Conventions.Member.IsApprovedLabel,
SortOrder = 3,
Id = --identity
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, Constants.Conventions.Member.IsLockedOut)
{
Alias = Constants.Conventions.Member.IsLockedOut,
Name = Constants.Conventions.Member.IsLockedOutLabel,
SortOrder = 4,
Id = --identity
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastLockoutDate)
{
Alias = Constants.Conventions.Member.LastLockoutDate,
Name = Constants.Conventions.Member.LastLockoutDateLabel,
SortOrder = 5,
Id = --identity
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastLoginDate)
{
Alias = Constants.Conventions.Member.LastLoginDate,
Name = Constants.Conventions.Member.LastLoginDateLabel,
SortOrder = 6,
Id = --identity
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date)
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastPasswordChangeDate)
{
Alias = Constants.Conventions.Member.LastPasswordChangeDate,
Name = Constants.Conventions.Member.LastPasswordChangeDateLabel,
SortOrder = 7,
Id = --identity
@@ -648,9 +648,8 @@ namespace Umbraco.Core.Services
if (dataTypeDefinition == null) continue;
}
var propertyType = new PropertyType(dataTypeDefinition)
var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value)
{
Alias = property.Element("Alias").Value,
Name = property.Element("Name").Value,
Description = property.Element("Description").Value,
Mandatory = property.Element("Mandatory").Value.ToLowerInvariant().Equals("true"),
@@ -5,6 +5,7 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Strategies
{
//TODO: This should just exist in the content service/repo!
public sealed class RelateOnCopyHandler : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+2
View File
@@ -136,6 +136,7 @@
<Compile Include="ApplicationContext.cs" />
<Compile Include="ApplicationEventHandler.cs" />
<Compile Include="AssemblyExtensions.cs" />
<Compile Include="AsyncLock.cs" />
<Compile Include="Attempt{T}.cs" />
<Compile Include="Auditing\AuditTrail.cs" />
<Compile Include="Auditing\Audit.cs" />
@@ -314,6 +315,7 @@
<Compile Include="HideFromTypeFinderAttribute.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="IDisposeOnRequestEnd.cs" />
<Compile Include="Manifest\GridEditorConverter.cs" />
<Compile Include="Media\Exif\BitConverterEx.cs" />
<Compile Include="Media\Exif\ExifBitConverter.cs" />
<Compile Include="Media\Exif\ExifEnums.cs" />
+28
View File
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
@@ -13,6 +16,31 @@ namespace Umbraco.Core
/// </summary>
internal static class XmlExtensions
{
/// <summary>
/// Saves the xml document async
/// </summary>
/// <param name="xdoc"></param>
/// <param name="filename"></param>
/// <returns></returns>
public static async Task SaveAsync(this XmlDocument xdoc, string filename)
{
if (xdoc.DocumentElement == null)
throw new XmlException("Cannot save xml document, there is no root element");
using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true))
using (var xmlWriter = XmlWriter.Create(fs, new XmlWriterSettings
{
Async = true,
Encoding = Encoding.UTF8,
Indent = true
}))
{
//NOTE: There are no nice methods to write it async, only flushing it async. We
// could implement this ourselves but it'd be a very manual process.
xdoc.WriteTo(xmlWriter);
await xmlWriter.FlushAsync().ConfigureAwait(false);
}
}
public static bool HasAttribute(this XmlAttributeCollection attributes, string attributeName)
{
@@ -108,12 +108,11 @@ namespace Umbraco.Tests.CodeFirst.Definitions
//Check if a PropertyType with the same alias already exists, as we don't want to override existing ones
if(group.PropertyTypes.Contains(definition.Alias)) continue;
var propertyType = new PropertyType(definition.DataTypeDefinition)
var propertyType = new PropertyType(definition.DataTypeDefinition, definition.Alias)
{
Mandatory = definition.Mandatory,
ValidationRegExp = definition.ValidationRegExp,
SortOrder = definition.Order,
Alias = definition.Alias,
Name = definition.Name
};
@@ -17,5 +17,16 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "AutoLegacy");
}
[Test]
public void DisableAlternativeTemplates()
{
Assert.IsTrue(SettingsSection.WebRouting.DisableAlternativeTemplates == false);
}
[Test]
public void DisableFindContentByIdPath()
{
Assert.IsTrue(SettingsSection.WebRouting.DisableFindContentByIdPath == false);
}
}
}
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using umbraco.cms.businesslogic.language;
using umbraco.cms.businesslogic.web;
using Umbraco.Tests.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Web.Routing;
namespace Umbraco.Tests.Integration
{
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture, RequiresSTA]
public class GetCultureTests : BaseServiceTest
{
protected override void FreezeResolution()
{
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
base.FreezeResolution();
}
[Test]
public void GetCulture()
{
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
contentTypeService.Save(contentType);
var contentService = ServiceContext.ContentService;
var c1 = contentService.CreateContentWithIdentity("content", -1, "umbBlah");
var c2 = contentService.CreateContentWithIdentity("content", c1, "umbBlah");
var c3 = contentService.CreateContentWithIdentity("content", c1, "umbBlah");
var c4 = contentService.CreateContentWithIdentity("content", c3, "umbBlah");
var langs = Language.GetAllAsList();
foreach (var l in langs.Skip(1))
l.Delete();
Language.MakeNew("fr-FR");
Language.MakeNew("de-DE");
var langEn = Language.GetByCultureCode("en-US");
var langFr = Language.GetByCultureCode("fr-FR");
var langDe = Language.GetByCultureCode("de-DE");
var domains = Domain.GetDomains(true); // we want wildcards too here
foreach (var d in domains)
d.Delete();
Domain.MakeNew("domain1.com/", c1.Id, langEn.id);
Domain.MakeNew("domain1.fr/", c1.Id, langFr.id);
Domain.MakeNew("*100112", c3.Id, langDe.id);
var content = c2;
var culture = Web.Models.ContentExtensions.GetCulture(null, ServiceContext.LocalizationService, ServiceContext.ContentService, content.Id, content.Path,
new Uri("http://domain1.com/"));
Assert.AreEqual("en-US", culture.Name);
content = c2;
culture = Web.Models.ContentExtensions.GetCulture(null, ServiceContext.LocalizationService, ServiceContext.ContentService, content.Id, content.Path,
new Uri("http://domain1.fr/"));
Assert.AreEqual("fr-FR", culture.Name);
content = c4;
culture = Web.Models.ContentExtensions.GetCulture(null, ServiceContext.LocalizationService, ServiceContext.ContentService, content.Id, content.Path,
new Uri("http://domain1.fr/"));
Assert.AreEqual("de-DE", culture.Name);
}
}
}
@@ -77,7 +77,7 @@ namespace Umbraco.Tests.Manifest
alias: 'Test.Test2',
name: 'Test 2',
config: { key1: 'some default val' },
view: '/hello/world.cshtml',
view: '~/hello/world.cshtml',
icon: 'helloworld'
},
{
@@ -85,7 +85,7 @@ namespace Umbraco.Tests.Manifest
name: 'Test 3',
config: { key1: 'some default val' },
view: '/hello/world.html',
render: '/hello/world.cshtml',
render: '~/hello/world.cshtml',
icon: 'helloworld'
}
]");
@@ -314,6 +314,105 @@ namespace Umbraco.Tests.Manifest
//}
[Test]
public void Create_Manifests_Editors()
{
var package1 = @"{
propertyEditors: [],
javascript: ['~/test.js', '~/test2.js']}";
var package2 = "{css: ['~/style.css', '~/folder-name/sdsdsd/stylesheet.css']}";
var package3 = @"{
'javascript': [ ],
'css': [ ],
'gridEditors': [
{
'name': 'Small Hero',
'alias': 'small-hero',
'view': '/App_Plugins/MyPlugin/small-hero/editortemplate.html',
'render': '/Views/Partials/Grid/Editors/SmallHero.cshtml',
'icon': 'icon-presentation',
'config': {
'image': {
'size': {
'width': 1200,
'height': 185
}
},
'link': {
'maxNumberOfItems': 1,
'minNumberOfItems': 0
}
}
},
{
'name': 'Document Links By Category',
'alias': 'document-links-by-category',
'view': '/App_Plugins/MyPlugin/document-links-by-category/editortemplate.html',
'render': '/Views/Partials/Grid/Editors/DocumentLinksByCategory.cshtml',
'icon': 'icon-umb-members'
}
]
}";
var package4 = @"{'propertyEditors': [
{
alias: 'Test.Test1',
name: 'Test 1',
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
valueType: 'int',
validation: {
'required': true,
'Regex': '\\d*'
}
},
prevalues: {
fields: [
{
label: 'Some config 1',
key: 'key1',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html',
validation: {
required: true
}
},
{
label: 'Some config 2',
key: 'key2',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html'
}
]
}
}
]}";
var package5 = @"{'parameterEditors': [
{
alias: 'parameter1',
name: 'My Parameter',
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html'
},
{
alias: 'parameter2',
name: 'Another parameter',
config: { key1: 'some config val' },
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
}
]}";
var result = ManifestParser.CreateManifests(package1, package2, package3, package4, package5).ToArray();
var paramEditors = result.SelectMany(x => ManifestParser.GetParameterEditors(x.ParameterEditors)).ToArray();
var propEditors = result.SelectMany(x => ManifestParser.GetPropertyEditors(x.PropertyEditors)).ToArray();
var gridEditors = result.SelectMany(x => ManifestParser.GetGridEditors(x.GridEditors)).ToArray();
Assert.AreEqual(2, gridEditors.Count());
Assert.AreEqual(2, paramEditors.Count());
Assert.AreEqual(1, propEditors.Count());
}
[Test]
public void Create_Manifest_With_Line_Comments()
{
@@ -42,14 +42,14 @@ namespace Umbraco.Tests.Migrations.Upgrades
"tags1", "tags1",
new PropertyTypeCollection(new[]
{
new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041},
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags1") {Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041},
}));
var contentTypeWith2Tags = MockedContentTypes.CreateSimpleContentType(
"tags2", "tags2",
new PropertyTypeCollection(new[]
{
new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041},
new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags2", Name = "tags2", SortOrder = 1, DataTypeDefinitionId = 1041}
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags1") {Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041},
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags2") {Name = "tags2", SortOrder = 1, DataTypeDefinitionId = 1041}
}));
contentTypeRepository.AddOrUpdate(contentTypeWith1Tag);
@@ -11,6 +11,27 @@ namespace Umbraco.Tests.Models.Collections
[TestFixture]
public class PropertyCollectionTests : BaseUmbracoConfigurationTest
{
[Test]
public void Property_Adds_Case_Insensitive_Compare()
{
var collection = new PropertyCollection();
collection.Add(new Property(new PropertyType("propEditor", DataTypeDatabaseType.Nvarchar, "test")));
collection.Add(new Property(new PropertyType("propEditor", DataTypeDatabaseType.Nvarchar, "Test")));
Assert.AreEqual(1, collection.Count);
}
[Test]
public void Property_Contains_Case_Insensitive_Compare()
{
var collection = new PropertyCollection();
collection.Add(new Property(new PropertyType("propEditor", DataTypeDatabaseType.Nvarchar, "test")));
Assert.IsTrue(collection.Contains("Test"));
}
[Test]
public void SimpleOrder_Returns_Null_On_FirstOrDefault_When_Empty()
{
+14 -21
View File
@@ -63,8 +63,8 @@ namespace Umbraco.Tests.Models
{
var contentType = MockedContentTypes.CreateSimpleContentType();
//add non-grouped properties
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "nonGrouped1", Name = "Non Grouped 1", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "nonGrouped2", Name = "Non Grouped 2", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext, "nonGrouped1") { Name = "Non Grouped 1", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext, "nonGrouped2") { Name = "Non Grouped 2", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
//ensure that nothing is marked as dirty
contentType.ResetDirtyProperties(false);
@@ -337,7 +337,7 @@ namespace Umbraco.Tests.Models
var asDirty = (ICanBeDirty)clone;
Assert.IsFalse(asDirty.IsPropertyDirty("Properties"));
clone.Properties.Add(new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "blah"}, "blah"));
clone.Properties.Add(new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Ntext, "blah"), "blah"));
Assert.IsTrue(asDirty.IsPropertyDirty("Properties"));
}
@@ -476,9 +476,8 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
contentType.PropertyGroups["Content"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext)
contentType.PropertyGroups["Content"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle",
Name = "Subtitle",
Description = "Optional subtitle",
HelpText = "",
@@ -500,9 +499,9 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "Optional subtitle", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "Optional subtitle", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88
};
contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType);
content.Properties.Add(new Property(propertyType){Value = "This is a subtitle Test"});
@@ -520,9 +519,8 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle",
Name = "Subtitle",
Description = "Optional subtitle",
HelpText = "",
@@ -551,9 +549,9 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act - note that the PropertyType's properties like SortOrder is not updated through the Content object
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "Title description added", HelpText = "", Mandatory = false, SortOrder = 10, DataTypeDefinitionId = -88
Name = "Title", Description = "Title description added", HelpText = "", Mandatory = false, SortOrder = 10, DataTypeDefinitionId = -88
};
content.Properties.Add(new Property(propertyType));
@@ -739,9 +737,8 @@ namespace Umbraco.Tests.Models
contentType.ResetDirtyProperties();
// Act
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle",
Name = "Subtitle",
Description = "Optional subtitle",
HelpText = "",
@@ -766,9 +763,8 @@ namespace Umbraco.Tests.Models
new PropertyTypeCollection(
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
{
Alias = "coauthor",
Name = "Co-Author",
Description = "Name of the Co-Author",
HelpText = "",
@@ -799,9 +795,8 @@ namespace Umbraco.Tests.Models
new PropertyTypeCollection(
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
{
Alias = "coauthor",
Name = "Co-Author",
Description = "Name of the Co-Author",
HelpText = "",
@@ -834,9 +829,8 @@ namespace Umbraco.Tests.Models
var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection(
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "coauthor")
{
Alias = "coauthor",
Name = "Co-Author",
Description = "Name of the Co-Author",
HelpText = "",
@@ -848,9 +842,8 @@ namespace Umbraco.Tests.Models
var mixin2 = MockedContentTypes.CreateSimpleContentType("mixin2", "Mixin2", new PropertyTypeCollection(
new List<PropertyType>
{
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "author")
{
Alias = "author",
Name = "Author",
Description = "Name of the Author",
HelpText = "",
+2 -2
View File
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Models
}
//add a property type without a property group
contentType.PropertyTypeCollection.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title2", Name = "Title2", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
new PropertyType("test", DataTypeDatabaseType.Ntext, "title2") { Name = "Title2", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AllowedTemplates = new[] { new Template("-1,2", "Name", "name") { Id = 200 }, new Template("-1,3", "Name2", "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
@@ -239,7 +239,7 @@ namespace Umbraco.Tests.Models
var asDirty = (ICanBeDirty)clone;
Assert.IsFalse(asDirty.IsPropertyDirty("PropertyTypes"));
clone.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Nvarchar) { Alias = "blah" });
clone.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Nvarchar, "blah"));
Assert.IsTrue(asDirty.IsPropertyDirty("PropertyTypes"));
Assert.IsFalse(asDirty.IsPropertyDirty("PropertyGroups"));
clone.AddPropertyGroup("hello");
@@ -132,8 +132,8 @@ namespace Umbraco.Tests.Models.Mapping
var idSeed = 1;
var contentType = MockedContentTypes.CreateSimpleContentType();
//add non-grouped properties
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "nonGrouped1", Name = "Non Grouped 1", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "nonGrouped2", Name = "Non Grouped 2", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "nonGrouped1") { Name = "Non Grouped 1", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "nonGrouped2") { Name = "Non Grouped 2", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
//set ids or it wont work
contentType.Id = idSeed;
foreach (var p in contentType.PropertyTypes)
@@ -15,10 +15,9 @@ namespace Umbraco.Tests.Models
var pg = new PropertyGroup(
new PropertyTypeCollection(new[]
{
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar)
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
Id = 3,
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
PropertyEditorAlias = "propTest",
@@ -32,10 +31,9 @@ namespace Umbraco.Tests.Models
ValidationRegExp = "xxxx",
DataTypeDatabaseType = DataTypeDatabaseType.Nvarchar
},
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar)
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test2")
{
Id = 4,
Alias = "test2",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 6,
PropertyEditorAlias = "propTest",
@@ -96,10 +94,9 @@ namespace Umbraco.Tests.Models
var pg = new PropertyGroup(
new PropertyTypeCollection(new[]
{
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar)
new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
Id = 3,
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
PropertyEditorAlias = "propTest",
@@ -113,10 +110,9 @@ namespace Umbraco.Tests.Models
ValidationRegExp = "xxxx",
DataTypeDatabaseType = DataTypeDatabaseType.Nvarchar
},
new PropertyType("TestPropertyEditor2", DataTypeDatabaseType.Nvarchar)
new PropertyType("TestPropertyEditor2", DataTypeDatabaseType.Nvarchar, "test2")
{
Id = 4,
Alias = "test2",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 6,
PropertyEditorAlias = "propTest",
@@ -12,10 +12,9 @@ namespace Umbraco.Tests.Models
[Test]
public void Can_Deep_Clone()
{
var pt = new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar)
var pt = new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
Id = 3,
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
PropertyEditorAlias = "propTest",
@@ -62,10 +61,9 @@ namespace Umbraco.Tests.Models
{
var ss = new SerializationService(new JsonNetSerializer());
var pt = new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar)
var pt = new PropertyType("TestPropertyEditor", DataTypeDatabaseType.Nvarchar, "test")
{
Id = 3,
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
PropertyEditorAlias = "propTest",
@@ -176,9 +176,8 @@ namespace Umbraco.Tests.Persistence
}
contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory" + Guid.NewGuid().ToString("N"), "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -135,9 +135,8 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
contentType.Thumbnail = "Doc2.png";
contentType.PropertyGroups["Content"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext)
contentType.PropertyGroups["Content"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle",
Name = "Subtitle",
Description = "Optional Subtitle",
HelpText = "",
@@ -342,9 +341,8 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
// Act
var urlAlias = new PropertyType("test", DataTypeDatabaseType.Nvarchar)
var urlAlias = new PropertyType("test", DataTypeDatabaseType.Nvarchar, "urlAlias")
{
Alias = "urlAlias",
Name = "Url Alias",
Description = "",
HelpText = "",
@@ -445,7 +443,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Act
var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta");
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "metaAuthor") { Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
repository.AddOrUpdate(contentType);
unitOfWork.Commit();
@@ -472,7 +470,7 @@ namespace Umbraco.Tests.Persistence.Repositories
unitOfWork.Commit();
var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta");
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "metaAuthor") { Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
repository.AddOrUpdate(contentType);
unitOfWork.Commit();
@@ -511,7 +509,7 @@ namespace Umbraco.Tests.Persistence.Repositories
contentType.RemovePropertyType("keywords");
//Add PropertyType
var propertyGroup = contentType.PropertyGroups.First(x => x.Name == "Meta");
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "metaAuthor", Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
propertyGroup.PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "metaAuthor") { Name = "Meta Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
repository.AddOrUpdate(contentType);
unitOfWork.Commit();
@@ -81,9 +81,8 @@ namespace Umbraco.Tests.Persistence.Repositories
var mediaType = repository.Get(NodeDto.NodeIdSeed);
mediaType.Thumbnail = "Doc2.png";
mediaType.PropertyGroups["Media"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext)
mediaType.PropertyGroups["Media"].PropertyTypes.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle",
Name = "Subtitle",
Description = "Optional Subtitle",
HelpText = "",
@@ -78,9 +78,8 @@ namespace Umbraco.Tests.PublishedContent
var mType = MockedContentTypes.CreateImageMediaType("image2");
//lets add an RTE to this
mType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Nvarchar)
new PropertyType("test", DataTypeDatabaseType.Nvarchar, "content")
{
Alias = "content",
Name = "Rich Text",
DataTypeDefinitionId = -87 //tiny mce
});
@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Routing;
using umbraco.cms.businesslogic.web;
using umbraco.cms.businesslogic.language;
using Language = umbraco.cms.businesslogic.language.Language;
namespace Umbraco.Tests.Routing
{
@@ -14,6 +16,13 @@ namespace Umbraco.Tests.Routing
[TestFixture]
class DomainsAndCulturesTests : BaseRoutingTest
{
protected override void FreezeResolution()
{
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
base.FreezeResolution();
}
public override void Initialize()
{
base.Initialize();
@@ -235,5 +244,32 @@ namespace Umbraco.Tests.Routing
Assert.AreEqual(expectedCulture, pcr.Culture.Name);
Assert.AreEqual(pcr.PublishedContent.Id, expectedNode);
}
#region Cases
[TestCase(10011, "http://domain1.com/", "en-US")]
[TestCase(100111, "http://domain1.com/", "en-US")]
[TestCase(10011, "http://domain1.fr/", "fr-FR")]
[TestCase(100111, "http://domain1.fr/", "fr-FR")]
[TestCase(1001121, "http://domain1.fr/", "de-DE")]
#endregion
public void GetCulture(int nodeId, string currentUrl, string expectedCulture)
{
var langEn = Language.GetByCultureCode("en-US");
var langFr = Language.GetByCultureCode("fr-FR");
var langDe = Language.GetByCultureCode("de-DE");
Domain.MakeNew("domain1.com/", 1001, langEn.id);
Domain.MakeNew("domain1.fr/", 1001, langFr.id);
Domain.MakeNew("*100112", 100112, langDe.id);
var routingContext = GetRoutingContext("http://anything/");
var umbracoContext = routingContext.UmbracoContext;
var content = umbracoContext.ContentCache.GetById(nodeId);
Assert.IsNotNull(content);
var culture = Web.Models.ContentExtensions.GetCulture(umbracoContext, null, null, content.Id, content.Path, new Uri(currentUrl));
Assert.AreEqual(expectedCulture, culture.Name);
}
}
}
@@ -1,85 +1,286 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Scheduling;
namespace Umbraco.Tests.Scheduling
{
[TestFixture]
[Timeout(30000)]
public class BackgroundTaskRunnerTests
{
[Test]
public void Startup_And_Shutdown()
[TestFixtureSetUp]
public void InitializeFixture()
{
BackgroundTaskRunner<IBackgroundTask> tManager;
using (tManager = new BackgroundTaskRunner<IBackgroundTask>(true, true))
{
tManager.StartUp();
}
NUnit.Framework.Assert.IsFalse(tManager.IsRunning);
TestHelper.SetupLog4NetForTests();
}
[Test]
public void Startup_Starts_Automatically()
public async void ShutdownWaitWhenRunning()
{
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(true, true))
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }))
{
tManager.Add(new MyTask());
NUnit.Framework.Assert.IsTrue(tManager.IsRunning);
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(800); // for long
Assert.IsTrue(runner.IsRunning);
runner.Shutdown(false, true); // -force +wait
await runner; // wait for the entire runner operation to complete
Assert.IsTrue(runner.IsCompleted);
}
}
[Test]
public void Task_Runs()
public async void ShutdownWhenRunning()
{
var myTask = new MyTask();
var waitHandle = new ManualResetEvent(false);
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(true, true))
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
tManager.TaskCompleted += (sender, task) => waitHandle.Set();
Console.WriteLine("Begin {0}", DateTime.Now);
tManager.Add(myTask);
runner.TaskStarting += (sender, args) => Console.WriteLine("starting {0}", DateTime.Now);
runner.TaskCompleted += (sender, args) => Console.WriteLine("completed {0}", DateTime.Now);
//wait for ITasks to complete
Assert.IsFalse(runner.IsRunning);
Console.WriteLine("Adding task {0}", DateTime.Now);
runner.Add(new MyTask(5000));
Assert.IsTrue(runner.IsRunning); // is running the task
Console.WriteLine("Shutting down {0}", DateTime.Now);
runner.Shutdown(false, false); // -force -wait
Assert.IsTrue(runner.IsCompleted);
Assert.IsTrue(runner.IsRunning); // still running that task
Thread.Sleep(3000); // wait slightly less than the task takes to complete
Assert.IsTrue(runner.IsRunning); // still running that task
await runner; // wait for the entire runner operation to complete
Console.WriteLine("End {0}", DateTime.Now);
}
}
[Test]
public async void ShutdownFlushesTheQueue()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
var t = new MyTask();
runner.Add(t);
Assert.IsTrue(runner.IsRunning); // is running the first task
runner.Shutdown(false, false); // -force -wait
await runner; // wait for the entire runner operation to complete
Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run
}
}
[Test]
public async void ShutdownForceTruncatesTheQueue()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
var t = new MyTask();
runner.Add(t);
Assert.IsTrue(runner.IsRunning); // is running the first task
runner.Shutdown(true, false); // +force -wait
await runner; // wait for the entire runner operation to complete
Assert.AreEqual(DateTime.MinValue, t.Ended); // t has not run
}
}
[Test]
public async void ShutdownThenForce()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
runner.Add(new MyTask());
Assert.IsTrue(runner.IsRunning); // is running the task
runner.Shutdown(false, false); // -force -wait
Assert.IsTrue(runner.IsCompleted);
Assert.IsTrue(runner.IsRunning); // still running that task
Thread.Sleep(3000);
Assert.IsTrue(runner.IsRunning); // still running that task
runner.Shutdown(true, false); // +force -wait
await runner; // wait for the entire runner operation to complete
}
}
[Test]
public void Create_IsRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
}
}
[Ignore]
[Test]
public async void Create_AutoStart_IsRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true }))
{
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
}
}
[Test]
public void Create_AutoStartAndKeepAlive_IsRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }))
{
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(800); // for long
Assert.IsTrue(runner.IsRunning);
// dispose will stop it
}
}
[Test]
public async void Dispose_IsRunning()
{
BackgroundTaskRunner<IBackgroundTask> runner;
using (runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }))
{
Assert.IsTrue(runner.IsRunning);
// dispose will stop it
}
await runner; // wait for the entire runner operation to complete
Assert.Throws<InvalidOperationException>(() => runner.Add(new MyTask()));
}
[Test]
public void Startup_KeepAlive_IsRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { KeepAlive = true }))
{
Assert.IsFalse(runner.IsRunning);
runner.StartUp();
Assert.IsTrue(runner.IsRunning);
// dispose will stop it
}
}
[Test]
public async void Create_AddTask_IsRunning()
{
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions()))
{
var waitHandle = new ManualResetEvent(false);
runner.TaskCompleted += (sender, args) =>
{
waitHandle.Set();
};
runner.Add(new MyTask());
Assert.IsTrue(runner.IsRunning);
waitHandle.WaitOne();
NUnit.Framework.Assert.IsTrue(myTask.Ended != default(DateTime));
await runner; //since we are not being kept alive, it will quit
Assert.IsFalse(runner.IsRunning);
}
}
[Test]
public void Many_Tasks_Run()
public void Create_KeepAliveAndAddTask_IsRunning()
{
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions { KeepAlive = true }))
{
var waitHandle = new ManualResetEvent(false);
runner.TaskCompleted += (sender, args) =>
{
Assert.IsTrue(sender.IsRunning);
waitHandle.Set();
};
runner.Add(new MyTask());
waitHandle.WaitOne();
Thread.Sleep(1000); // we are waiting a second just to prove that it's still running and hasn't been shut off
Assert.IsTrue(runner.IsRunning);
}
}
[Test]
public async void WaitOnRunner_OneTask()
{
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyTask();
Assert.IsTrue(task.Ended == default(DateTime));
runner.Add(task);
await runner.CurrentThreadingTask; // wait for the Task operation to complete
Assert.IsTrue(task.Ended != default(DateTime)); // task is done
await runner; // wait for the entire runner operation to complete
}
}
[Ignore]
[Test]
public async void WaitOnRunner_Tasks()
{
var tasks = new List<BaseTask>();
for (var i = 0; i < 10; i++)
tasks.Add(new MyTask());
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions { KeepAlive = false, LongRunning = true, PreserveRunningTask = true }))
{
tasks.ForEach(runner.Add);
await runner; // wait for the entire runner operation to complete
// check that tasks are done
Assert.IsTrue(tasks.All(x => x.Ended != default(DateTime)));
Assert.AreEqual(TaskStatus.RanToCompletion, runner.CurrentThreadingTask.Status);
Assert.IsFalse(runner.IsRunning);
Assert.IsFalse(runner.IsDisposed);
}
}
[Test]
public async void WaitOnTask()
{
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyTask();
var waitHandle = new ManualResetEvent(false);
runner.TaskCompleted += (sender, t) => waitHandle.Set();
Assert.IsTrue(task.Ended == default(DateTime));
runner.Add(task);
waitHandle.WaitOne(); // wait 'til task is done
Assert.IsTrue(task.Ended != default(DateTime)); // task is done
await runner; // wait for the entire runner operation to complete
}
}
[Test]
public async void WaitOnTasks()
{
var tasks = new Dictionary<BaseTask, ManualResetEvent>();
for (var i = 0; i < 10; i++)
{
tasks.Add(new MyTask(), new ManualResetEvent(false));
}
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(true, true))
using (var runner = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions()))
{
tManager.TaskCompleted += (sender, task) => tasks[task.Task].Set();
runner.TaskCompleted += (sender, task) => tasks[task.Task].Set();
foreach (var t in tasks) runner.Add(t.Key);
tasks.ForEach(t => tManager.Add(t.Key));
//wait for all ITasks to complete
// wait 'til tasks are done, check that tasks are done
WaitHandle.WaitAll(tasks.Values.Select(x => (WaitHandle)x).ToArray());
Assert.IsTrue(tasks.All(x => x.Key.Ended != default(DateTime)));
foreach (var task in tasks)
{
NUnit.Framework.Assert.IsTrue(task.Key.Ended != default(DateTime));
}
await runner; // wait for the entire runner operation to complete
}
}
@@ -99,7 +300,7 @@ namespace Umbraco.Tests.Scheduling
IDictionary<BaseTask, ManualResetEvent> tasks = getTasks();
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(true, true))
using (tManager = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions { LongRunning = true, KeepAlive = true }))
{
tManager.TaskCompleted += (sender, task) => tasks[task.Task].Set();
@@ -111,7 +312,7 @@ namespace Umbraco.Tests.Scheduling
foreach (var task in tasks)
{
NUnit.Framework.Assert.IsTrue(task.Key.Ended != default(DateTime));
Assert.IsTrue(task.Key.Ended != default(DateTime));
}
//execute another batch after a bit
@@ -125,71 +326,11 @@ namespace Umbraco.Tests.Scheduling
foreach (var task in tasks)
{
NUnit.Framework.Assert.IsTrue(task.Key.Ended != default(DateTime));
Assert.IsTrue(task.Key.Ended != default(DateTime));
}
}
}
[Test]
public void Task_Queue_Will_Be_Completed_Before_Shutdown()
{
var tasks = new Dictionary<BaseTask, ManualResetEvent>();
for (var i = 0; i < 10; i++)
{
tasks.Add(new MyTask(), new ManualResetEvent(false));
}
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(true, true))
{
tManager.TaskCompleted += (sender, task) => tasks[task.Task].Set();
tasks.ForEach(t => tManager.Add(t.Key));
////wait for all ITasks to complete
//WaitHandle.WaitAll(tasks.Values.Select(x => (WaitHandle)x).ToArray());
tManager.Stop(false);
//immediate stop will block until complete - but since we are running on
// a single thread this doesn't really matter as the above will just process
// until complete.
tManager.Stop(true);
NUnit.Framework.Assert.AreEqual(0, tManager.TaskCount);
}
}
//NOTE: These tests work in .Net 4.5 but in this current version we don't have the correct
// async/await signatures with GetAwaiter, so am just commenting these out in this version
[Test]
public async void Non_Persistent_Runner_Will_End_After_Queue_Empty()
{
var tasks = new List<BaseTask>();
for (var i = 0; i < 10; i++)
{
tasks.Add(new MyTask());
}
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(persistentThread: false, dedicatedThread:true))
{
tasks.ForEach(t => tManager.Add(t));
//wait till the thread is done
await tManager;
foreach (var task in tasks)
{
Assert.IsTrue(task.Ended != default(DateTime));
}
Assert.AreEqual(TaskStatus.RanToCompletion, tManager.TaskStatus);
Assert.IsFalse(tManager.IsRunning);
Assert.IsFalse(tManager.IsDisposed);
}
}
[Test]
public async void Non_Persistent_Runner_Will_Start_New_Threads_When_Required()
{
@@ -205,15 +346,14 @@ namespace Umbraco.Tests.Scheduling
List<BaseTask> tasks = getTasks();
BackgroundTaskRunner<BaseTask> tManager;
using (tManager = new BackgroundTaskRunner<BaseTask>(persistentThread: false, dedicatedThread: true))
using (var tManager = new BackgroundTaskRunner<BaseTask>(new BackgroundTaskRunnerOptions { LongRunning = true, PreserveRunningTask = true }))
{
tasks.ForEach(t => tManager.Add(t));
tasks.ForEach(tManager.Add);
//wait till the thread is done
await tManager;
await tManager.CurrentThreadingTask;
Assert.AreEqual(TaskStatus.RanToCompletion, tManager.TaskStatus);
Assert.AreEqual(TaskStatus.RanToCompletion, tManager.CurrentThreadingTask.Status);
Assert.IsFalse(tManager.IsRunning);
Assert.IsFalse(tManager.IsDisposed);
@@ -226,56 +366,366 @@ namespace Umbraco.Tests.Scheduling
tasks = getTasks();
//add more tasks
tasks.ForEach(t => tManager.Add(t));
tasks.ForEach(tManager.Add);
//wait till the thread is done
await tManager;
await tManager.CurrentThreadingTask;
foreach (var task in tasks)
{
Assert.IsTrue(task.Ended != default(DateTime));
}
Assert.AreEqual(TaskStatus.RanToCompletion, tManager.TaskStatus);
Assert.AreEqual(TaskStatus.RanToCompletion, tManager.CurrentThreadingTask.Status);
Assert.IsFalse(tManager.IsRunning);
Assert.IsFalse(tManager.IsDisposed);
}
}
[Ignore]
[Test]
public void RecurringTaskTest()
{
var runCount = 0;
var waitHandle = new ManualResetEvent(false);
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
runner.TaskCompleted += (sender, args) => runCount++;
runner.TaskStarting += async (sender, args) =>
{
//wait for each task to finish once it's started
await sender.CurrentThreadingTask;
if (runCount > 3)
{
waitHandle.Set();
}
};
var task = new MyRecurringTask(runner, 200, 500);
runner.Add(task);
Assert.IsTrue(runner.IsRunning); // waiting on delay
Assert.AreEqual(0, runCount);
waitHandle.WaitOne();
Assert.GreaterOrEqual(runCount, 4);
// stops recurring
runner.Shutdown(false, false);
}
}
[Test]
public async void DelayedTaskRuns()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyDelayedTask(200);
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(5000);
Assert.IsTrue(runner.IsRunning); // still waiting for the task to release
Assert.IsFalse(task.HasRun);
task.Release();
await runner.CurrentThreadingTask; //wait for current task to complete
Assert.IsTrue(task.HasRun);
await runner; // wait for the entire runner operation to complete
}
}
[Test]
public async void DelayedTaskStops()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyDelayedTask(200);
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(5000);
Assert.IsTrue(runner.IsRunning); // still waiting for the task to release
Assert.IsFalse(task.HasRun);
runner.Shutdown(false, false);
await runner; // wait for the entire runner operation to complete
Assert.IsTrue(task.HasRun);
}
}
[Ignore]
[Test]
public void DelayedRecurring()
{
var runCount = 0;
var waitHandle = new ManualResetEvent(false);
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
runner.TaskCompleted += (sender, args) => runCount++;
runner.TaskStarting += async (sender, args) =>
{
//wait for each task to finish once it's started
await sender.CurrentThreadingTask;
if (runCount > 3)
{
waitHandle.Set();
}
};
var task = new MyDelayedRecurringTask(runner, 2000, 1000);
runner.Add(task);
Assert.IsTrue(runner.IsRunning); // waiting on delay
Assert.AreEqual(0, runCount);
waitHandle.WaitOne();
Assert.GreaterOrEqual(runCount, 4);
// stops recurring
runner.Shutdown(false, false);
}
}
[Test]
public async void FailingTaskSync()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(false); // -async
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
}
[Test]
public async void FailingTaskAsync()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(true); // +async
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
}
private class MyFailingTask : IBackgroundTask
{
private readonly bool _isAsync;
public MyFailingTask(bool isAsync)
{
_isAsync = isAsync;
}
public void Run()
{
Thread.Sleep(1000);
throw new Exception("Task has thrown.");
}
public async Task RunAsync(CancellationToken token)
{
await Task.Delay(1000);
throw new Exception("Task has thrown.");
}
public bool IsAsync
{
get { return _isAsync; }
}
// fixme - must also test what happens if we throw on dispose!
public void Dispose()
{ }
}
private class MyDelayedRecurringTask : DelayedRecurringTaskBase<MyDelayedRecurringTask>
{
public MyDelayedRecurringTask(IBackgroundTaskRunner<MyDelayedRecurringTask> runner, int delayMilliseconds, int periodMilliseconds)
: base(runner, delayMilliseconds, periodMilliseconds)
{ }
private MyDelayedRecurringTask(MyDelayedRecurringTask source)
: base(source)
{ }
public override bool IsAsync
{
get { return false; }
}
public override void PerformRun()
{
// nothing to do at the moment
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
}
protected override MyDelayedRecurringTask GetRecurring()
{
return new MyDelayedRecurringTask(this);
}
}
private class MyDelayedTask : ILatchedBackgroundTask
{
private readonly int _runMilliseconds;
private readonly ManualResetEvent _gate;
public bool HasRun { get; private set; }
public MyDelayedTask(int runMilliseconds)
{
_runMilliseconds = runMilliseconds;
_gate = new ManualResetEvent(false);
}
public WaitHandle Latch
{
get { return _gate; }
}
public bool IsLatched
{
get { return true; }
}
public bool RunsOnShutdown
{
get { return true; }
}
public void Run()
{
Thread.Sleep(_runMilliseconds);
HasRun = true;
}
public void Release()
{
_gate.Set();
}
public Task RunAsync(CancellationToken token)
{
throw new NotImplementedException();
}
public bool IsAsync
{
get { return false; }
}
public void Dispose()
{ }
}
private class MyRecurringTask : RecurringTaskBase<MyRecurringTask>
{
private readonly int _runMilliseconds;
public MyRecurringTask(IBackgroundTaskRunner<MyRecurringTask> runner, int runMilliseconds, int periodMilliseconds)
: base(runner, periodMilliseconds)
{
_runMilliseconds = runMilliseconds;
}
private MyRecurringTask(MyRecurringTask source, int runMilliseconds)
: base(source)
{
_runMilliseconds = runMilliseconds;
}
public override void PerformRun()
{
Thread.Sleep(_runMilliseconds);
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
}
public override bool IsAsync
{
get { return false; }
}
protected override MyRecurringTask GetRecurring()
{
return new MyRecurringTask(this, _runMilliseconds);
}
}
private class MyTask : BaseTask
{
private readonly int _milliseconds;
public MyTask()
: this(500)
{ }
public MyTask(int milliseconds)
{
_milliseconds = milliseconds;
}
public override void Run()
public override void PerformRun()
{
Thread.Sleep(500);
}
public override void Cancel()
{
Thread.Sleep(_milliseconds);
}
}
public abstract class BaseTask : IBackgroundTask
{
public bool WasCancelled { get; set; }
public Guid UniqueId { get; protected set; }
public abstract void Run();
public abstract void Cancel();
public abstract void PerformRun();
public void Run()
{
PerformRun();
Ended = DateTime.Now;
}
public Task RunAsync(CancellationToken token)
{
throw new NotImplementedException();
//return Task.Delay(500);
}
public bool IsAsync
{
get { return false; }
}
public virtual void Cancel()
{
WasCancelled = true;
}
public DateTime Queued { get; set; }
public DateTime Started { get; set; }
public DateTime Ended { get; set; }
public virtual void Dispose()
{
Ended = DateTime.Now;
}
{ }
}
}
}
@@ -123,9 +123,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -160,9 +159,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -194,9 +192,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -228,9 +225,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -265,9 +261,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -310,9 +305,8 @@ namespace Umbraco.Tests.Services
});
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -357,9 +351,8 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -389,9 +382,8 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -419,9 +411,8 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -449,9 +440,8 @@ namespace Umbraco.Tests.Services
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
@@ -173,10 +173,9 @@ namespace Umbraco.Tests.Services
var cts = ServiceContext.ContentTypeService;
var dtdYesNo = ServiceContext.DataTypeService.GetDataTypeDefinitionById(-49);
var ctBase = new ContentType(-1) { Name = "Base", Alias = "Base", Icon = "folder.gif", Thumbnail = "folder.png" };
ctBase.AddPropertyType(new PropertyType(dtdYesNo)
ctBase.AddPropertyType(new PropertyType(dtdYesNo, Constants.Conventions.Content.NaviHide)
{
Name = "Hide From Navigation",
Alias = Constants.Conventions.Content.NaviHide
}
/*,"Navigation"*/);
cts.Save(ctBase);
@@ -190,7 +189,7 @@ namespace Umbraco.Tests.Services
Thumbnail = "folder.png",
AllowedAsRoot = true
};
ctHomePage.AddPropertyType(new PropertyType(dtdYesNo) { Name = "Some property", Alias = "someProperty" }
ctHomePage.AddPropertyType(new PropertyType(dtdYesNo, "someProperty") { Name = "Some property" }
/*,"Navigation"*/);
cts.Save(ctHomePage);
@@ -222,9 +221,9 @@ namespace Umbraco.Tests.Services
Trashed = false
};
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, DataTypeDefinitionId = -87 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = false, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title") { Name = "Title", Description = "", Mandatory = false, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext, "bodyText") { Name = "Body Text", Description = "", Mandatory = false, DataTypeDefinitionId = -87 });
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author") { Name = "Author", Description = "Name of the author", Mandatory = false, DataTypeDefinitionId = -88 });
service.Save(contentType);
@@ -488,9 +487,9 @@ namespace Umbraco.Tests.Services
service.Save(child);
// Act
var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var added = composition.AddPropertyType(duplicatePropertyType, "Meta");
@@ -524,9 +523,9 @@ namespace Umbraco.Tests.Services
service.Save(advancedPage);
// Act
var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var duplicatePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var addedToBasePage = basePage.AddPropertyType(duplicatePropertyType, "Content");
var addedToAdvancedPage = advancedPage.AddPropertyType(duplicatePropertyType, "Content");
@@ -578,16 +577,16 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
service.Save(basePage);
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
service.Save(contentPage);
@@ -596,9 +595,9 @@ namespace Umbraco.Tests.Services
service.Save(advancedPage);
//NOTE: It should not be possible to Save 'BasePage' with the Title PropertyType added
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var titleAdded = basePage.AddPropertyType(titlePropertyType, "Content");
@@ -643,30 +642,30 @@ namespace Umbraco.Tests.Services
service.Save(metaComposition);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
service.Save(basePage);
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
service.Save(contentPage);
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitleAdded = advancedPage.AddPropertyType(subtitlePropertyType, "Content");
service.Save(advancedPage);
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var titleAdded = seoComposition.AddPropertyType(titlePropertyType, "Content");
service.Save(seoComposition);
@@ -723,30 +722,30 @@ namespace Umbraco.Tests.Services
service.Save(metaComposition);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
service.Save(basePage);
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
service.Save(contentPage);
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitleAdded = advancedPage.AddPropertyType(subtitlePropertyType, "Content");
service.Save(advancedPage);
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var titleAdded = seoComposition.AddPropertyType(titlePropertyType, "Content");
service.Save(seoComposition);
@@ -763,10 +762,10 @@ namespace Umbraco.Tests.Services
Assert.That(titleAdded, Is.True);
Assert.That(seoCompositionAdded, Is.True);
Assert.That(metaCompositionAdded, Is.True);
var testPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var testPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "test")
{
Alias = "test", Name = "Test", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Test", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var testAdded = seoComposition.AddPropertyType(testPropertyType, "Content");
service.Save(seoComposition);
@@ -794,13 +793,13 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
@@ -840,24 +839,24 @@ namespace Umbraco.Tests.Services
service.Save(advancedPage);
// Act
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var titleAdded = basePage.AddPropertyType(titlePropertyType, "Content");
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = contentPage.AddPropertyType(bodyTextPropertyType, "Content");
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = advancedPage.AddPropertyType(authorPropertyType, "Content");
service.Save(basePage);
@@ -913,9 +912,9 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var propertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title")
{
Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var addedToContentPage = contentPage.AddPropertyType(propertyType, "Content");
@@ -947,14 +946,14 @@ namespace Umbraco.Tests.Services
service.Save(contentPage);
// Act
var propertyTypeOne = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var propertyTypeOne = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "testTextbox")
{
Alias = "testTextbox", Name = "Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var firstOneAdded = contentPage.AddPropertyType(propertyTypeOne, "Content_");
var propertyTypeTwo = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var propertyTypeTwo = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "anotherTextbox")
{
Alias = "anotherTextbox", Name = "Another Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Another Test Textbox", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var secondOneAdded = contentPage.AddPropertyType(propertyTypeTwo, "Content");
service.Save(contentPage);
@@ -998,29 +997,29 @@ namespace Umbraco.Tests.Services
service.Save(contentPage);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = contentPage.AddPropertyType(bodyTextPropertyType, "Content_");//Will be added to the parent tab
var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");//Will be added to the "Content Meta" composition
service.Save(contentPage);
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var descriptionPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var descriptionPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "description")
{
Alias = "description", Name = "Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var keywordsPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var keywordsPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "keywords")
{
Alias = "keywords", Name = "Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = advancedPage.AddPropertyType(authorPropertyType, "Content_");//Will be added to an ancestor tab
var descriptionAdded = advancedPage.AddPropertyType(descriptionPropertyType, "Contentx");//Will be added to a parent tab
@@ -1065,17 +1064,17 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var subtitlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "subtitle")
{
Alias = "subtitle", Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Subtitle", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = page.AddPropertyType(bodyTextPropertyType, "Content_");
var subtitleAdded = contentPage.AddPropertyType(subtitlePropertyType, "Content");
@@ -1118,16 +1117,16 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
service.Save(basePage);
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
service.Save(contentPage);
@@ -1176,16 +1175,16 @@ namespace Umbraco.Tests.Services
service.Save(contentMetaComposition);
// Act
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var authorPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "author")
{
Alias = "author", Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Author", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var authorAdded = contentPage.AddPropertyType(authorPropertyType, "Content");
service.Save(contentPage);
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
var bodyTextPropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "bodyText")
{
Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
};
var bodyTextAdded = basePage.AddPropertyType(bodyTextPropertyType, "Content");
service.Save(basePage);
@@ -1243,7 +1242,7 @@ namespace Umbraco.Tests.Services
};
var contentCollection = new PropertyTypeCollection();
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "componentGroup", Name = "Component Group", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "componentGroup") { Name = "Component Group", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
component.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Component", SortOrder = 1 });
return component;
@@ -1264,9 +1263,8 @@ namespace Umbraco.Tests.Services
Trashed = false
};
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext)
var propertyType = new PropertyType("test", DataTypeDatabaseType.Ntext, "bannerName")
{
Alias = "bannerName",
Name = "Banner Name",
Description = "",
Mandatory = false,
@@ -1292,7 +1290,7 @@ namespace Umbraco.Tests.Services
};
var contentCollection = new PropertyTypeCollection();
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "hostname", Name = "Hostname", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "hostname") { Name = "Hostname", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
site.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Site Settings", SortOrder = 1 });
return site;
@@ -1314,9 +1312,9 @@ namespace Umbraco.Tests.Services
};
var contentCollection = new PropertyTypeCollection();
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "title") { Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "bodyText") { Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType("test", DataTypeDatabaseType.Ntext, "author") { Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
@@ -676,9 +676,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Int_Value_Exact()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "number")
{
Alias = "number",
Name = "Number",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -51
@@ -701,9 +700,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Int_Value_Greater_Than()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "number")
{
Alias = "number",
Name = "Number",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -51
@@ -726,9 +724,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Int_Value_Greater_Than_Equal_To()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "number")
{
Alias = "number",
Name = "Number",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -51
@@ -751,9 +748,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Int_Value_Less_Than()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.DateAlias, DataTypeDatabaseType.Date)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.DateAlias, DataTypeDatabaseType.Date, "number")
{
Alias = "number",
Name = "Number",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -51
@@ -776,9 +772,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Int_Value_Less_Than_Or_Equal()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "number")
{
Alias = "number",
Name = "Number",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -51
@@ -801,9 +796,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Date_Value_Exact()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "date")
{
Alias = "date",
Name = "Date",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -36
@@ -826,9 +820,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Date_Value_Greater_Than()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "date")
{
Alias = "date",
Name = "Date",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -36
@@ -851,9 +844,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Date_Value_Greater_Than_Equal_To()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "date")
{
Alias = "date",
Name = "Date",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -36
@@ -876,9 +868,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Date_Value_Less_Than()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "date")
{
Alias = "date",
Name = "Date",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -36
@@ -901,9 +892,8 @@ namespace Umbraco.Tests.Services
public void Get_By_Property_Date_Value_Less_Than_Or_Equal()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer)
memberType.AddPropertyType(new PropertyType(Constants.PropertyEditors.IntegerAlias, DataTypeDatabaseType.Integer, "date")
{
Alias = "date",
Name = "Date",
//NOTE: This is what really determines the db type - the above definition doesn't really do anything
DataTypeDefinitionId = -36
@@ -36,9 +36,8 @@ namespace Umbraco.Tests.Services
var tagService = ServiceContext.TagService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType("test", DataTypeDatabaseType.Ntext)
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
+3 -11
View File
@@ -58,9 +58,9 @@
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.61.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.62.2941, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.61.2941\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.62.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -170,6 +170,7 @@
<Compile Include="Cache\CacheRefresherTests.cs" />
<Compile Include="CoreStrings\StringValidationTests.cs" />
<Compile Include="FrontEnd\UmbracoHelperTests.cs" />
<Compile Include="Integration\GetCultureTests.cs" />
<Compile Include="Membership\DynamicMemberContentTests.cs" />
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />
@@ -509,7 +510,6 @@
<Compile Include="UmbracoExamine\ExamineBaseTest.cs" />
<Compile Include="UmbracoExamine\IndexInitializer.cs" />
<Compile Include="UmbracoExamine\IndexTest.cs" />
<Compile Include="UmbracoExamine\PdfIndexerTests.cs" />
<Compile Include="UmbracoExamine\SearchTests.cs" />
<Compile Include="UmbracoExamine\TestDataService.cs" />
<Compile Include="UmbracoExamine\TestFiles.Designer.cs">
@@ -568,14 +568,10 @@
<EmbeddedResource Include="TreesAndSections\applications.config" />
<EmbeddedResource Include="TreesAndSections\trees.config" />
<None Include="Packaging\Packages\Document_Type_Picker_1.1.umb" />
<None Include="UmbracoExamine\TestFiles\Converting_file_to_PDF.pdf" />
<None Include="UmbracoExamine\TestFiles\PDFStandards.PDF" />
<None Include="UmbracoExamine\TestFiles\SurviorFlipCup.pdf" />
<None Include="UmbracoExamine\TestFiles\umbraco-sort.config">
<SubType>Designer</SubType>
</None>
<None Include="UmbracoExamine\TestFiles\umbraco.config" />
<None Include="UmbracoExamine\TestFiles\windows-vista.pdf" />
<None Include="unit-test-log4net.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -625,10 +621,6 @@
<Project>{651E1350-91B6-44B7-BD60-7207006D7003}</Project>
<Name>Umbraco.Web</Name>
</ProjectReference>
<ProjectReference Include="..\UmbracoExamine.PDF\UmbracoExamine.PDF.csproj">
<Project>{f30dddb8-3994-4673-82ae-057123c6e1a8}</Project>
<Name>UmbracoExamine.PDF</Name>
</ProjectReference>
<ProjectReference Include="..\UmbracoExamine\UmbracoExamine.csproj">
<Project>{07fbc26b-2927-4a22-8d96-d644c667fecc}</Project>
<Name>UmbracoExamine</Name>
@@ -1,12 +1,11 @@
using System;
using System.Linq;
using System.Net.Mime;
using System.Xml.XPath;
using Examine;
using Examine.LuceneEngine.Config;
using Examine.LuceneEngine.Providers;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Store;
using Moq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -15,9 +14,9 @@ using Umbraco.Core.Services;
using UmbracoExamine;
using UmbracoExamine.Config;
using UmbracoExamine.DataServices;
using UmbracoExamine.PDF;
using IContentService = UmbracoExamine.DataServices.IContentService;
using IMediaService = UmbracoExamine.DataServices.IMediaService;
using IContentService = Umbraco.Core.Services.IContentService;
using IMediaService = Umbraco.Core.Services.IMediaService;
using Version = Lucene.Net.Util.Version;
namespace Umbraco.Tests.UmbracoExamine
{
@@ -27,11 +26,11 @@ namespace Umbraco.Tests.UmbracoExamine
internal static class IndexInitializer
{
public static UmbracoContentIndexer GetUmbracoIndexer(
Lucene.Net.Store.Directory luceneDir,
Directory luceneDir,
Analyzer analyzer = null,
IDataService dataService = null,
Umbraco.Core.Services.IContentService contentService = null,
Umbraco.Core.Services.IMediaService mediaService = null,
IContentService contentService = null,
IMediaService mediaService = null,
IDataTypeService dataTypeService = null,
IMemberService memberService = null,
IUserService userService = null)
@@ -42,7 +41,7 @@ namespace Umbraco.Tests.UmbracoExamine
}
if (contentService == null)
{
contentService = Mock.Of<Umbraco.Core.Services.IContentService>();
contentService = Mock.Of<IContentService>();
}
if (userService == null)
{
@@ -73,7 +72,7 @@ namespace Umbraco.Tests.UmbracoExamine
.ToArray();
mediaService = Mock.Of<Umbraco.Core.Services.IMediaService>(
mediaService = Mock.Of<IMediaService>(
x => x.GetPagedDescendants(
It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), out totalRecs, It.IsAny<string>(), It.IsAny<Direction>(), It.IsAny<string>())
==
@@ -91,7 +90,7 @@ namespace Umbraco.Tests.UmbracoExamine
if (analyzer == null)
{
analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29);
analyzer = new StandardAnalyzer(Version.LUCENE_29);
}
var indexSet = new IndexSet();
@@ -113,33 +112,23 @@ namespace Umbraco.Tests.UmbracoExamine
return i;
}
public static UmbracoExamineSearcher GetUmbracoSearcher(Lucene.Net.Store.Directory luceneDir, Analyzer analyzer = null)
public static UmbracoExamineSearcher GetUmbracoSearcher(Directory luceneDir, Analyzer analyzer = null)
{
if (analyzer == null)
{
analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29);
analyzer = new StandardAnalyzer(Version.LUCENE_29);
}
return new UmbracoExamineSearcher(luceneDir, analyzer);
}
public static LuceneSearcher GetLuceneSearcher(Lucene.Net.Store.Directory luceneDir)
public static LuceneSearcher GetLuceneSearcher(Directory luceneDir)
{
return new LuceneSearcher(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29));
return new LuceneSearcher(luceneDir, new StandardAnalyzer(Version.LUCENE_29));
}
public static PDFIndexer GetPdfIndexer(Lucene.Net.Store.Directory luceneDir)
public static MultiIndexSearcher GetMultiSearcher(Directory pdfDir, Directory simpleDir, Directory conventionDir, Directory cwsDir)
{
var i = new PDFIndexer(luceneDir,
new TestDataService(),
new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29),
false);
i.IndexingError += IndexingError;
return i;
}
public static MultiIndexSearcher GetMultiSearcher(Lucene.Net.Store.Directory pdfDir, Lucene.Net.Store.Directory simpleDir, Lucene.Net.Store.Directory conventionDir, Lucene.Net.Store.Directory cwsDir)
{
var i = new MultiIndexSearcher(new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29));
var i = new MultiIndexSearcher(new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Version.LUCENE_29));
return i;
}
@@ -1,116 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Examine;
using Lucene.Net.Search;
using Lucene.Net.Store;
using NUnit.Framework;
using UmbracoExamine;
using UmbracoExamine.PDF;
namespace Umbraco.Tests.UmbracoExamine
{
[TestFixture]
public class PdfIndexerTests // itextsharp is not med trust safe so can't use hte base class: ExamineBaseTest<PdfIndexerTests>
{
private readonly TestMediaService _mediaService = new TestMediaService();
private static PDFIndexer _indexer;
private static UmbracoExamineSearcher _searcher;
private Lucene.Net.Store.Directory _luceneDir;
[SetUp]
public void TestSetup()
{
UmbracoExamineSearcher.DisableInitializationCheck = true;
BaseUmbracoIndexer.DisableInitializationCheck = true;
//we'll copy over the pdf files first
var svc = new TestDataService();
var path = svc.MapPath("/App_Data/Converting_file_to_PDF.pdf");
var f = new FileInfo(path);
var dir = f.Directory;
//ensure the folder is there
System.IO.Directory.CreateDirectory(dir.FullName);
var pdfs = new[] { TestFiles.Converting_file_to_PDF, TestFiles.PDFStandards, TestFiles.SurviorFlipCup, TestFiles.windows_vista };
var names = new[] { "Converting_file_to_PDF.pdf", "PDFStandards.pdf", "SurviorFlipCup.pdf", "windows_vista.pdf" };
for (int index = 0; index < pdfs.Length; index++)
{
var p = pdfs[index];
using (var writer = File.Create(Path.Combine(dir.FullName, names[index])))
{
writer.Write(p, 0, p.Length);
}
}
_luceneDir = new RAMDirectory();
_indexer = IndexInitializer.GetPdfIndexer(_luceneDir);
_indexer.RebuildIndex();
_searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
}
[TearDown]
public void TestTearDown()
{
UmbracoExamineSearcher.DisableInitializationCheck = null;
BaseUmbracoIndexer.DisableInitializationCheck = null;
_luceneDir.Dispose();
}
[Test]
public void PDFIndexer_Ensure_ParentID_Honored()
{
//change parent id to 1116
var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
//get the 2112 pdf node: 2112
var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.Where(x => (int)x.Attribute("id") == 2112)
.First();
//create a copy of 2112 undneath 1111 which is 'not indexable'
var newpdf = XElement.Parse(node.ToString());
newpdf.SetAttributeValue("id", "999999");
newpdf.SetAttributeValue("path", "-1,1111,999999");
newpdf.SetAttributeValue("parentID", "1111");
//now reindex
_indexer.ReIndexNode(newpdf, IndexTypes.Media);
//make sure it doesn't exist
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(999999).Compile());
Assert.AreEqual(0, results.Count());
}
[Test]
public void PDFIndexer_Reindex()
{
//search the pdf content to ensure it's there
var contents = _searcher.Search(_searcher.CreateSearchCriteria().Id(1113).Compile()).Single()
.Fields[PDFIndexer.TextContentFieldName];
Assert.IsTrue(contents.Contains("Fonts are automatically embedded in Word 2008"));
contents = _searcher.Search(_searcher.CreateSearchCriteria().Id(1114).Compile()).Single()
.Fields[PDFIndexer.TextContentFieldName];
Assert.IsTrue(contents.Contains("Drink the beer and then flip the cup"));
//NOTE: This is one of those PDFs that cannot be read and not sure how to force it too.
// Will leave this here as one day we might figure it out.
//contents = _searcher.Search(_searcher.CreateSearchCriteria().Id(1115).Compile()).Single()
// .Fields[PDFIndexer.TextContentFieldName];
//Assert.IsTrue(contents.Contains("Activation associates the use of the software"));
contents = _searcher.Search(_searcher.CreateSearchCriteria().Id(1116).Compile()).Single()
.Fields[PDFIndexer.TextContentFieldName];
Assert.IsTrue(contents.Contains("This lack of standardization could be chaotic"));
}
}
}
+1 -41
View File
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18034
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -60,16 +60,6 @@ namespace Umbraco.Tests.UmbracoExamine {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] Converting_file_to_PDF {
get {
object obj = ResourceManager.GetObject("Converting_file_to_PDF", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
///&lt;media&gt;
@@ -83,26 +73,6 @@ namespace Umbraco.Tests.UmbracoExamine {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] PDFStandards {
get {
object obj = ResourceManager.GetObject("PDFStandards", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] SurviorFlipCup {
get {
object obj = ResourceManager.GetObject("SurviorFlipCup", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
///&lt;!DOCTYPE root[
@@ -148,15 +118,5 @@ namespace Umbraco.Tests.UmbracoExamine {
internal static string umbraco_sort {
get {
return ResourceManager.GetString("umbraco_sort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] windows_vista {
get {
object obj = ResourceManager.GetObject("windows_vista", resourceCulture);
return ((byte[])(obj));
}
}
@@ -118,25 +118,13 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Converting_file_to_PDF" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\converting_file_to_pdf.pdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="media" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\media.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="PDFStandards" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\pdfstandards.pdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="umbraco" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\umbraco.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="umbraco_sort" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\umbraco-sort.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="windows_vista" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\windows-vista.pdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="SurviorFlipCup" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>testfiles\surviorflipcup.pdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.61.2941" targetFramework="net45" />
<package id="Examine" version="0.1.62.2941" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
@@ -327,6 +327,11 @@ function mediaHelper(umbRequestHelper) {
* @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
*/
detectIfImageByExtension: function (imagePath) {
if (!imagePath) {
return false;
}
var lowered = imagePath.toLowerCase();
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
@@ -37,7 +37,7 @@ function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $
if (content.parentId && content.parentId != -1) {
entityResource.getAncestors(content.id, "document")
.then(function (anc) {
$scope.ancestors = anc.reverse();
$scope.ancestors = anc;
});
}
}
@@ -71,7 +71,7 @@ function mediaEditController($scope, $routeParams, appState, mediaResource, enti
//We fetch all ancestors of the node to generate the footer breadcrump navigation
entityResource.getAncestors($routeParams.id, "media")
.then(function (anc) {
$scope.ancestors = anc.reverse();
$scope.ancestors = anc;
});
}
@@ -175,8 +175,10 @@ function contentPickerController($scope, dialogService, entityResource, editorSt
return d.id == id;
});
entity.icon = iconHelper.convertFromLegacyIcon(entity.icon);
$scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon });
if(entity) {
entity.icon = iconHelper.convertFromLegacyIcon(entity.icon);
$scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon });
}
});
+4 -5
View File
@@ -127,9 +127,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.61.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.62.2941, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.61.2941\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.62.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -1878,7 +1878,6 @@
<Content Include="Umbraco\Plugins\Tinymce3\insertChar.aspx" />
<Content Include="Umbraco\Webservices\TreeClientService.asmx" />
<Content Include="Umbraco_Client\Application\NamespaceManager.js" />
<Content Include="Umbraco_Client\PasswordStrength\passwordstrength.js" />
<Content Include="Umbraco_Client\Ui\jQueryWresize.js" />
<Content Include="Umbraco\Search\quickSearch.js" />
<Content Include="Umbraco\Members\search.aspx" />
@@ -2547,9 +2546,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7240</DevelopmentServerPort>
<DevelopmentServerPort>7250</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7240</IISUrl>
<IISUrl>http://localhost:7250</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -137,10 +137,14 @@
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
@disableFindContentByIdPath
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
this setting to true stops that behavior
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false">
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false">
</web.routing>
</settings>
@@ -295,10 +295,14 @@
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
@disableFindContentByIdPath
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
this setting to true stops that behavior
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false">
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false">
</web.routing>
</settings>
+1 -1
View File
@@ -4,7 +4,7 @@
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="ClientDependency-Mvc" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.61.2941" targetFramework="net45" />
<package id="Examine" version="0.1.62.2941" targetFramework="net45" />
<package id="ImageProcessor" version="1.9.5.0" targetFramework="net45" />
<package id="ImageProcessor.Web" version="3.3.1.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
@@ -1010,7 +1010,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="editors">Editor</key>
<key alias="excerptField">Excerpt field</key>
<key alias="language">Language</key>
<key alias="loginname">Login</key>
<key alias="loginname">Username</key>
<key alias="mediastartnode">Start Node in Media Library</key>
<key alias="modules">Sections</key>
<key alias="noConsole">Disable Umbraco Access</key>
@@ -1029,7 +1029,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="permissionSelectPages">Select pages to modify their permissions</key>
<key alias="searchAllChildren">Search all children</key>
<key alias="startnode">Start Node in Content</key>
<key alias="username">Username</key>
<key alias="username">Name</key>
<key alias="userPermissions">User permissions</key>
<key alias="usertype">User type</key>
<key alias="userTypes">User types</key>
@@ -107,7 +107,7 @@
<p>
<strong>Please note:</strong> Installing a package containing several items and
files can take some time. Do not refresh the page or navigate away before, the installer
notifies you the install is completed.
notifies you once the install is completed.
</p>
</div>
@@ -280,9 +280,9 @@
<cc1:PropertyPanel runat="server">
<p>
All items in the package has been installed</p>
All items in the package have been installed</p>
<p>
Overview of what was installed can found under "installed package" in the developer
Overview of what was installed can be found under "installed package" in the developer
section.</p>
<p>
Uninstall is available at the same location.</p>
@@ -1,148 +0,0 @@
//
// password_strength_plugin.js
// Copyright (c) 2009 myPocket technologies (www.mypocket-technologies.com)
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// View the GNU General Public License <http://www.gnu.org/licenses/>.
// @author Darren Mason (djmason9@gmail.com)
// @date 1/23/2009
// @projectDescription Password Strength Meter is a jQuery plug-in provide you smart algorithm to detect a password strength. Based on Firas Kassem orginal plugin - http://phiras.wordpress.com/2007/04/08/password-strength-meter-a-jquery-plugin/
// @version 1.0.0
//
// @requires jquery.js (tested with 1.3.1)
// @param shortPass: "shortPass", //optional
// @param badPass: "badPass", //optional
// @param goodPass: "goodPass", //optional
// @param strongPass: "strongPass", //optional
// @param baseStyle: "testresult", //optional
// @param userid: "", //required override
// @param messageloc: 1 //before == 0 or after == 1
//
(function ($) {
$.fn.shortPass = 'The password is too short';
$.fn.badPass = 'The password is weak';
$.fn.goodPass = 'Good password';
$.fn.strongPass = 'Strong password';
$.fn.samePassword = 'User name and Password are identical.';
$.fn.resultStyle = "";
$.fn.passStrength = function (options) {
var defaults = {
shortPass: 'shortPass', //optional
badPass: 'badPass', //optional
goodPass: 'goodPass', //optional
strongPass: 'strongPass', //optional
baseStyle: 'testresult', //optional
userid: '', //required override
messageloc: 1, //before == 0 or after == 1
minLength: 7
};
var opts = $.extend(defaults, options);
return this.each(function () {
var obj = $(this);
$(obj).unbind().keyup(function () {
var results = $.fn.teststrength($(this).val(), opts.userid, opts);
var fieldSpan = this.parent;
if (opts.messageloc === 1) {
$(this).parent().next("." + opts.baseStyle).remove();
$(this).parent().after("<span class=\"" + opts.baseStyle + "\"><strong></strong></span>");
$(this).parent().next("." + opts.baseStyle).addClass($(this).resultStyle).find("strong").text(results);
}
else {
$(this).prev("." + opts.baseStyle).remove();
$(this).before("<span class=\"" + opts.baseStyle + "\"><strong></strong></span>");
$(this).prev("." + opts.baseStyle).addClass($(this).resultStyle).find("strong").text(results);
}
});
//FUNCTIONS
$.fn.teststrength = function (password, username, option) {
var score = 0;
//password < minLength
if (password.length < option.minLength) { this.resultStyle = option.shortPass; return $(this).shortPass; }
//password == user name
if (password.toLowerCase() == username.toLowerCase()) { this.resultStyle = option.badPass; return $(this).samePassword; }
//password length
score += password.length // 4;
score += ($.fn.checkRepetition(1, password).length - password.length) // 1;
score += ($.fn.checkRepetition(2, password).length - password.length) // 1;
score += ($.fn.checkRepetition(3, password).length - password.length) // 1;
score += ($.fn.checkRepetition(4, password).length - password.length) // 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/)) { score += 5; }
//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) { score += 5; }
//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) { score += 10; }
//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/)) { score += 15; }
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/)) { score += 15; }
//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/)) { score += 15; }
//password is just a numbers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/)) { score -= 10; }
//verifying 0 < score < 100
if (score < 0) { score = 0; }
if (score > 100) { score = 100; }
if (score < 34) { this.resultStyle = option.badPass; return $(this).badPass; }
if (score < 68) { this.resultStyle = option.goodPass; return $(this).goodPass; }
this.resultStyle = option.strongPass;
return $(this).strongPass;
};
});
};
})(jQuery);
$.fn.checkRepetition = function(pLen, str) {
var res = '';
for (var i = 0; i < str.length; i++) {
var repeated = true;
for (var j = 0; j < pLen && (j + i + pLen) < str.length; j++) {
repeated = repeated && (str.charAt(j + i) == str.charAt(j + i + pLen));
}
if (j < pLen) { repeated = false; }
if (repeated) {
i += pLen - 1;
repeated = false;
}
else {
res += str.charAt(i);
}
}
return res;
};
@@ -14,6 +14,7 @@ using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.member;
using System.Linq;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Publishing;
using Content = Umbraco.Core.Models.Content;
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs;
@@ -143,10 +144,90 @@ namespace Umbraco.Web.Cache
ContentService.Trashed += ContentServiceTrashed;
ContentService.EmptiedRecycleBin += ContentServiceEmptiedRecycleBin;
PublishingStrategy.Published += PublishingStrategy_Published;
PublishingStrategy.UnPublished += PublishingStrategy_UnPublished;
//public access events
Access.AfterSave += Access_AfterSave;
}
#region Publishing
void PublishingStrategy_UnPublished(IPublishingStrategy sender, PublishEventArgs<IContent> e)
{
if (e.PublishedEntities.Any())
{
if (e.PublishedEntities.Count() > 1)
{
foreach (var c in e.PublishedEntities)
{
UnPublishSingle(c);
}
}
else
{
var content = e.PublishedEntities.FirstOrDefault();
UnPublishSingle(content);
}
}
}
/// <summary>
/// Refreshes the xml cache for a single node by removing it
/// </summary>
private void UnPublishSingle(IContent content)
{
DistributedCache.Instance.RemovePageCache(content);
}
void PublishingStrategy_Published(IPublishingStrategy sender, PublishEventArgs<IContent> e)
{
if (e.PublishedEntities.Any())
{
if (e.IsAllRepublished)
{
UpdateEntireCache();
return;
}
if (e.PublishedEntities.Count() > 1)
{
UpdateMultipleContentCache(e.PublishedEntities);
}
else
{
var content = e.PublishedEntities.FirstOrDefault();
UpdateSingleContentCache(content);
}
}
}
/// <summary>
/// Refreshes the xml cache for all nodes
/// </summary>
private void UpdateEntireCache()
{
DistributedCache.Instance.RefreshAllPageCache();
}
/// <summary>
/// Refreshes the xml cache for nodes in list
/// </summary>
private void UpdateMultipleContentCache(IEnumerable<IContent> content)
{
DistributedCache.Instance.RefreshPageCache(content.ToArray());
}
/// <summary>
/// Refreshes the xml cache for a single node
/// </summary>
private void UpdateSingleContentCache(IContent content)
{
DistributedCache.Instance.RefreshPageCache(content);
}
#endregion
#region Public access event handlers
static void Access_AfterSave(Access sender, SaveEventArgs e)
@@ -557,6 +557,7 @@ namespace Umbraco.Web.Editors
return Services.EntityService.GetAll(objectType.Value, ids)
.WhereNotNull()
.OrderBy(x => x.Level)
.Select(Mapper.Map<EntityBasic>);
}
//now we need to convert the unknown ones
+35 -32
View File
@@ -14,58 +14,58 @@ namespace Umbraco.Web.Install
internal static readonly string[] PermissionFiles = { };
internal static readonly string[] PackagesPermissionsDirs = { SystemDirectories.Bin, SystemDirectories.Umbraco, SystemDirectories.UserControls, SystemDirectories.Packages };
public static bool RunFilePermissionTestSuite()
public static bool RunFilePermissionTestSuite(out Dictionary<string, List<string>> errorReport)
{
var newReport = new Dictionary<string, string>();
errorReport = new Dictionary<string, List<string>>();
if (!TestDirectories(PermissionDirs, ref newReport))
return false;
List<string> errors;
if (!TestDirectories(PackagesPermissionsDirs, ref newReport))
return false;
if (TestDirectories(PermissionDirs, out errors) == false)
errorReport["Folder creation failed"] = errors.ToList();
if (!TestFiles(PermissionFiles, ref newReport))
return false;
if (TestDirectories(PackagesPermissionsDirs, out errors) == false)
errorReport["File writing for packages failed"] = errors.ToList();
if (!TestContentXml(ref newReport))
return false;
if (TestFiles(PermissionFiles, out errors) == false)
errorReport["File writing failed"] = errors.ToList();
if (!TestFolderCreation(SystemDirectories.Media, ref newReport))
return false;
if (TestContentXml(out errors) == false)
errorReport["Cache file writing failed"] = errors.ToList();
return true;
if (TestFolderCreation(SystemDirectories.Media, out errors) == false)
errorReport["Media folder creation failed"] = errors.ToList();
return errorReport.Any() == false;
}
public static bool TestDirectories(string[] directories, ref Dictionary<string, string> errorReport)
public static bool TestDirectories(string[] directories, out List<string> errorReport)
{
errorReport = new List<string>();
bool succes = true;
foreach (string dir in PermissionDirs)
{
bool result = SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt"));
if (!result)
if (result == false)
{
succes = false;
if (errorReport != null)
errorReport.Add(dir, "Missing permissions, cannot create new files");
errorReport.Add(dir);
}
}
return succes;
}
public static bool TestFiles(string[] files, ref Dictionary<string,string> errorReport)
public static bool TestFiles(string[] files, out List<string> errorReport)
{
errorReport = new List<string>();
bool succes = true;
foreach (string file in PermissionFiles)
{
bool result = OpenFileForWrite(IOHelper.MapPath(file));
if (!result)
if (result == false)
{
if (errorReport != null)
errorReport.Add(file, "Missing write permissions");
errorReport.Add(file);
succes = false;
}
}
@@ -73,8 +73,9 @@ namespace Umbraco.Web.Install
return succes;
}
public static bool TestFolderCreation(string folder, ref Dictionary<string,string> errorReport)
public static bool TestFolderCreation(string folder, out List<string> errorReport)
{
errorReport = new List<string>();
try
{
string tempDir = IOHelper.MapPath(folder + "/testCreatedByConfigWizard");
@@ -84,24 +85,26 @@ namespace Umbraco.Web.Install
}
catch
{
if (errorReport != null)
errorReport.Add(folder, "Could not create sub-directory");
errorReport.Add(folder);
return false;
}
}
public static bool TestContentXml(ref Dictionary<string, string> errorReport)
public static bool TestContentXml(out List<string> errorReport)
{
// Test umbraco.xml file
errorReport = new List<string>();
// Test creating/saving/deleting a file in the same location as the content xml file
// NOTE: We cannot modify the xml file directly because a background thread is responsible for
// that and we might get lock issues.
try
{
content.Instance.PersistXmlToFile();
var xmlFile = content.Instance.UmbracoXmlDiskCacheFileName + ".tmp";
SaveAndDeleteFile(xmlFile);
return true;
}
catch
{
if(errorReport != null)
errorReport.Add(SystemFiles.ContentCacheXml, "Could not persist content cache");
errorReport.Add(SystemFiles.ContentCacheXml);
return false;
}
}
@@ -112,7 +115,7 @@ namespace Umbraco.Web.Install
{
//first check if the directory of the file exists, and if not try to create that first.
FileInfo fi = new FileInfo(file);
if (!fi.Directory.Exists)
if (fi.Directory.Exists == false)
{
fi.Directory.Create();
}
@@ -17,77 +17,9 @@ namespace Umbraco.Web.Install.InstallSteps
{
//first validate file permissions
var permissionsOk = true;
var reportParts = new Dictionary<string, List<string>>();
// Test default dir permissions
foreach (var dir in FilePermissionHelper.PermissionDirs)
{
var result = SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt"));
if (!result)
{
var report = reportParts.GetOrCreate("Folder creation failed");
permissionsOk = false;
report.Add(dir);
}
}
// Test default file permissions
foreach (var file in FilePermissionHelper.PermissionFiles)
{
var result = OpenFileForWrite(IOHelper.MapPath(file));
if (!result)
{
var report = reportParts.GetOrCreate("File writing failed");
permissionsOk = false;
report.Add(file);
}
}
// Test package dir permissions
string packageResult = "";
foreach (var dir in FilePermissionHelper.PackagesPermissionsDirs)
{
var result =
SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt"));
if (!result)
{
var report = reportParts.GetOrCreate("File writing for packages failed");
permissionsOk = false;
report.Add(dir);
}
}
// Test umbraco.xml file
try
{
content.Instance.PersistXmlToFile();
}
catch (Exception ee)
{
permissionsOk = false;
string tempFile = SystemFiles.ContentCacheXml;
if (tempFile.Substring(0, 1) == "/")
tempFile = tempFile.Substring(1, tempFile.Length - 1);
var report = reportParts.GetOrCreate("Cache file writing failed");
report.Add(tempFile);
}
// Test creation of folders
try
{
string tempDir = IOHelper.MapPath(SystemDirectories.Media + "/testCreatedByConfigWizard");
Directory.CreateDirectory(tempDir);
Directory.Delete(tempDir);
}
catch
{
permissionsOk = false;
var report = reportParts.GetOrCreate("Media folder creation failed");
report.Add("Could not create sub folders in " + SystemDirectories.Media);
}
Dictionary<string, List<string>> reportParts;
permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out reportParts);
if (permissionsOk == false)
{
@@ -97,41 +29,6 @@ namespace Umbraco.Web.Install.InstallSteps
return null;
}
private bool OpenFileForWrite(string file)
{
try
{
File.AppendText(file).Close();
}
catch
{
return false;
}
return true;
}
private static bool SaveAndDeleteFile(string file)
{
try
{
//first check if the directory of the file exists, and if not try to create that first.
var fi = new FileInfo(file);
if (!fi.Directory.Exists)
{
fi.Directory.Create();
}
File.WriteAllText(file,
"This file has been created by the umbraco configuration wizard. It is safe to delete it!");
File.Delete(file);
return true;
}
catch
{
return false;
}
}
public override bool RequiresExecution(object model)
{
return true;
+44 -10
View File
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.Linq;
using umbraco.cms.businesslogic.web;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
@@ -21,6 +22,7 @@ namespace Umbraco.Web.Models
{
return GetCulture(UmbracoContext.Current,
ApplicationContext.Current.Services.LocalizationService,
ApplicationContext.Current.Services.ContentService,
content.Id, content.Path,
current);
}
@@ -31,30 +33,62 @@ namespace Umbraco.Web.Models
/// </summary>
/// <param name="umbracoContext">An <see cref="UmbracoContext"/> instance.</param>
/// <param name="localizationService">An <see cref="ILocalizationService"/> implementation.</param>
/// <param name="contentService">An <see cref="IContentService"/> implementation.</param>
/// <param name="contentId">The content identifier.</param>
/// <param name="contentPath">The content path.</param>
/// <param name="current">The request Uri.</param>
/// <returns>The culture that would be selected to render the content.</returns>
internal static CultureInfo GetCulture(UmbracoContext umbracoContext, ILocalizationService localizationService,
internal static CultureInfo GetCulture(UmbracoContext umbracoContext, ILocalizationService localizationService, IContentService contentService,
int contentId, string contentPath, Uri current)
{
var route = umbracoContext.ContentCache.GetRouteById(contentId); // cached
var pos = route.IndexOf('/');
var route = umbracoContext == null
? null // for tests only
: umbracoContext.ContentCache.GetRouteById(contentId); // cached
var domain = pos == 0
? null
: DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current).Domain;
Domain domain;
if (route == null)
{
// if content is not published then route is null and we have to work
// on non-published content (note: could optimize by checking routes?)
var content = contentService.GetById(contentId);
if (content == null)
return GetDefaultCulture(localizationService);
var hasDomain = DomainHelper.NodeHasDomains(content.Id);
while (hasDomain == false && content != null)
{
content = content.Parent();
hasDomain = content != null && DomainHelper.NodeHasDomains(content.Id);
}
domain = hasDomain ? DomainHelper.DomainForNode(content.Id, current).Domain : null;
}
else
{
// if content is published then we have a (cached) route
// from which we can figure out the domain
var pos = route.IndexOf('/');
domain = pos == 0
? null
: DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current).Domain;
}
if (domain == null)
{
var defaultLanguage = localizationService.GetAllLanguages().FirstOrDefault();
return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode);
}
return GetDefaultCulture(localizationService);
var wcDomain = DomainHelper.FindWildcardDomainInPath(DomainHelper.GetAllDomains(true), contentPath, domain.RootNodeId);
return wcDomain == null
? new CultureInfo(domain.Language.CultureAlias)
: new CultureInfo(wcDomain.Language.CultureAlias);
}
private static CultureInfo GetDefaultCulture(ILocalizationService localizationService)
{
var defaultLanguage = localizationService.GetAllLanguages().FirstOrDefault();
return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode);
}
}
}
@@ -1,9 +1,10 @@
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
@@ -14,8 +15,10 @@ namespace Umbraco.Web.Mvc
{
private IPublishedContent _publishedContent;
private readonly int _pageId;
private NameValueCollection _queryStringValues;
private readonly UmbracoContext _umbracoContext;
private string _url;
public string Url
{
get
@@ -24,7 +27,7 @@ namespace Umbraco.Web.Mvc
if (PublishedContent == null)
{
throw new InvalidOperationException("Cannot redirect, no entity was found for id " + _pageId);
throw new InvalidOperationException(string.Format("Cannot redirect, no entity was found for id {0}", _pageId));
}
var result = _umbracoContext.RoutingContext.UrlProvider.GetUrl(PublishedContent.Id);
@@ -34,7 +37,7 @@ namespace Umbraco.Web.Mvc
return _url;
}
throw new InvalidOperationException("Could not route to entity with id " + _pageId + ", the NiceUrlProvider could not generate a URL");
throw new InvalidOperationException(string.Format("Could not route to entity with id {0}, the NiceUrlProvider could not generate a URL", _pageId));
}
}
@@ -61,6 +64,26 @@ namespace Umbraco.Web.Mvc
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues)
: this(pageId, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString)
: this(pageId, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
@@ -70,6 +93,63 @@ namespace Umbraco.Web.Mvc
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues)
: this(publishedContent, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="queryString"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString)
: this(publishedContent, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, UmbracoContext umbracoContext)
{
_pageId = pageId;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
@@ -82,16 +162,33 @@ namespace Umbraco.Web.Mvc
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, UmbracoContext umbracoContext)
{
_pageId = pageId;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
public override void ExecuteResult(ControllerContext context)
{
@@ -103,10 +200,26 @@ namespace Umbraco.Web.Mvc
}
var destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
if (_queryStringValues != null && _queryStringValues.Count > 0)
{
destinationUrl = destinationUrl += "?" + string.Join("&",
_queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x])));
}
context.Controller.TempData.Keep();
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
}
private NameValueCollection ParseQueryString(string queryString)
{
if (!string.IsNullOrEmpty(queryString))
{
return HttpUtility.ParseQueryString(queryString);
}
return null;
}
}
}
+64 -2
View File
@@ -1,10 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Web.Security;
using System.Collections.Specialized;
namespace Umbraco.Web.Mvc
{
@@ -56,6 +55,28 @@ namespace Umbraco.Web.Mvc
return new RedirectToUmbracoPageResult(pageId, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(pageId, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString)
{
return new RedirectToUmbracoPageResult(pageId, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
@@ -66,6 +87,28 @@ namespace Umbraco.Web.Mvc
return new RedirectToUmbracoPageResult(publishedContent, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(publishedContent, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, string queryString)
{
return new RedirectToUmbracoPageResult(publishedContent, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page
/// </summary>
@@ -75,6 +118,25 @@ namespace Umbraco.Web.Mvc
return new RedirectToUmbracoPageResult(CurrentPage, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(string queryString)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco URL
/// </summary>
@@ -0,0 +1,206 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Web.Scheduling;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
/// <summary>
/// This is the background task runner that persists the xml file to the file system
/// </summary>
/// <remarks>
/// This is used so that all file saving is done on a web aware worker background thread and all logic is performed async so this
/// process will not interfere with any web requests threads. This is also done as to not require any global locks and to ensure that
/// if multiple threads are performing publishing tasks that the file will be persisted in accordance with the final resulting
/// xml structure since the file writes are queued.
/// </remarks>
internal class XmlCacheFilePersister : ILatchedBackgroundTask
{
private readonly IBackgroundTaskRunner<XmlCacheFilePersister> _runner;
private readonly content _content;
private readonly ManualResetEventSlim _latch = new ManualResetEventSlim(false);
private readonly object _locko = new object();
private bool _released;
private Timer _timer;
private DateTime _initialTouch;
private readonly AsyncLock _runLock = new AsyncLock(); // ensure we run once at a time
// note:
// as long as the runner controls the runs, we know that we run once at a time, but
// when the AppDomain goes down and the runner has completed and yet the persister is
// asked to save, then we need to run immediately - but the runner may be running, so
// we need to make sure there's no collision - hence _runLock
private const int WaitMilliseconds = 4000; // save the cache 4s after the last change (ie every 4s min)
private const int MaxWaitMilliseconds = 30000; // save the cache after some time (ie no more than 30s of changes)
// save the cache when the app goes down
public bool RunsOnShutdown { get { return true; } }
// initialize the first instance, which is inactive (not touched yet)
public XmlCacheFilePersister(IBackgroundTaskRunner<XmlCacheFilePersister> runner, content content)
: this(runner, content, false)
{ }
private XmlCacheFilePersister(IBackgroundTaskRunner<XmlCacheFilePersister> runner, content content, bool touched)
{
_runner = runner;
_content = content;
if (runner.TryAdd(this) == false)
{
_runner = null; // runner's down
_released = true; // don't mess with timer
return;
}
// runner could decide to run it anytime now
if (touched == false) return;
LogHelper.Debug<XmlCacheFilePersister>("Created, save in {0}ms.", () => WaitMilliseconds);
_initialTouch = DateTime.Now;
_timer = new Timer(_ => TimerRelease());
_timer.Change(WaitMilliseconds, 0);
}
public XmlCacheFilePersister Touch()
{
// if _released is false then we're going to setup a timer
// then the runner wants to shutdown & run immediately
// this sets _released to true & the timer will trigger eventualy & who cares?
// if _released is true, either it's a normal release, or
// a runner shutdown, in which case we won't be able to
// add a new task, and so we'll run immediately
var ret = this;
var runNow = false;
lock (_locko)
{
if (_released) // our timer has triggered OR the runner is shutting down
{
LogHelper.Debug<XmlCacheFilePersister>("Touched, was released...");
// release: has run or is running, too late, return a new task (adds itself to runner)
if (_runner == null)
{
LogHelper.Debug<XmlCacheFilePersister>("Runner is down, run now.");
runNow = true;
}
else
{
LogHelper.Debug<XmlCacheFilePersister>("Create new...");
ret = new XmlCacheFilePersister(_runner, _content, true);
if (ret._runner == null)
{
// could not enlist with the runner, runner is completed, must run now
LogHelper.Debug<XmlCacheFilePersister>("Runner is down, run now.");
runNow = true;
}
}
}
else if (_timer == null) // we don't have a timer yet
{
LogHelper.Debug<XmlCacheFilePersister>("Touched, was idle, start and save in {0}ms.");
_initialTouch = DateTime.Now;
_timer = new Timer(_ => TimerRelease());
_timer.Change(WaitMilliseconds, 0);
}
else // we have a timer
{
// change the timer to trigger in WaitMilliseconds unless we've been touched first more
// than MaxWaitMilliseconds ago and then leave the time unchanged
if (DateTime.Now - _initialTouch < TimeSpan.FromMilliseconds(MaxWaitMilliseconds))
{
LogHelper.Debug<XmlCacheFilePersister>("Touched, was waiting, can delay, save in {0}ms.", () => WaitMilliseconds);
_timer.Change(WaitMilliseconds, 0);
}
else
{
LogHelper.Debug<XmlCacheFilePersister>("Touched, was waiting, cannot delay.");
}
}
}
if (runNow)
Run();
return ret; // this, by default, unless we created a new one
}
private void TimerRelease()
{
lock (_locko)
{
LogHelper.Debug<XmlCacheFilePersister>("Timer: release.");
if (_timer != null)
_timer.Dispose();
_timer = null;
_released = true;
// if running (because of shutdown) this will have no effect
// else it tells the runner it is time to run the task
_latch.Set();
}
}
public WaitHandle Latch
{
get { return _latch.WaitHandle; }
}
public bool IsLatched
{
get { return true; }
}
public async Task RunAsync(CancellationToken token)
{
lock (_locko)
{
LogHelper.Debug<XmlCacheFilePersister>("Run now (async).");
// just make sure - in case the runner is running the task on shutdown
_released = true;
}
// http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code
// http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
// do we really need that ConfigureAwait here?
using (await _runLock.LockAsync())
{
await _content.SaveXmlToFileAsync().ConfigureAwait(false);
}
}
public bool IsAsync
{
get { return true; }
}
public void Dispose()
{ }
public void Run()
{
lock (_locko)
{
LogHelper.Debug<XmlCacheFilePersister>("Run now (sync).");
// not really needed but safer (it's only us invoking Run, but the method is public...)
_released = true;
}
using (_runLock.Lock())
{
_content.SaveXmlToFile();
}
}
}
}
@@ -1899,6 +1899,7 @@ namespace Umbraco.Web
{
return Models.ContentExtensions.GetCulture(UmbracoContext.Current,
ApplicationContext.Current.Services.LocalizationService,
ApplicationContext.Current.Services.ContentService,
content.Id, content.Path,
current);
}
@@ -2,6 +2,8 @@ using System;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
namespace Umbraco.Web.Routing
{
@@ -13,13 +15,31 @@ namespace Umbraco.Web.Routing
/// </remarks>
public class ContentFinderByIdPath : IContentFinder
{
/// <summary>
private readonly IWebRoutingSection _webRoutingSection;
public ContentFinderByIdPath()
: this(UmbracoConfig.For.UmbracoSettings().WebRouting)
{
}
public ContentFinderByIdPath(IWebRoutingSection webRoutingSection)
{
_webRoutingSection = webRoutingSection;
}
/// <summary>
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
/// </summary>
/// <param name="docRequest">The <c>PublishedContentRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public bool TryFindContent(PublishedContentRequest docRequest)
{
if (docRequest.RoutingContext.UmbracoContext != null && docRequest.RoutingContext.UmbracoContext.InPreviewMode == false
&& _webRoutingSection.DisableFindContentByIdPath)
return false;
IPublishedContent node = null;
var path = docRequest.Uri.GetAbsolutePathDecoded();
+419 -232
View File
@@ -1,301 +1,405 @@
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Events;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// This is used to create a background task runner which will stay alive in the background of and complete
/// any tasks that are queued. It is web aware and will ensure that it is shutdown correctly when the app domain
/// is shutdown.
/// Manages a queue of tasks of type <typeparamref name="T"/> and runs them in the background.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class BackgroundTaskRunner<T> : IDisposable, IRegisteredObject
where T : IBackgroundTask
/// <typeparam name="T">The type of the managed tasks.</typeparam>
/// <remarks>The task runner is web-aware and will ensure that it shuts down correctly when the AppDomain
/// shuts down (ie is unloaded).</remarks>
internal class BackgroundTaskRunner<T> : IBackgroundTaskRunner<T>
where T : class, IBackgroundTask
{
private readonly bool _dedicatedThread;
private readonly bool _persistentThread;
private readonly BackgroundTaskRunnerOptions _options;
private readonly BlockingCollection<T> _tasks = new BlockingCollection<T>();
private Task _consumer;
private readonly object _locker = new object();
private readonly ManualResetEventSlim _completedEvent = new ManualResetEventSlim(false);
private BackgroundTaskRunnerAwaiter<T> _awaiter;
private volatile bool _isRunning; // is running
private volatile bool _isCompleted; // does not accept tasks anymore, may still be running
private Task _runningTask;
private volatile bool _isRunning = false;
private static readonly object Locker = new object();
private CancellationTokenSource _tokenSource;
internal event EventHandler<TaskEventArgs<T>> TaskError;
internal event EventHandler<TaskEventArgs<T>> TaskStarting;
internal event EventHandler<TaskEventArgs<T>> TaskCompleted;
internal event EventHandler<TaskEventArgs<T>> TaskCancelled;
public BackgroundTaskRunner(bool dedicatedThread = false, bool persistentThread = false)
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskError;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskStarting;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskCompleted;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskCancelled;
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Completed;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunner{T}"/> class.
/// </summary>
public BackgroundTaskRunner()
: this(new BackgroundTaskRunnerOptions())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunner{T}"/> class with a set of options.
/// </summary>
/// <param name="options">The set of options.</param>
public BackgroundTaskRunner(BackgroundTaskRunnerOptions options)
{
_dedicatedThread = dedicatedThread;
_persistentThread = persistentThread;
if (options == null) throw new ArgumentNullException("options");
_options = options;
HostingEnvironment.RegisterObject(this);
if (options.AutoStart)
StartUp();
}
/// <summary>
/// Gets the number of tasks in the queue.
/// </summary>
public int TaskCount
{
get { return _tasks.Count; }
}
/// <summary>
/// Gets a value indicating whether a task is currently running.
/// </summary>
public bool IsRunning
{
get { return _isRunning; }
}
public TaskStatus TaskStatus
{
get { return _consumer.Status; }
}
/// <summary>
/// Returns the task awaiter so that consumers of the BackgroundTaskManager can await
/// the threading operation.
/// Gets a value indicating whether the runner has completed and cannot accept tasks anymore.
/// </summary>
/// <returns></returns>
/// <remarks>
/// This is just the coolest thing ever, check this article out:
/// http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx
///
/// So long as we have a method called GetAwaiter() that returns an instance of INotifyCompletion
/// we can await anything! :)
/// </remarks>
public TaskAwaiter GetAwaiter()
public bool IsCompleted
{
return _consumer.GetAwaiter();
get { return _isCompleted; }
}
/// <summary>
/// Gets an awaiter used to await the running Threading.Task.
/// </summary>
/// <exception cref="InvalidOperationException">There is no running task.</exception>
/// <remarks>
/// Unless the AutoStart option is true, there will be no running task until
/// a background task is added to the queue. Unless the KeepAlive option is true, there
/// will be no running task when the queue is empty.
/// </remarks>
public ThreadingTaskAwaiter CurrentThreadingTask
{
get
{
if (_runningTask == null)
throw new InvalidOperationException("There is no current Threading.Task.");
return new ThreadingTaskAwaiter(_runningTask);
}
}
/// <summary>
/// Gets an awaiter used to await the BackgroundTaskRunner running operation
/// </summary>
/// <returns>An awaiter for the BackgroundTaskRunner running operation</returns>
/// <remarks>
/// <para>This is used to wait until the background task runner is no longer running (IsRunning == false)
/// </para>
/// <para> So long as we have a method called GetAwaiter() that returns an instance of INotifyCompletion
/// we can await anything. In this case we are awaiting with a custom BackgroundTaskRunnerAwaiter
/// which waits for the Completed event to be raised.
/// ref: http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx
/// </para>
/// </remarks>
public BackgroundTaskRunnerAwaiter<T> GetAwaiter()
{
return _awaiter ?? (_awaiter = new BackgroundTaskRunnerAwaiter<T>(this));
}
/// <summary>
/// Adds a task to the queue.
/// </summary>
/// <param name="task">The task to add.</param>
/// <exception cref="InvalidOperationException">The task runner has completed.</exception>
public void Add(T task)
{
//add any tasks first
LogHelper.Debug<BackgroundTaskRunner<T>>(" Task added {0}", () => task.GetType());
_tasks.Add(task);
lock (_locker)
{
if (_isCompleted)
throw new InvalidOperationException("The task runner has completed.");
//ensure's everything is started
StartUp();
// add task
LogHelper.Debug<BackgroundTaskRunner<T>>("Task added {0}", task.GetType);
_tasks.Add(task);
// start
StartUpLocked();
}
}
/// <summary>
/// Tries to add a task to the queue.
/// </summary>
/// <param name="task">The task to add.</param>
/// <returns>true if the task could be added to the queue; otherwise false.</returns>
/// <remarks>Returns false if the runner is completed.</remarks>
public bool TryAdd(T task)
{
lock (_locker)
{
if (_isCompleted) return false;
// add task
LogHelper.Debug<BackgroundTaskRunner<T>>("Task added {0}", task.GetType);
_tasks.Add(task);
// start
StartUpLocked();
return true;
}
}
/// <summary>
/// Starts the tasks runner, if not already running.
/// </summary>
/// <remarks>Is invoked each time a task is added, to ensure it is going to be processed.</remarks>
/// <exception cref="InvalidOperationException">The task runner has completed.</exception>
public void StartUp()
{
if (!_isRunning)
if (_isRunning) return;
lock (_locker)
{
lock (Locker)
{
//double check
if (!_isRunning)
{
_isRunning = true;
//Create a new token source since this is a new proces
_tokenSource = new CancellationTokenSource();
StartConsumer();
LogHelper.Debug<BackgroundTaskRunner<T>>("Starting");
}
}
}
}
if (_isCompleted)
throw new InvalidOperationException("The task runner has completed.");
public void ShutDown()
{
lock (Locker)
{
_isRunning = false;
try
{
if (_consumer != null)
{
//cancel all operations
_tokenSource.Cancel();
try
{
_consumer.Wait();
}
catch (AggregateException e)
{
//NOTE: We are logging Debug because we are expecting these errors
LogHelper.Debug<BackgroundTaskRunner<T>>("AggregateException thrown with the following inner exceptions:");
// Display information about each exception.
foreach (var v in e.InnerExceptions)
{
var exception = v as TaskCanceledException;
if (exception != null)
{
LogHelper.Debug<BackgroundTaskRunner<T>>(" .Net TaskCanceledException: .Net Task ID {0}", () => exception.Task.Id);
}
else
{
LogHelper.Debug<BackgroundTaskRunner<T>>(" Exception: {0}", () => v.GetType().Name);
}
}
}
}
if (_tasks.Count > 0)
{
LogHelper.Debug<BackgroundTaskRunner<T>>("Processing remaining tasks before shutdown: {0}", () => _tasks.Count);
//now we need to ensure the remaining queue is processed if there's any remaining,
// this will all be processed on the current/main thread.
T remainingTask;
while (_tasks.TryTake(out remainingTask))
{
ConsumeTaskInternal(remainingTask);
}
}
LogHelper.Debug<BackgroundTaskRunner<T>>("Shutdown");
//disposing these is really optional since they'll be disposed immediately since they are no longer running
//but we'll put this here anyways.
if (_consumer != null && (_consumer.IsCompleted || _consumer.IsCanceled))
{
_consumer.Dispose();
}
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("Error occurred shutting down task runner", ex);
}
finally
{
HostingEnvironment.UnregisterObject(this);
}
StartUpLocked();
}
}
/// <summary>
/// Starts the consumer task
/// Starts the tasks runner, if not already running.
/// </summary>
private void StartConsumer()
/// <remarks>Must be invoked within lock(_locker) and with _isCompleted being false.</remarks>
private void StartUpLocked()
{
var token = _tokenSource.Token;
// double check
if (_isRunning) return;
_isRunning = true;
_consumer = Task.Factory.StartNew(() =>
StartThread(token),
// create a new token source since this is a new process
_tokenSource = new CancellationTokenSource();
_runningTask = PumpIBackgroundTasks(Task.Factory, _tokenSource.Token);
LogHelper.Debug<BackgroundTaskRunner<T>>("Starting");
}
/// <summary>
/// Shuts the taks runner down.
/// </summary>
/// <param name="force">True for force the runner to stop.</param>
/// <param name="wait">True to wait until the runner has stopped.</param>
/// <remarks>If <paramref name="force"/> is false, no more tasks can be queued but all queued tasks
/// will run. If it is true, then only the current one (if any) will end and no other task will run.</remarks>
public void Shutdown(bool force, bool wait)
{
lock (_locker)
{
_isCompleted = true; // do not accept new tasks
if (_isRunning == false) return; // done already
}
// try to be nice
// assuming multiple threads can do these without problems
_completedEvent.Set();
_tasks.CompleteAdding();
if (force)
{
// we must bring everything down, now
Thread.Sleep(100); // give time to CompleAdding()
lock (_locker)
{
// was CompleteAdding() enough?
if (_isRunning == false) return;
}
// try to cancel running async tasks (cannot do much about sync tasks)
// break delayed tasks delay
// truncate running queues
_tokenSource.Cancel(false); // false is the default
}
// tasks in the queue will be executed...
if (wait == false) return;
_runningTask.Wait(); // wait for whatever is running to end...
}
/// <summary>
/// Runs background tasks for as long as there are background tasks in the queue, with an asynchronous operation.
/// </summary>
/// <param name="factory">The supporting <see cref="TaskFactory"/>.</param>
/// <param name="token">A cancellation token.</param>
/// <returns>The asynchronous operation.</returns>
private Task PumpIBackgroundTasks(TaskFactory factory, CancellationToken token)
{
var taskSource = new TaskCompletionSource<object>(factory.CreationOptions);
var enumerator = _options.KeepAlive ? _tasks.GetConsumingEnumerable(token).GetEnumerator() : null;
// ReSharper disable once MethodSupportsCancellation // always run
var taskSourceContinuing = taskSource.Task.ContinueWith(t =>
{
// because the pump does not lock, there's a race condition,
// the pump may stop and then we still have tasks to process,
// and then we must restart the pump - lock to avoid race cond
lock (_locker)
{
if (token.IsCancellationRequested || _tasks.Count == 0)
{
LogHelper.Debug<BackgroundTaskRunner<T>>("_isRunning = false");
_isRunning = false; // done
if (_options.PreserveRunningTask == false)
_runningTask = null;
//raise event
OnCompleted();
return;
}
}
// if _runningTask is taskSource.Task then we must keep continuing it,
// not starting a new taskSource, else _runningTask would complete and
// something may be waiting on it
//PumpIBackgroundTasks(factory, token); // restart
// ReSharper disable once MethodSupportsCancellation // always run
t.ContinueWithTask(_ => PumpIBackgroundTasks(factory, token)); // restart
});
Action<Task> pump = null;
pump = task =>
{
// RunIBackgroundTaskAsync does NOT throw exceptions, just raises event
// so if we have an exception here, really, wtf? - must read the exception
// anyways so it does not bubble up and kill everything
if (task != null && task.IsFaulted)
{
var exception = task.Exception;
LogHelper.Error<BackgroundTaskRunner<T>>("Task runner exception.", exception);
}
// is it ok to run?
if (TaskSourceCanceled(taskSource, token)) return;
// try to get a task
// the blocking MoveNext will end if token is cancelled or collection is completed
T bgTask;
var hasBgTask = _options.KeepAlive
? (bgTask = enumerator.MoveNext() ? enumerator.Current : null) != null // blocking
: _tasks.TryTake(out bgTask); // non-blocking
// no task, signal the runner we're done
if (hasBgTask == false)
{
TaskSourceCompleted(taskSource, token);
return;
}
// wait for latched task, supporting cancellation
var dbgTask = bgTask as ILatchedBackgroundTask;
if (dbgTask != null && dbgTask.IsLatched)
{
WaitHandle.WaitAny(new[] { dbgTask.Latch, token.WaitHandle, _completedEvent.WaitHandle });
if (TaskSourceCanceled(taskSource, token)) return;
// else run now, either because latch ok or runner is completed
// still latched & not running on shutdown = stop here
if (dbgTask.IsLatched && dbgTask.RunsOnShutdown == false)
{
TaskSourceCompleted(taskSource, token);
return;
}
}
// run the task as first task, or a continuation
task = task == null
? RunIBackgroundTaskAsync(bgTask, token)
// ReSharper disable once MethodSupportsCancellation // always run
: task.ContinueWithTask(_ => RunIBackgroundTaskAsync(bgTask, token));
// and pump
// ReSharper disable once MethodSupportsCancellation // always run
task.ContinueWith(t => pump(t));
};
// start it all
factory.StartNew(() => pump(null),
token,
_dedicatedThread ? TaskCreationOptions.LongRunning : TaskCreationOptions.None,
_options.LongRunning ? TaskCreationOptions.LongRunning : TaskCreationOptions.None,
TaskScheduler.Default);
//if this is not a persistent thread, wait till it's done and shut ourselves down
// thus ending the thread or giving back to the thread pool. If another task is added
// another thread will spawn or be taken from the pool to process.
if (!_persistentThread)
{
_consumer.ContinueWith(task => ShutDown());
}
return taskSourceContinuing;
}
/// <summary>
/// Invokes a new worker thread to consume tasks
/// </summary>
/// <param name="token"></param>
private void StartThread(CancellationToken token)
{
// Was cancellation already requested?
if (token.IsCancellationRequested)
{
LogHelper.Info<BackgroundTaskRunner<T>>("Thread {0} was cancelled before it got started.", () => Thread.CurrentThread.ManagedThreadId);
token.ThrowIfCancellationRequested();
}
TakeAndConsumeTask(token);
}
/// <summary>
/// Trys to get a task from the queue, if there isn't one it will wait a second and try again
/// </summary>
/// <param name="token"></param>
private void TakeAndConsumeTask(CancellationToken token)
private bool TaskSourceCanceled(TaskCompletionSource<object> taskSource, CancellationToken token)
{
if (token.IsCancellationRequested)
{
LogHelper.Info<BackgroundTaskRunner<T>>("Thread {0} was cancelled.", () => Thread.CurrentThread.ManagedThreadId);
token.ThrowIfCancellationRequested();
taskSource.SetCanceled();
return true;
}
return false;
}
//If this is true, the thread will stay alive and just wait until there is anything in the queue
// and process it. When there is nothing in the queue, the thread will just block until there is
// something to process.
//When this is false, the thread will process what is currently in the queue and once that is
// done, the thread will end and we will shutdown the process
if (_persistentThread)
{
//This will iterate over the collection, if there is nothing to take
// the thread will block until there is something available.
//We need to pass our cancellation token so that the thread will
// cancel when we shutdown
foreach (var t in _tasks.GetConsumingEnumerable(token))
{
ConsumeTaskCancellable(t, token);
}
//recurse and keep going
TakeAndConsumeTask(token);
}
private void TaskSourceCompleted(TaskCompletionSource<object> taskSource, CancellationToken token)
{
if (token.IsCancellationRequested)
taskSource.SetCanceled();
else
{
T repositoryTask;
while (_tasks.TryTake(out repositoryTask))
{
ConsumeTaskCancellable(repositoryTask, token);
}
//the task will end here
}
taskSource.SetResult(null);
}
internal void ConsumeTaskCancellable(T task, CancellationToken token)
{
if (token.IsCancellationRequested)
{
OnTaskCancelled(new TaskEventArgs<T>(task));
//NOTE: Since the task hasn't started this is pretty pointless so leaving it out.
LogHelper.Info<BackgroundTaskRunner<T>>("Task {0}) was cancelled.",
() => task.GetType());
token.ThrowIfCancellationRequested();
}
ConsumeTaskInternal(task);
}
private void ConsumeTaskInternal(T task)
/// <summary>
/// Runs a background task asynchronously.
/// </summary>
/// <param name="bgTask">The background task.</param>
/// <param name="token">A cancellation token.</param>
/// <returns>The asynchronous operation.</returns>
internal async Task RunIBackgroundTaskAsync(T bgTask, CancellationToken token)
{
try
{
OnTaskStarting(new TaskEventArgs<T>(task));
OnTaskStarting(new TaskEventArgs<T>(bgTask));
try
{
using (task)
using (bgTask) // ensure it's disposed
{
task.Run();
if (bgTask.IsAsync)
//configure await = false since we don't care about the context, we're on a background thread.
await bgTask.RunAsync(token).ConfigureAwait(false);
else
bgTask.Run();
}
}
catch (Exception e)
{
OnTaskError(new TaskEventArgs<T>(task, e));
OnTaskError(new TaskEventArgs<T>(bgTask, e));
throw;
}
OnTaskCompleted(new TaskEventArgs<T>(task));
OnTaskCompleted(new TaskEventArgs<T>(bgTask));
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("An error occurred consuming task", ex);
}
LogHelper.Error<BackgroundTaskRunner<T>>("Task has failed.", ex);
}
}
#region Events
protected virtual void OnTaskError(TaskEventArgs<T> e)
{
var handler = TaskError;
@@ -305,23 +409,74 @@ namespace Umbraco.Web.Scheduling
protected virtual void OnTaskStarting(TaskEventArgs<T> e)
{
var handler = TaskStarting;
if (handler != null) handler(this, e);
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskStarting exception occurred", ex);
}
}
}
protected virtual void OnTaskCompleted(TaskEventArgs<T> e)
{
var handler = TaskCompleted;
if (handler != null) handler(this, e);
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskCompleted exception occurred", ex);
}
}
}
protected virtual void OnTaskCancelled(TaskEventArgs<T> e)
{
var handler = TaskCancelled;
if (handler != null) handler(this, e);
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskCancelled exception occurred", ex);
}
}
//dispose it
e.Task.Dispose();
}
protected virtual void OnCompleted()
{
var handler = Completed;
if (handler != null)
{
try
{
handler(this, EventArgs.Empty);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("OnCompleted exception occurred", ex);
}
}
}
#endregion
#region IDisposable
#region Disposal
private readonly object _disposalLocker = new object();
public bool IsDisposed { get; private set; }
@@ -338,8 +493,9 @@ namespace Umbraco.Web.Scheduling
protected virtual void Dispose(bool disposing)
{
if (this.IsDisposed || !disposing)
if (this.IsDisposed || disposing == false)
return;
lock (_disposalLocker)
{
if (IsDisposed)
@@ -351,31 +507,62 @@ namespace Umbraco.Web.Scheduling
protected virtual void DisposeResources()
{
ShutDown();
// just make sure we eventually go down
Shutdown(true, false);
}
#endregion
/// <summary>
/// Requests a registered object to unregister.
/// </summary>
/// <param name="immediate">true to indicate the registered object should unregister from the hosting
/// environment before returning; otherwise, false.</param>
/// <remarks>
/// <para>"When the application manager needs to stop a registered object, it will call the Stop method."</para>
/// <para>The application manager will call the Stop method to ask a registered object to unregister. During
/// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method.</para>
/// </remarks>
public void Stop(bool immediate)
{
if (immediate == false)
{
LogHelper.Debug<BackgroundTaskRunner<T>>("Application is shutting down, waiting for tasks to complete");
Dispose();
// The Stop method is first called with the immediate parameter set to false. The object can either complete
// processing, call the UnregisterObject method, and then return or it can return immediately and complete
// processing asynchronously before calling the UnregisterObject method.
LogHelper.Debug<BackgroundTaskRunner<T>>("Shutting down, waiting for tasks to complete.");
Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait
lock (_locker)
{
if (_runningTask != null)
_runningTask.ContinueWith(_ =>
{
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down, tasks completed.");
});
else
{
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down, tasks completed.");
}
}
}
else
{
//NOTE: this will thread block the current operation if the manager
// is still shutting down because the Shutdown operation is also locked
// by this same lock instance. This would only matter if Stop is called by ASP.Net
// on two different threads though, otherwise the current thread will just block normally
// until the app is shutdown
lock (Locker)
{
LogHelper.Info<BackgroundTaskRunner<T>>("Application is shutting down immediately");
}
}
// If the registered object does not complete processing before the application manager's time-out
// period expires, the Stop method is called again with the immediate parameter set to true. When the
// immediate parameter is true, the registered object must call the UnregisterObject method before returning;
// otherwise, its registration will be removed by the application manager.
LogHelper.Info<BackgroundTaskRunner<T>>("Shutting down immediately.");
Shutdown(true, true); // cancel all tasks, wait for the current one to end
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down.");
}
}
}
}
@@ -0,0 +1,78 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Custom awaiter used to await when the BackgroundTaskRunner is completed (IsRunning == false)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// This custom awaiter simply uses a TaskCompletionSource to set the result when the Completed event of the
/// BackgroundTaskRunner executes.
/// A custom awaiter requires implementing INotifyCompletion as well as IsCompleted, OnCompleted and GetResult
/// see: http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx
/// </remarks>
internal class BackgroundTaskRunnerAwaiter<T> : INotifyCompletion where T : class, IBackgroundTask
{
private readonly BackgroundTaskRunner<T> _runner;
private readonly TaskCompletionSource<int> _tcs;
private readonly TaskAwaiter<int> _awaiter;
public BackgroundTaskRunnerAwaiter(BackgroundTaskRunner<T> runner)
{
if (runner == null) throw new ArgumentNullException("runner");
_runner = runner;
_tcs = new TaskCompletionSource<int>();
_awaiter = _tcs.Task.GetAwaiter();
if (_runner.IsRunning)
{
_runner.Completed += (s, e) =>
{
LogHelper.Debug<BackgroundTaskRunnerAwaiter<T>>("Setting result");
_tcs.SetResult(0);
};
}
else
{
//not running, just set the result
_tcs.SetResult(0);
}
}
public BackgroundTaskRunnerAwaiter<T> GetAwaiter()
{
return this;
}
/// <summary>
/// This is completed when the runner is finished running
/// </summary>
public bool IsCompleted
{
get
{
LogHelper.Debug<BackgroundTaskRunnerAwaiter<T>>("IsCompleted :: " + _tcs.Task.IsCompleted + ", " + (_runner.IsRunning == false));
//Need to check if the task is completed because it might already be done on the ctor and the runner never runs
return _tcs.Task.IsCompleted || _runner.IsRunning == false;
}
}
public void OnCompleted(Action continuation)
{
_awaiter.OnCompleted(continuation);
}
public void GetResult()
{
_awaiter.GetResult();
}
}
}
@@ -0,0 +1,44 @@
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Provides options to the <see cref="BackgroundTaskRunner{T}"/> class.
/// </summary>
internal class BackgroundTaskRunnerOptions
{
//TODO: Could add options for using a stack vs queue if required
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunnerOptions"/> class.
/// </summary>
public BackgroundTaskRunnerOptions()
{
LongRunning = false;
KeepAlive = false;
AutoStart = false;
}
/// <summary>
/// Gets or sets a value indicating whether the running task should be a long-running,
/// coarse grained operation.
/// </summary>
public bool LongRunning { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the running task should block and wait
/// on the queue, or end, when the queue is empty.
/// </summary>
public bool KeepAlive { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the running task should start immediately
/// or only once a task has been added to the queue.
/// </summary>
public bool AutoStart { get; set; }
/// <summary>
/// Gets or setes a value indicating whether the running task should be preserved
/// once completed, or reset to null. For unit tests.
/// </summary>
public bool PreserveRunningTask { get; set; }
}
}
@@ -0,0 +1,74 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Provides a base class for recurring background tasks.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
internal abstract class DelayedRecurringTaskBase<T> : RecurringTaskBase<T>, ILatchedBackgroundTask
where T : class, IBackgroundTask
{
private readonly ManualResetEventSlim _latch;
private Timer _timer;
protected DelayedRecurringTaskBase(IBackgroundTaskRunner<T> runner, int delayMilliseconds, int periodMilliseconds)
: base(runner, periodMilliseconds)
{
if (delayMilliseconds > 0)
{
_latch = new ManualResetEventSlim(false);
_timer = new Timer(_ =>
{
_timer.Dispose();
_timer = null;
_latch.Set();
});
_timer.Change(delayMilliseconds, 0);
}
}
protected DelayedRecurringTaskBase(DelayedRecurringTaskBase<T> source)
: base(source)
{
// no latch on recurring instances
_latch = null;
}
public override void Run()
{
if (_latch != null)
_latch.Dispose();
base.Run();
}
public override async Task RunAsync(CancellationToken token)
{
if (_latch != null)
_latch.Dispose();
await base.RunAsync(token);
}
public WaitHandle Latch
{
get
{
if (_latch == null)
throw new InvalidOperationException("The task is not latched.");
return _latch.WaitHandle;
}
}
public bool IsLatched
{
get { return _latch != null; }
}
public virtual bool RunsOnShutdown
{
get { return true; }
}
}
}
@@ -1,9 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Represents a background task.
/// </summary>
internal interface IBackgroundTask : IDisposable
{
/// <summary>
/// Runs the background task.
/// </summary>
void Run();
/// <summary>
/// Runs the task asynchronously.
/// </summary>
/// <param name="token">A cancellation token.</param>
/// <returns>A <see cref="Task"/> instance representing the execution of the background task.</returns>
/// <exception cref="NotImplementedException">The background task cannot run asynchronously.</exception>
Task RunAsync(CancellationToken token);
/// <summary>
/// Indicates whether the background task can run asynchronously.
/// </summary>
bool IsAsync { get; }
}
}
@@ -0,0 +1,20 @@
using System;
using System.Web.Hosting;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Defines a service managing a queue of tasks of type <typeparamref name="T"/> and running them in the background.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
/// <remarks>The interface is not complete and exists only to have the contravariance on T.</remarks>
internal interface IBackgroundTaskRunner<in T> : IDisposable, IRegisteredObject
where T : class, IBackgroundTask
{
bool IsCompleted { get; }
void Add(T task);
bool TryAdd(T task);
// fixme - complete the interface?
}
}
@@ -0,0 +1,31 @@
using System;
using System.Threading;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Represents a latched background task.
/// </summary>
/// <remarks>Latched background tasks can suspend their execution until
/// a condition is met. However if the tasks runner has to terminate,
/// latched background tasks can be executed immediately, depending on
/// the value returned by RunsOnShutdown.</remarks>
internal interface ILatchedBackgroundTask : IBackgroundTask
{
/// <summary>
/// Gets a wait handle on the task condition.
/// </summary>
/// <exception cref="InvalidOperationException">The task is not latched.</exception>
WaitHandle Latch { get; }
/// <summary>
/// Gets a value indicating whether the task is latched.
/// </summary>
bool IsLatched { get; }
/// <summary>
/// Gets a value indicating whether the task can be executed immediately if the task runner has to terminate.
/// </summary>
bool RunsOnShutdown { get; }
}
}
+46 -8
View File
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using System.Web;
using System.Web.Caching;
using umbraco.BusinessLogic;
@@ -8,17 +9,31 @@ using Umbraco.Core.Logging;
namespace Umbraco.Web.Scheduling
{
internal class LogScrubber : DisposableObject, IBackgroundTask
internal class LogScrubber : DelayedRecurringTaskBase<LogScrubber>
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
public LogScrubber(ApplicationContext appContext, IUmbracoSettingsSection settings)
public LogScrubber(IBackgroundTaskRunner<LogScrubber> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
_appContext = appContext;
_settings = settings;
}
public LogScrubber(LogScrubber source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
protected override LogScrubber GetRecurring()
{
return new LogScrubber(this);
}
private int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings)
{
int maximumAge = 24 * 60 * 60;
@@ -35,19 +50,42 @@ namespace Umbraco.Web.Scheduling
}
/// <summary>
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
/// </summary>
protected override void DisposeResources()
{
public static int GetLogScrubbingInterval(IUmbracoSettingsSection settings)
{
var interval = 4 * 60 * 60 * 1000; // 4 hours, in milliseconds
try
{
if (settings.Logging.CleaningMiliseconds > -1)
interval = settings.Logging.CleaningMiliseconds;
}
catch (Exception e)
{
LogHelper.Error<LogScrubber>("Unable to locate a log scrubbing interval. Defaulting to 4 hours.", e);
}
return interval;
}
public void Run()
public override void PerformRun()
{
using (DisposableTimer.DebugDuration<LogScrubber>(() => "Log scrubbing executing", () => "Log scrubbing complete"))
{
Log.CleanLogs(GetLogScrubbingMaximumAge(_settings));
}
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
}
public override bool IsAsync
{
get { return false; }
}
public override bool RunsOnShutdown
{
get { return false; }
}
}
}
@@ -0,0 +1,115 @@
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Provides a base class for recurring background tasks.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
internal abstract class RecurringTaskBase<T> : IBackgroundTask
where T : class, IBackgroundTask
{
private readonly IBackgroundTaskRunner<T> _runner;
private readonly int _periodMilliseconds;
private Timer _timer;
private T _recurrent;
/// <summary>
/// Initializes a new instance of the <see cref="RecurringTaskBase{T}"/> class with a tasks runner and a period.
/// </summary>
/// <param name="runner">The task runner.</param>
/// <param name="periodMilliseconds">The period.</param>
/// <remarks>The task will repeat itself periodically. Use this constructor to create a new task.</remarks>
protected RecurringTaskBase(IBackgroundTaskRunner<T> runner, int periodMilliseconds)
{
_runner = runner;
_periodMilliseconds = periodMilliseconds;
}
/// <summary>
/// Initializes a new instance of the <see cref="RecurringTaskBase{T}"/> class with a source task.
/// </summary>
/// <param name="source">The source task.</param>
/// <remarks>Use this constructor to create a new task from a source task in <c>GetRecurring</c>.</remarks>
protected RecurringTaskBase(RecurringTaskBase<T> source)
{
_runner = source._runner;
_timer = source._timer;
_periodMilliseconds = source._periodMilliseconds;
}
/// <summary>
/// Implements IBackgroundTask.Run().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public virtual void Run()
{
PerformRun();
Repeat();
}
/// <summary>
/// Implements IBackgroundTask.RunAsync().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public virtual async Task RunAsync(CancellationToken token)
{
await PerformRunAsync();
Repeat();
}
private void Repeat()
{
// again?
if (_runner.IsCompleted) return; // fail fast
if (_periodMilliseconds == 0) return;
_recurrent = GetRecurring();
if (_recurrent == null)
{
_timer.Dispose();
_timer = null;
return; // done
}
// note
// must use the single-parameter constructor on Timer to avoid it from being GC'd
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
_timer = _timer ?? new Timer(_ => _runner.TryAdd(_recurrent));
_timer.Change(_periodMilliseconds, 0);
}
/// <summary>
/// Indicates whether the background task can run asynchronously.
/// </summary>
public abstract bool IsAsync { get; }
/// <summary>
/// Runs the background task.
/// </summary>
public abstract void PerformRun();
/// <summary>
/// Runs the task asynchronously.
/// </summary>
/// <returns>A <see cref="Task"/> instance representing the execution of the background task.</returns>
public abstract Task PerformRunAsync();
/// <summary>
/// Gets a new occurence of the recurring task.
/// </summary>
/// <returns>A new task instance to be queued, or <c>null</c> to terminate the recurring task.</returns>
/// <remarks>The new task instance must be created via the <c>RecurringTaskBase(RecurringTaskBase{T} source)</c> constructor,
/// where <c>source</c> is the current task, eg: <c>return new MyTask(this);</c></remarks>
protected abstract T GetRecurring();
/// <summary>
/// Dispose the task.
/// </summary>
public virtual void Dispose()
{ }
}
}
@@ -2,6 +2,7 @@ using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -11,30 +12,41 @@ using Umbraco.Web.Mvc;
namespace Umbraco.Web.Scheduling
{
internal class ScheduledPublishing : DisposableObject, IBackgroundTask
internal class ScheduledPublishing : DelayedRecurringTaskBase<ScheduledPublishing>
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
private static bool _isPublishingRunning = false;
private static bool _isPublishingRunning;
public ScheduledPublishing(ApplicationContext appContext, IUmbracoSettingsSection settings)
public ScheduledPublishing(IBackgroundTaskRunner<ScheduledPublishing> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
_appContext = appContext;
_settings = settings;
}
/// <summary>
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
/// </summary>
protected override void DisposeResources()
private ScheduledPublishing(ScheduledPublishing source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
public void Run()
protected override ScheduledPublishing GetRecurring()
{
return new ScheduledPublishing(this);
}
public override void PerformRun()
{
if (_appContext == null) return;
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<ScheduledPublishing>("Does not run on slave servers.");
return;
}
using (DisposableTimer.DebugDuration<ScheduledPublishing>(() => "Scheduled publishing executing", () => "Scheduled publishing complete"))
{
@@ -75,5 +87,20 @@ namespace Umbraco.Web.Scheduling
}
}
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
}
public override bool IsAsync
{
get { return false; }
}
public override bool RunsOnShutdown
{
get { return false; }
}
}
}
+38 -9
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Xml;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
@@ -16,19 +17,33 @@ namespace Umbraco.Web.Scheduling
// would need to be a publicly available task (URL) which isn't really very good :(
// We should really be using the AdminTokenAuthorizeAttribute for this stuff
internal class ScheduledTasks : DisposableObject, IBackgroundTask
internal class ScheduledTasks : DelayedRecurringTaskBase<ScheduledTasks>
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
private static readonly Hashtable ScheduledTaskTimes = new Hashtable();
private static bool _isPublishingRunning = false;
public ScheduledTasks(ApplicationContext appContext, IUmbracoSettingsSection settings)
public ScheduledTasks(IBackgroundTaskRunner<ScheduledTasks> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
_appContext = appContext;
_settings = settings;
}
public ScheduledTasks(ScheduledTasks source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
protected override ScheduledTasks GetRecurring()
{
return new ScheduledTasks(this);
}
private void ProcessTasks()
{
var scheduledTasks = _settings.ScheduledTasks.Tasks;
@@ -77,15 +92,14 @@ namespace Umbraco.Web.Scheduling
return false;
}
/// <summary>
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
/// </summary>
protected override void DisposeResources()
public override void PerformRun()
{
}
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<ScheduledTasks>("Does not run on slave servers.");
return;
}
public void Run()
{
using (DisposableTimer.DebugDuration<ScheduledTasks>(() => "Scheduled tasks executing", () => "Scheduled tasks complete"))
{
if (_isPublishingRunning) return;
@@ -106,5 +120,20 @@ namespace Umbraco.Web.Scheduling
}
}
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
}
public override bool IsAsync
{
get { return false; }
}
public override bool RunsOnShutdown
{
get { return false; }
}
}
}
+22 -84
View File
@@ -19,12 +19,10 @@ namespace Umbraco.Web.Scheduling
internal sealed class Scheduler : ApplicationEventHandler
{
private static Timer _pingTimer;
private static Timer _schedulingTimer;
private static BackgroundTaskRunner<ScheduledPublishing> _publishingRunner;
private static BackgroundTaskRunner<ScheduledTasks> _tasksRunner;
private static BackgroundTaskRunner<LogScrubber> _scrubberRunner;
private static Timer _logScrubberTimer;
private static volatile bool _started = false;
private static BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
private static BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
private static BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
private static volatile bool _started;
private static readonly object Locker = new object();
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
@@ -49,97 +47,37 @@ namespace Umbraco.Web.Scheduling
_started = true;
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
// time to setup the tasks
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>();
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>();
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>();
//We have 3 background runners that are web aware, if the app domain dies, these tasks will wind down correctly
_publishingRunner = new BackgroundTaskRunner<ScheduledPublishing>();
_tasksRunner = new BackgroundTaskRunner<ScheduledTasks>();
_scrubberRunner = new BackgroundTaskRunner<LogScrubber>();
var settings = UmbracoConfig.For.UmbracoSettings();
//NOTE: It is important to note that we need to use the ctor for a timer without the 'state' object specified, this is in order
// to ensure that the timer itself is not GC'd since internally .net will pass itself in as the state object and that will keep it alive.
// There's references to this here: http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
// we also make these timers static to ensure further GC safety.
// note
// must use the single-parameter constructor on Timer to avoid it from being GC'd
// also make the timer static to ensure further GC safety
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
// ping/keepalive - NOTE: we don't use a background runner for this because it does not need to be web aware, if the app domain dies, no problem
// ping/keepalive - no need for a background runner - does not need to be web aware, ok if the app domain dies
_pingTimer = new Timer(state => KeepAlive.Start(applicationContext, UmbracoConfig.For.UmbracoSettings()));
_pingTimer.Change(60000, 300000);
// scheduled publishing/unpublishing
_schedulingTimer = new Timer(state => PerformScheduling(applicationContext, UmbracoConfig.For.UmbracoSettings()));
_schedulingTimer.Change(60000, 60000);
// install on all, will only run on non-slaves servers
// both are delayed recurring tasks
_publishingRunner.Add(new ScheduledPublishing(_publishingRunner, 60000, 60000, applicationContext, settings));
_tasksRunner.Add(new ScheduledTasks(_tasksRunner, 60000, 60000, applicationContext, settings));
//log scrubbing
_logScrubberTimer = new Timer(state => PerformLogScrub(applicationContext, UmbracoConfig.For.UmbracoSettings()));
_logScrubberTimer.Change(60000, GetLogScrubbingInterval(UmbracoConfig.For.UmbracoSettings()));
// log scrubbing
// install & run on all servers
// LogScrubber is a delayed recurring task
_scrubberRunner.Add(new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), applicationContext, settings));
}
}
}
};
};
}
private int GetLogScrubbingInterval(IUmbracoSettingsSection settings)
{
var interval = 4 * 60 * 60 * 1000; // 4 hours, in milliseconds
try
{
if (settings.Logging.CleaningMiliseconds > -1)
interval = settings.Logging.CleaningMiliseconds;
}
catch (Exception e)
{
LogHelper.Error<Scheduler>("Unable to locate a log scrubbing interval. Defaulting to 4 hours.", e);
}
return interval;
}
private static void PerformLogScrub(ApplicationContext appContext, IUmbracoSettingsSection settings)
{
_scrubberRunner.Add(new LogScrubber(appContext, settings));
}
/// <summary>
/// This performs all of the scheduling on the one timer
/// </summary>
/// <param name="appContext"></param>
/// <param name="settings"></param>
/// <remarks>
/// No processing will be done if this server is a slave
/// </remarks>
private static void PerformScheduling(ApplicationContext appContext, IUmbracoSettingsSection settings)
{
using (DisposableTimer.DebugDuration<Scheduler>(() => "Scheduling interval executing", () => "Scheduling interval complete"))
{
//get the current server status to see if this server should execute the scheduled publishing
var serverStatus = ServerEnvironmentHelper.GetStatus(settings);
switch (serverStatus)
{
case CurrentServerEnvironmentStatus.Single:
case CurrentServerEnvironmentStatus.Master:
case CurrentServerEnvironmentStatus.Unknown:
//if it's a single server install, a master or it cannot be determined
// then we will process the scheduling
//do the scheduled publishing
_publishingRunner.Add(new ScheduledPublishing(appContext, settings));
//do the scheduled tasks
_tasksRunner.Add(new ScheduledTasks(appContext, settings));
break;
case CurrentServerEnvironmentStatus.Slave:
//do not process
LogHelper.Debug<Scheduler>(
() => string.Format("Current server ({0}) detected as a slave, no scheduled processes will execute on this server", NetworkHelper.MachineName));
break;
}
}
}
}
}

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