Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ffb73e467 | |||
| 57c3665d0f | |||
| 8037f929aa | |||
| fec3e57ed6 | |||
| 5106ffab97 | |||
| 9f569171c3 | |||
| d607041ca4 | |||
| b3daf7f55d | |||
| a77d3bc4ac | |||
| c4aa87d73d | |||
| 57909489cb | |||
| c78b371965 | |||
| c525be3e44 | |||
| cc804bd3bd | |||
| 24dc8309f1 | |||
| ce81f9b1b1 | |||
| 3f70761d49 | |||
| dee45146a4 | |||
| b35397b464 | |||
| 147cc92ac5 | |||
| 3230fce76a | |||
| 1d22c1e81e | |||
| ece2e2359e | |||
| 8cbe53f49d | |||
| cac3ddc3c8 | |||
| 0eac50ceba | |||
| d5e735ac14 | |||
| 8d841d3537 | |||
| 5f175874b2 | |||
| fa053426c3 | |||
| 437e7f0ffc | |||
| afa6c35bb5 | |||
| 8d00dbf648 | |||
| cd26a1ad21 | |||
| 437ab4aaf1 | |||
| 051e9366e5 | |||
| 46c6503320 | |||
| 1cf2fc7870 | |||
| cf1c38dcaa | |||
| 73d7cd4856 | |||
| 1b87fd074c | |||
| 33f21e0528 | |||
| 0b5a2452c2 | |||
| cc8962f090 | |||
| f6b9fdba72 | |||
| e0effec3b4 | |||
| 76a2f433d9 | |||
| a2bfca4d3a | |||
| fb24becd0d | |||
| 58857c5d83 | |||
| 7a31d6cae3 | |||
| 021bb6d367 | |||
| 0742108ec1 | |||
| 1c3a7b95fc | |||
| fda72b2acc | |||
| ec742d1f66 | |||
| 4bdac1475f | |||
| 58ce04e26b | |||
| 4c3de920c6 | |||
| be37026587 | |||
| a73b7a5849 | |||
| b7436dc55f | |||
| bc068b201d | |||
| 912b01c9aa | |||
| a82035061c | |||
| 0c107fd869 | |||
| 6419f301f7 | |||
| e775471013 | |||
| cc109d1ed0 | |||
| 9dfccd86fd | |||
| ffaf1f96b9 | |||
| aa3c1d645b | |||
| c6b393f2e8 | |||
| 344d909e6b | |||
| e3925f18ef | |||
| 53a4d063e0 | |||
| 907e5e7ca4 | |||
| 94e281e951 | |||
| 8c4eba7837 | |||
| 08a2ec6883 | |||
| b02e0c0232 | |||
| fff0151b62 | |||
| 90fdbb007e |
+1
-2
@@ -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
@@ -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 "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
|
||||
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyFileVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyFileVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the copyright year-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyCopyright\("Copyright © Umbraco (\d{4})"\)"
|
||||
ReplacementText="AssemblyCopyright("Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))")"/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,13 +0,0 @@
|
||||
Umbraco CMS version 4.5 is licensed under the OSI approved MIT License:
|
||||
|
||||
Copyright (c) 2007-2010 umbraco I/S.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Umbraco versions prior to 4.5 were licensed under a combination of MIT and a propriatary UI license. The license history can be found here:
|
||||
http://umbraco.codeplex.com/license
|
||||
@@ -1,33 +0,0 @@
|
||||
Umbraco - the simple, flexible and friendly ASP.NET CMS
|
||||
=======================================================
|
||||
For the first time on the Microsoft platform a free user and developer friendly cms that makes it quick and easy to create websites - or a breeze to build complex web applications. umbraco got award-winning integration capabilities and supports your ASP.NET User and Custom Controls out of the box. It's a developers dream and your users will love it too. Used by more than 57.000 active websites including Heinz.com, Peugeot.com, NAIAS.com and Microsofts documentinteropinitiative.org website you can be sure that the technology is proven, stable and scales.
|
||||
|
||||
More info at http://umbraco.com
|
||||
|
||||
Exploring the repository
|
||||
========================
|
||||
Most contributors will work on their own fork and should pick the branch in which they need their fix. For more information see:
|
||||
http://our.umbraco.org/contribute/guidelines-for-core-contribution
|
||||
|
||||
Exploring the source
|
||||
====================
|
||||
The Umbraco source code is never required for you to start building sites. If you are not using the source code for debugging purposes or to contribute to the source then please download the binaries listed at http://umbraco.codeplex.com
|
||||
|
||||
With that said, the Umbraco solution can be opened in Visual Studio 2010 with SP1 installed or Visual Studio 2012.
|
||||
|
||||
Contributing
|
||||
============
|
||||
Umbraco is Open Source which means you can contribute to make it great!
|
||||
Read all about it at http://our.umbraco.org/contribute
|
||||
|
||||
Forums
|
||||
======
|
||||
We have a forum running on http://our.umbraco.org for friendly community support.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
Our documentation section provides help on installing and using Umbraco including API reference documentation: http://our.umbraco.org/documentation
|
||||
|
||||
Submitting Issues
|
||||
=================
|
||||
Another way you can contribute to Umbraco is by providing issue reports, for information on how to submit an issue report refer to our online guide at http://our.umbraco.org/contribute/report-an-issue-or-request-a-feature
|
||||
+6
-3
@@ -1,8 +1,8 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2012")]
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -10,3 +10,6 @@ using System.Resources;
|
||||
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.2.5")]
|
||||
[assembly: AssemblyInformationalVersion("7.2.5-RC")]
|
||||
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
|
||||
//
|
||||
// notes:
|
||||
// - this is NOT a reader/writer lock
|
||||
// - this is NOT a recursive lock
|
||||
//
|
||||
internal class AsyncLock
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
private readonly Semaphore _semaphore2;
|
||||
private readonly IDisposable _releaser;
|
||||
private readonly Task<IDisposable> _releaserTask;
|
||||
|
||||
public AsyncLock()
|
||||
: this (null)
|
||||
{ }
|
||||
|
||||
public AsyncLock(string name)
|
||||
{
|
||||
// WaitOne() waits until count > 0 then decrements count
|
||||
// Release() increments count
|
||||
// initial count: the initial count value
|
||||
// maximum count: the max value of count, and then Release() throws
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
// anonymous semaphore
|
||||
// use one unique releaser, that will not release the semaphore when finalized
|
||||
// because the semaphore is destroyed anyway if the app goes down
|
||||
|
||||
_semaphore = new SemaphoreSlim(1, 1); // create a local (to the app domain) semaphore
|
||||
_releaser = new SemaphoreSlimReleaser(_semaphore);
|
||||
_releaserTask = Task.FromResult(_releaser);
|
||||
}
|
||||
else
|
||||
{
|
||||
// named semaphore
|
||||
// use dedicated releasers, that will release the semaphore when finalized
|
||||
// because the semaphore is system-wide and we cannot leak counts
|
||||
|
||||
_semaphore2 = new Semaphore(1, 1, name); // create a system-wide named semaphore
|
||||
}
|
||||
}
|
||||
|
||||
private IDisposable CreateReleaser()
|
||||
{
|
||||
// for anonymous semaphore, use the unique releaser, else create a new one
|
||||
return _semaphore != null
|
||||
? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore)
|
||||
: (IDisposable)new NamedSemaphoreReleaser(_semaphore2);
|
||||
}
|
||||
|
||||
public Task<IDisposable> LockAsync()
|
||||
{
|
||||
var wait = _semaphore != null
|
||||
? _semaphore.WaitAsync()
|
||||
: WaitOneAsync(_semaphore2);
|
||||
|
||||
return wait.IsCompleted
|
||||
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
|
||||
: wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
|
||||
this, CancellationToken.None,
|
||||
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public IDisposable Lock()
|
||||
{
|
||||
if (_semaphore != null)
|
||||
_semaphore.Wait();
|
||||
else
|
||||
_semaphore2.WaitOne();
|
||||
return _releaser ?? CreateReleaser(); // anonymous vs named
|
||||
}
|
||||
|
||||
public IDisposable Lock(int millisecondsTimeout)
|
||||
{
|
||||
var entered = _semaphore != null
|
||||
? _semaphore.Wait(millisecondsTimeout)
|
||||
: _semaphore2.WaitOne(millisecondsTimeout);
|
||||
if (entered == false)
|
||||
throw new TimeoutException("Failed to enter the lock within timeout.");
|
||||
return _releaser ?? CreateReleaser(); // anonymous vs named
|
||||
}
|
||||
|
||||
// note - before making those classes some structs, read
|
||||
// about "impure methods" and mutating readonly structs...
|
||||
|
||||
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
|
||||
{
|
||||
private readonly Semaphore _semaphore;
|
||||
|
||||
internal NamedSemaphoreReleaser(Semaphore semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// critical
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
// we WANT to release the semaphore because it's a system object
|
||||
// ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
|
||||
// which means that the finalizer "should" run in all situations
|
||||
|
||||
// however... that can fail with System.ObjectDisposedException because the
|
||||
// underlying handle was closed... because we cannot guarantee that the semaphore
|
||||
// is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
|
||||
// which should keep it around and then we free the handle?
|
||||
|
||||
// so... I'm not sure this is safe really...
|
||||
|
||||
~NamedSemaphoreReleaser()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
|
||||
private class SemaphoreSlimReleaser : IDisposable
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
|
||||
internal SemaphoreSlimReleaser(SemaphoreSlim semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// normal
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
~SemaphoreSlimReleaser()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
|
||||
// http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
|
||||
// F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
|
||||
// version below should be OK
|
||||
|
||||
private static Task WaitOneAsync(WaitHandle handle)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var callbackHandleInitLock = new object();
|
||||
lock (callbackHandleInitLock)
|
||||
{
|
||||
RegisteredWaitHandle callbackHandle = null;
|
||||
// ReSharper disable once RedundantAssignment
|
||||
callbackHandle = ThreadPool.RegisterWaitForSingleObject(
|
||||
handle,
|
||||
(state, timedOut) =>
|
||||
{
|
||||
tcs.SetResult(null);
|
||||
|
||||
// we take a lock here to make sure the outer method has completed setting the local variable callbackHandle.
|
||||
lock (callbackHandleInitLock)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
callbackHandle.Unregister(null);
|
||||
}
|
||||
},
|
||||
/*state:*/ null,
|
||||
/*millisecondsTimeOutInterval:*/ Timeout.Infinite,
|
||||
/*executeOnlyOnce:*/ true);
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -16,12 +16,14 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
|
||||
private readonly object _addLocker = new object();
|
||||
internal Action OnAdd;
|
||||
internal Func<Property, bool> ValidateAdd { get; set; }
|
||||
|
||||
internal PropertyCollection()
|
||||
{}
|
||||
: base(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyCollection"/> class with a delegate responsible for validating the addition of <see cref="Property"/> instances.
|
||||
@@ -29,11 +31,13 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="validationCallback">The validation callback.</param>
|
||||
/// <remarks></remarks>
|
||||
internal PropertyCollection(Func<Property, bool> validationCallback)
|
||||
: this()
|
||||
{
|
||||
ValidateAdd = validationCallback;
|
||||
}
|
||||
|
||||
public PropertyCollection(IEnumerable<Property> properties)
|
||||
: this()
|
||||
{
|
||||
Reset(properties);
|
||||
}
|
||||
@@ -78,7 +82,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
internal new void Add(Property item)
|
||||
{
|
||||
using (new WriteLock(_addLocker))
|
||||
lock (_addLocker)
|
||||
{
|
||||
var key = GetKeyForItem(item);
|
||||
if (key != null)
|
||||
@@ -113,14 +117,14 @@ namespace Umbraco.Core.Models
|
||||
/// <remarks></remarks>
|
||||
public new bool Contains(string propertyTypeAlias)
|
||||
{
|
||||
return this.Any(x => x.Alias == propertyTypeAlias);
|
||||
return base.Contains(propertyTypeAlias);
|
||||
}
|
||||
|
||||
public int IndexOfKey(string key)
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (this[i].Alias == key)
|
||||
if (this[i].Alias.InvariantEquals(key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
@@ -145,7 +149,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.FirstOrDefault(x => x.Alias == propertyType.Alias);
|
||||
return this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyType.Alias));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +169,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypes">List of PropertyType</param>
|
||||
protected internal void EnsurePropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
||||
{
|
||||
if(/*!this.Any() &&*/ propertyTypes != null)
|
||||
if (/*!this.Any() &&*/ propertyTypes != null)
|
||||
{
|
||||
foreach (var propertyType in propertyTypes)
|
||||
{
|
||||
@@ -180,7 +184,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypes">List of PropertyType</param>
|
||||
protected internal void EnsureCleanPropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
||||
{
|
||||
if(propertyTypes != null)
|
||||
if (propertyTypes != null)
|
||||
{
|
||||
//Remove PropertyTypes that doesn't exist in the list of new PropertyTypes
|
||||
var aliases = this.Select(p => p.Alias).Except(propertyTypes.Select(x => x.Alias)).ToList();
|
||||
|
||||
@@ -120,27 +120,22 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public bool Equals(PropertyGroup other)
|
||||
{
|
||||
//Check whether the compared object is null.
|
||||
if (Object.ReferenceEquals(other, null)) return false;
|
||||
|
||||
//Check whether the compared object references the same data.
|
||||
if (Object.ReferenceEquals(this, other)) return true;
|
||||
if (base.Equals(other)) return true;
|
||||
|
||||
//Check whether the PropertyGroup's properties are equal.
|
||||
return Id.Equals(other.Id) && Name.Equals(other.Name);
|
||||
return Name.InvariantEquals(other.Name);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
|
||||
//Get hash code for the Name field if it is not null.
|
||||
int hashName = Name == null ? 0 : Name.GetHashCode();
|
||||
int baseHash = base.GetHashCode();
|
||||
|
||||
//Get hash code for the Id field.
|
||||
int hashId = Id.GetHashCode();
|
||||
//Get hash code for the Alias field.
|
||||
int nameHash = Name.ToLowerInvariant().GetHashCode();
|
||||
|
||||
//Calculate the hash code for the product.
|
||||
return hashName ^ hashId;
|
||||
return baseHash ^ nameHash;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()))
|
||||
{
|
||||
|
||||
@@ -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()))
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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 = "",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 <?xml version="1.0" encoding="utf-8"?>
|
||||
///<media>
|
||||
@@ -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 <?xml version="1.0" encoding="utf-8"?>
|
||||
///<!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>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -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 });
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user