diff --git a/.gitignore b/.gitignore index 8e77a3ce1f..fd94476152 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ src/Umbraco.Web.UI/[Cc]onfig/appSettings.config src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config src/Umbraco.Web.UI/umbraco/plugins/* src/Umbraco.Web.UI/umbraco/js/init.js +build/ApiDocs/* +build/ApiDocs/Output/* \ No newline at end of file diff --git a/README.md b/README.md index 91065214ad..d4a024ddbc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ Umbraco CMS =========== -##Note: Building version 7 from source## -If you're interested in using the source code and building version 7, make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. +Umbraco is a free open source Content Management System built on the ASP.NET platform. + +## Building Umbraco from source ## +The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt dev` in `src\Umbraco.Web.UI.Client`. + +If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. ## Watch a five minute introduction video ## diff --git a/build/ApiDocs/TOC.css b/build/ApiDocs/TOC.css new file mode 100644 index 0000000000..9c32aba214 --- /dev/null +++ b/build/ApiDocs/TOC.css @@ -0,0 +1,170 @@ +/* File : TOC.css +// Author : Eric Woodruff (Eric@EWoodruff.us) +// Updated : 09/07/2007 +// +// Stylesheet for the table of content +*/ + +* +{ + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; +} + +body +{ + font-family: Segoe UI, Arial, Verdana, Helvetica, sans-serif; + font-size: 0.9em; + background-color: white; + color: White; + overflow: hidden; +} + +input +{ + padding:5px; + margin: 3px 0px 3px 0px +} + +img +{ + border: 0; + margin-left: 5px; + margin-right: 2px; +} + +img.TreeNodeImg +{ + cursor: pointer; +} + +img.TOCLink +{ + cursor: pointer; + margin-left: 0; + margin-right: 0; +} + +a.SelectedNode, a.UnselectedNode +{ + color: #333; + text-decoration: none; + padding: 1px 3px 1px 3px; + white-space: nowrap; +} + +a.SelectedNode +{ + background-color: #ffffff; + border: solid 1px #999999; + padding: 0px 2px 0px 2px; +} + +a.UnselectedNode:hover, a.SelectedNode:hover +{ + background-color: #cccccc; + border: solid 1px #999999; + padding: 0px 2px 0px 2px; +} + +.Visible +{ + display: block; + margin-left: 2em; +} + +.Hidden +{ + display: none; +} + +.Tree +{ + background-color: #fff; + color: #333; + width: 300px; + overflow: auto; +} + +.TreeNode, .TreeItem +{ + white-space: nowrap; + margin: 2px 2px 2px 2px; +} + +.TOCDiv +{ + position: relative; + float: left; + width: 300px; + height: 100%; +} + +.TOCSizer +{ + clear: none; + float: left; + width: 10px; + height: 100%; + background-image: url("Splitter.gif"); + background-position:center center; + background-repeat:no-repeat; + position: relative; + cursor: w-resize; + border-left: solid 1px #CCC; +} + +.TopicContent +{ + position: relative; + float: right; + background-color: white; + height: 100%; +} + +.SearchOpts +{ + padding: 5px 5px 10px 5px; + color: black; + width: 300px; + border-bottom: solid lightgrey 1px; + height: 110px !important; +} + +.NavOpts +{ + padding: 5px 5px 6px 5px; + color: black; + width: 300px; + border-bottom: solid lightgrey 1px; +} + +.NavOpts img { + display:inline-block; + margin: 0px 5px 0px 5px; +} + +.IndexOpts +{ + padding: 5px 5px 6px 5px; + color: black; + width: 300px; + border-bottom: solid lightgrey 1px; +} + +.IndexItem +{ + white-space: nowrap; + margin: 2px 2px 2px 2px; +} + +.IndexSubItem +{ + white-space: nowrap; + margin: 2px 2px 2px 12px; +} + +.PaddedText +{ + margin: 10px 10px 10px 10px; +} diff --git a/build/ApiDocs/csharp-api-docs.shfbproj b/build/ApiDocs/csharp-api-docs.shfbproj new file mode 100644 index 0000000000..f635f2e2ee --- /dev/null +++ b/build/ApiDocs/csharp-api-docs.shfbproj @@ -0,0 +1,172 @@ + + + + + Debug + AnyCPU + 2.0 + {cb4d85f1-7390-40ee-b41f-a724bb8fddea} + 1.9.5.0 + + Documentation + Documentation + Documentation + + .NET Framework 4.5 + .\Output\ + UmbracoClassLibrary + en-US + OnlyErrors + csharp-api-docs.log + Website + True + False + False + False + True + CSharp + Blank + False + VS2010 + False + Guid + Umbraco .Net Class Library + AboveNamespaces + Attributes, InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected + + + + + + + None + None + False + True + Summary, AutoDocumentCtors, AutoDocumentDispose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/Build.bat b/build/Build.bat index af15cd5f07..e573dde1e9 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -1,14 +1,24 @@ @ECHO OFF -SET release=7.0.1 +SET release=7.1.1 SET comment= SET version=%release% IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%) +ECHO Building Umbraco %version% ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %release% +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 install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder% + +ECHO Removing the belle build folder to make sure everything is clean as a whistle +RD ..\src\Umbraco.Web.UI.Client\build /Q /S + +ECHO Performing MSBuild and producing Umbraco binaries zip files %windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% +ECHO Adding dummy files to include in the NuGet package so that empty folders actually get created echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Code\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Data\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Plugins\dummy.txt @@ -20,9 +30,12 @@ echo This file is only here so that the containing folder will be included in th echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\Partials\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt +ECHO Adding Web.config transform files to the NuGet package ren .\_BuildOutput\WebApp\MacroScripts\Web.config Web.config.transform ren .\_BuildOutput\WebApp\Views\Web.config Web.config.transform +ren .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform +ECHO Packing the NuGet release files ..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% ..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.nuspec -Version %version% diff --git a/build/Build.proj b/build/Build.proj index 134bb668ed..2b9040dab3 100644 --- a/build/Build.proj +++ b/build/Build.proj @@ -267,7 +267,7 @@ - + @@ -310,7 +310,12 @@ - + + $(BUILD_RELEASE) + $(BUILD_RELEASE)-$(BUILD_COMMENT) + + + + + \ No newline at end of file diff --git a/build/BuildBelle.bat b/build/BuildBelle.bat new file mode 100644 index 0000000000..bb9cfedb5b --- /dev/null +++ b/build/BuildBelle.bat @@ -0,0 +1,32 @@ +@ECHO OFF +ECHO Installing Npm NuGet Package + +SET nuGetFolder=%CD%\..\src\packages\ +ECHO Configured packages folder: %nuGetFolder% +ECHO Current folder: %CD% + +%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% + +for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\" +for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\" + +ECHO Temporarily adding Npm and Node to path +SET oldPath=%PATH% + +path=%npmPath%;%nodePath%;%PATH% + +SET buildFolder=%CD% + +ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\ +CD %CD%\..\src\Umbraco.Web.UI.Client\ + +ECHO Do npm install and the grunt build of Belle +call npm install +call npm install -g grunt-cli +call grunt build + +ECHO Reset path to what it was before +path=%oldPath% + +ECHO Move back to the build folder +CD %buildFolder% \ No newline at end of file diff --git a/build/BuildDocs.ps1 b/build/BuildDocs.ps1 new file mode 100644 index 0000000000..6f46a43fde --- /dev/null +++ b/build/BuildDocs.ps1 @@ -0,0 +1,27 @@ +##We cannot continue if sandcastle is not installed determined by env variable: SHFBROOT + +if (-not (Test-Path Env:\SHFBROOT)) +{ + throw "The docs cannot be build, install Sandcastle help file builder" +} + +$PSScriptFilePath = (Get-Item $MyInvocation.MyCommand.Path).FullName +$BuildRoot = Split-Path -Path $PSScriptFilePath -Parent +$OutputPath = Join-Path -Path $BuildRoot -ChildPath "ApiDocs\Output" +$ProjFile = Join-Path -Path $BuildRoot -ChildPath "ApiDocs\csharp-api-docs.shfbproj" + +"Building docs with project file: $ProjFile" + +$MSBuild = "$Env:SYSTEMROOT\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" + +# build it! +& $MSBuild "$ProjFile" + +# remove files left over +Remove-Item $BuildRoot\* -include csharp-api-docs.shfbproj_* + +# copy our custom styles in +Copy-Item $BuildRoot\ApiDocs\TOC.css $OutputPath\TOC.css + +"" +"Done!" \ No newline at end of file diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 65a19d9089..81e0f7e369 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -4,7 +4,7 @@ UmbracoCms.Core 7.0.0 Umbraco Cms Core Binaries - Morten Christensen + Umbraco HQ Umbraco HQ http://umbraco.codeplex.com/license http://umbraco.com/ @@ -16,8 +16,8 @@ umbraco - - + + @@ -26,10 +26,12 @@ - - + + - + + + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index f93d2fdc6b..85431799e8 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -4,7 +4,7 @@ UmbracoCms 7.0.0 Umbraco Cms - Morten Christensen + Umbraco HQ Umbraco HQ http://umbraco.codeplex.com/license http://umbraco.com/ @@ -19,8 +19,6 @@ - - @@ -32,15 +30,14 @@ + - - - + + + - - diff --git a/build/NuSpecs/build/net40/UmbracoCms.props b/build/NuSpecs/build/UmbracoCms.props similarity index 97% rename from build/NuSpecs/build/net40/UmbracoCms.props rename to build/NuSpecs/build/UmbracoCms.props index 022c6a1d01..5e11945a9d 100644 --- a/build/NuSpecs/build/net40/UmbracoCms.props +++ b/build/NuSpecs/build/UmbracoCms.props @@ -1,14 +1,14 @@ - - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - + + + + + AddUmbracoFilesToOutput; + $(CopyAllFilesToSingleFolderForPackageDependsOn); + + + + AddUmbracoFilesToOutput; + $(CopyAllFilesToSingleFolderForPackageDependsOn); + + \ No newline at end of file diff --git a/build/NuSpecs/build/UmbracoCms.targets b/build/NuSpecs/build/UmbracoCms.targets new file mode 100644 index 0000000000..c196ed0bb7 --- /dev/null +++ b/build/NuSpecs/build/UmbracoCms.targets @@ -0,0 +1,50 @@ + + + + 7.1.1 + + + + ..\packages\UmbracoCms.$(UmbracoVersion)\UmbracoFiles\ + + + + + + + + + + + App_Browsers + + + App_Code + + + App_Plugins + + + bin\amd64 + + + bin\x86 + + + Config\Splashes + + + umbraco + + + umbraco_client + + + . + + + %(CustomFilesToInclude.Dir)\%(RecursiveDir)%(Filename)%(Extension) + + + + \ No newline at end of file diff --git a/build/NuSpecs/build/net40/UmbracoCms.targets b/build/NuSpecs/build/net40/UmbracoCms.targets deleted file mode 100644 index 613366722e..0000000000 --- a/build/NuSpecs/build/net40/UmbracoCms.targets +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - umbraco\%(RecursiveDir)%(Filename)%(Extension) - - - umbraco_client\%(RecursiveDir)%(Filename)%(Extension) - - - App_Browsers\%(RecursiveDir)%(Filename)%(Extension) - - - - \ No newline at end of file diff --git a/build/NuSpecs/build/net45/UmbracoCms.props b/build/NuSpecs/build/net45/UmbracoCms.props deleted file mode 100644 index 022c6a1d01..0000000000 --- a/build/NuSpecs/build/net45/UmbracoCms.props +++ /dev/null @@ -1,14 +0,0 @@ - - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - - \ No newline at end of file diff --git a/build/NuSpecs/build/net45/UmbracoCms.targets b/build/NuSpecs/build/net45/UmbracoCms.targets deleted file mode 100644 index 613366722e..0000000000 --- a/build/NuSpecs/build/net45/UmbracoCms.targets +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - umbraco\%(RecursiveDir)%(Filename)%(Extension) - - - umbraco_client\%(RecursiveDir)%(Filename)%(Extension) - - - App_Browsers\%(RecursiveDir)%(Filename)%(Extension) - - - - \ No newline at end of file diff --git a/build/NuSpecs/build/net451/UmbracoCms.props b/build/NuSpecs/build/net451/UmbracoCms.props deleted file mode 100644 index 022c6a1d01..0000000000 --- a/build/NuSpecs/build/net451/UmbracoCms.props +++ /dev/null @@ -1,14 +0,0 @@ - - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - - - AddUmbracoFilesToOutput; - $(CopyAllFilesToSingleFolderForPackageDependsOn); - - - \ No newline at end of file diff --git a/build/NuSpecs/build/net451/UmbracoCms.targets b/build/NuSpecs/build/net451/UmbracoCms.targets deleted file mode 100644 index 613366722e..0000000000 --- a/build/NuSpecs/build/net451/UmbracoCms.targets +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - umbraco\%(RecursiveDir)%(Filename)%(Extension) - - - umbraco_client\%(RecursiveDir)%(Filename)%(Extension) - - - App_Browsers\%(RecursiveDir)%(Filename)%(Extension) - - - - \ No newline at end of file diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt index 413da7442b..714423880c 100644 --- a/build/NuSpecs/tools/Readme.txt +++ b/build/NuSpecs/tools/Readme.txt @@ -1,8 +1,17 @@ A note about running Umbraco from Visual Studio. -When upgrading your website using nuget a backup of config files and web.config will be created. Only the web.config will -be overwritten by default to ensure that it has the necessary settings from the current release. This means that you'll -have to merge the files if you made any changes to the previous config files. +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 will overwrite Web.config anyway but we keep a backup in the App_Data\NuGetBackup folder. There you'll +find a folder with the current date and time in which the backup has been placed. Make sure to merge the +new file with the old backup files before you proceed. +The web.config will be overwritten by default to ensure that it has the necessary settings from the current release. + +We've also overwritten all the files in the Umbraco and Umbraco_Client folder, these have also 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. + The config files found in the config folder will usually not be changed for patch releases, so they can usually be skipped, but the web.config will have to have its previous "umbracoConfigurationStatus"-appsetting and "umbracoDbDSN" connection string copied over (as a minimum). @@ -10,8 +19,8 @@ copied over (as a minimum). This nuget package includes build targets that extends the creation of a deploy package, which is generated by Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use Publish this won't affect you. -These folders will now be automatically included when creating a deploy package or publishing to the file system: -umbraco and umbraco_client. +These things will now be automatically included when creating a deploy package or publishing to the file system: +umbraco, umbraco_client, config\splashes and global.asax. Please read the release notes on our.umbraco.org: http://our.umbraco.org/contribute/releases diff --git a/build/NuSpecs/tools/install.ps1 b/build/NuSpecs/tools/install.ps1 index e2abca0760..8d25ea23bf 100644 --- a/build/NuSpecs/tools/install.ps1 +++ b/build/NuSpecs/tools/install.ps1 @@ -1,27 +1,38 @@ param($rootPath, $toolsPath, $package, $project) -if ($project) { - # Create a backup of extisting umbraco config files - $configPath = Join-Path (Split-Path $project.FullName -Parent) "\config" - $backupPath = Join-Path $configPath "\backup" - Get-ChildItem -path $configPath | - Where -filterscript {($_.Name.EndsWith("config"))} | Foreach-Object { - $newFileName = Join-Path $backupPath $_.Name.replace(".config",".config.backup") - New-Item -ItemType File -Path $newFileName -Force - Copy-Item $_.FullName $newFileName -Force - } - +if ($project) { + $dateTime = Get-Date -Format yyyyMMdd-HHmmss + $backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime" + + # Create backup folder if it doesn't exist yet + New-Item -ItemType Directory -Force -Path $backupPath + # Create a backup of original web.config $projectDestinationPath = Split-Path $project.FullName -Parent - $webConfigSource = Join-Path $projectDestinationPath "web.config" - $webConfigDestination = Join-Path $projectDestinationPath "web.config.backup" - Copy-Item $webConfigSource $webConfigDestination + $webConfigSource = Join-Path $projectDestinationPath "Web.config" + Copy-Item $webConfigSource $backupPath -Force - # Copy umbraco files from package to project folder - $projectDestinationPath = Split-Path $project.FullName -Parent - $umbracoFilesPath = Join-Path $rootPath "UmbracoFiles\*" - Copy-Item $umbracoFilesPath $projectDestinationPath -recurse -force - + # Copy Web.config from package to project folder + $umbracoFilesPath = Join-Path $rootPath "UmbracoFiles\Web.config" + Copy-Item $umbracoFilesPath $projectDestinationPath -Force + + # Copy umbraco and umbraco_files from package to project folder + # This is only done when these folders already exist because we + # only want to do this for upgrades + $umbracoFolder = Join-Path $projectDestinationPath "Umbraco\" + if(Test-Path $umbracoFolder) { + $umbracoFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco" + Copy-Item $umbracoFolder $backupPath -Force + robocopy $umbracoFolderSource $umbracoFolder /e /xf UI.xml + } + + $umbracoClientFolder = Join-Path $projectDestinationPath "Umbraco_Client" + if(Test-Path $umbracoClientFolder) { + $umbracoClientFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco_Client" + Copy-Item $umbracoClientFolder $backupPath -Force + robocopy $umbracoFolderSource $umbracoClientFolder /e + } + # Open readme.txt file $DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt') } \ No newline at end of file diff --git a/src/.nuget/NuGet.exe b/src/.nuget/NuGet.exe index c296edf177..8f61340295 100644 Binary files a/src/.nuget/NuGet.exe and b/src/.nuget/NuGet.exe differ diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets index 930ac95d01..6ff51f6e83 100644 --- a/src/.nuget/NuGet.targets +++ b/src/.nuget/NuGet.targets @@ -18,13 +18,13 @@ - + - + @@ -41,7 +41,7 @@ - $(NuGetToolsPath)\nuget.exe + $(NuGetToolsPath)\NuGet.exe @(PackageSource) "$(NuGetExePath)" @@ -50,9 +50,14 @@ $(TargetDir.Trim('\\')) -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " - $(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols @@ -70,7 +75,6 @@ - )", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""'](\w+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([\w\.]+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); /// /// This formats the persisted string to something useful for the rte so that the macro renders properly since we @@ -39,8 +39,8 @@ namespace Umbraco.Core.Macros //
var alias = match.Groups[2].Value; var sb = new StringBuilder("
5000 || image.Height > 5000) + { + //use mid quality + g.InterpolationMode = InterpolationMode.Bilinear; + } + else + { + //use best quality + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + } + + + g.SmoothingMode = SmoothingMode.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.CompositingQuality = CompositingQuality.HighQuality; diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 3fbdeccda8..c18c7c3a43 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -306,10 +306,8 @@ namespace Umbraco.Core.Models /// Value as a public virtual TPassType GetValue(string propertyTypeAlias) { - if (Properties[propertyTypeAlias].Value is TPassType) - return (TPassType)Properties[propertyTypeAlias].Value; - - return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType)); + var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo(); + return convertAttempt.Success ? convertAttempt.Result : default(TPassType); } /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index d377095a05..73b5ed34db 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -26,6 +26,20 @@ namespace Umbraco.Core.Models { #region IContent + /// + /// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published) + /// + /// + /// + /// + /// This is helpful for determining if the published event will execute during the saved event for a content item. + /// + internal static bool JustPublished(this IContent entity) + { + var dirty = (IRememberBeingDirty)entity; + return dirty.WasPropertyDirty("Published") && entity.Published; + } + /// /// Determines if a new version should be created /// @@ -618,7 +632,7 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in internal static XElement ToDeepXml(this IContent content) { - return ApplicationContext.Current.Services.PackagingService.Export(content, true); + return ApplicationContext.Current.Services.PackagingService.Export(content, true, raiseEvents: false); } /// @@ -628,7 +642,7 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in public static XElement ToXml(this IContent content) { - return ApplicationContext.Current.Services.PackagingService.Export(content); + return ApplicationContext.Current.Services.PackagingService.Export(content, raiseEvents: false); } /// @@ -638,24 +652,19 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in public static XElement ToXml(this IMedia media) { - return ApplicationContext.Current.Services.PackagingService.Export(media); - } - - internal static XElement ToDeepXml(this IMedia media) - { - return ApplicationContext.Current.Services.PackagingService.Export(media, true); + return ApplicationContext.Current.Services.PackagingService.Export(media, raiseEvents: false); } /// - /// Creates the xml representation for the object + /// Creates the full xml representation for the object and all of it's descendants /// - /// to generate xml for - /// Xml representation of the passed in - public static XElement ToXml(this IMember member) + /// to generate xml for + /// Xml representation of the passed in + internal static XElement ToDeepXml(this IMedia media) { - return ApplicationContext.Current.Services.PackagingService.Export(member); + return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false); } - + /// /// Creates the xml representation for the object /// @@ -668,6 +677,16 @@ namespace Umbraco.Core.Models //If current IContent is published we should get latest unpublished version return content.ToXml(); } + + /// + /// Creates the xml representation for the object + /// + /// to generate xml for + /// Xml representation of the passed in + public static XElement ToXml(this IMember member) + { + return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member); + } #endregion } diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 87acf07d45..f22c72f34d 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -43,6 +43,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _propertyTypes.CollectionChanged += PropertyTypesChanged; _additionalData = new Dictionary(); } @@ -54,6 +55,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _propertyTypes.CollectionChanged += PropertyTypesChanged; _additionalData = new Dictionary(); } @@ -433,8 +435,7 @@ namespace Umbraco.Core.Models { if (PropertyTypeExists(propertyType.Alias) == false) { - _propertyTypes.Add(propertyType); - _propertyTypes.CollectionChanged += PropertyTypesChanged; + _propertyTypes.Add(propertyType); return true; } diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs index db3f47ba13..c670a75108 100644 --- a/src/Umbraco.Core/Models/DictionaryItem.cs +++ b/src/Umbraco.Core/Models/DictionaryItem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models { diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs new file mode 100644 index 0000000000..6daf99a58d --- /dev/null +++ b/src/Umbraco.Core/Models/EntityExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + public static class EntityExtensions + { + + /// + /// Returns true if this entity has just been created and persisted to the data store + /// + /// + /// + /// + /// This is useful when handling events to determine if an entity is a brand new entity or was + /// already existing. + /// + public static bool IsNewEntity(this IEntity entity) + { + var dirty = (IRememberBeingDirty)entity; + return dirty.WasPropertyDirty("Id"); + } + } +} diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs index 978e54390f..f520b63210 100644 --- a/src/Umbraco.Core/Models/File.cs +++ b/src/Umbraco.Core/Models/File.cs @@ -6,6 +6,16 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models { + internal sealed class Folder : Entity + { + public Folder(string folderPath) + { + Path = folderPath; + } + + public string Path { get; set; } + } + /// /// Represents an abstract file which provides basic functionality for a File with an Alias and Name /// @@ -23,6 +33,8 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.Content); private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path); + private string _alias; + private string _name; /// /// Gets or sets the Name of the File including extension @@ -32,7 +44,11 @@ namespace Umbraco.Core.Models { get { - return new FileInfo(Path).Name; + if (_name == null) + { + _name = System.IO.Path.GetFileName(Path); + } + return _name; } } @@ -44,10 +60,14 @@ namespace Umbraco.Core.Models { get { - var fileInfo = new FileInfo(Path); - var name = fileInfo.Name; - int lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase); - return name.Substring(0, lastIndexOf); + if (_alias == null) + { + var name = System.IO.Path.GetFileName(Path); + if (name == null) return string.Empty; + var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase); + _alias = name.Substring(0, lastIndexOf); + } + return _alias; } } @@ -60,6 +80,10 @@ namespace Umbraco.Core.Models get { return _path; } set { + //reset + _alias = null; + _name = null; + SetPropertyValueAndDetectChanges(o => { _path = value; diff --git a/src/Umbraco.Core/Models/IMember.cs b/src/Umbraco.Core/Models/IMember.cs index edacd0b62b..d5838f30ff 100644 --- a/src/Umbraco.Core/Models/IMember.cs +++ b/src/Umbraco.Core/Models/IMember.cs @@ -1,106 +1,10 @@ using System; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Models { - public interface IMember : IContentBase + public interface IMember : IContentBase, IMembershipUser { - /// - /// Gets or sets the Username - /// - string Username { get; set; } - - /// - /// Gets or sets the Email - /// - string Email { get; set; } - - /// - /// Gets or sets the Password - /// - string Password { get; set; } - - /// - /// Gets or sets the Password Question - /// - /// - /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias - /// Part of the standard properties collection. - /// - string PasswordQuestion { get; set; } - - /// - /// Gets or sets the Password Answer - /// - /// - /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias - /// Part of the standard properties collection. - /// - string PasswordAnswer { get; set; } - - /// - /// Gets or set the comments for the member - /// - /// - /// Alias: umbracoCommentPropertyTypeAlias - /// Part of the standard properties collection. - /// - string Comments { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Member is approved - /// - /// - /// Alias: umbracoApprovePropertyTypeAlias - /// Part of the standard properties collection. - /// - bool IsApproved { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Member is locked out - /// - /// - /// Alias: umbracoLockPropertyTypeAlias - /// Part of the standard properties collection. - /// - bool IsLockedOut { get; set; } - - /// - /// Gets or sets the date for last login - /// - /// - /// Alias: umbracoLastLoginPropertyTypeAlias - /// Part of the standard properties collection. - /// - DateTime LastLoginDate { get; set; } - - /// - /// Gest or sets the date for last password change - /// - /// - /// Alias: umbracoMemberLastPasswordChange - /// Part of the standard properties collection. - /// - DateTime LastPasswordChangeDate { get; set; } - - /// - /// Gets or sets the date for when Member was locked out - /// - /// - /// Alias: umbracoMemberLastLockout - /// Part of the standard properties collection. - /// - DateTime LastLockoutDate { get; set; } - - /// - /// Gets or sets the number of failed password attempts. - /// This is the number of times the password was entered incorrectly upon login. - /// - /// - /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias - /// Part of the standard properties collection. - /// - int FailedPasswordAttempts { get; set; } - /// /// String alias of the default ContentType /// diff --git a/src/Umbraco.Core/Models/IMemberGroup.cs b/src/Umbraco.Core/Models/IMemberGroup.cs new file mode 100644 index 0000000000..5c3741997b --- /dev/null +++ b/src/Umbraco.Core/Models/IMemberGroup.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a member type + /// + public interface IMemberGroup : IAggregateRoot + { + /// + /// The name of the member group + /// + string Name { get; set; } + + /// + /// Profile of the user who created this Entity + /// + int CreatorId { get; set; } + + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + IDictionary AdditionalData { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ITemplate.cs b/src/Umbraco.Core/Models/ITemplate.cs index 2014ac9837..7fb4eac4cf 100644 --- a/src/Umbraco.Core/Models/ITemplate.cs +++ b/src/Umbraco.Core/Models/ITemplate.cs @@ -10,5 +10,11 @@ /// /// RenderingEngine GetTypeOfRenderingEngine(); + + /// + /// Set the mastertemplate + /// + /// + void SetMasterTemplate(ITemplate masterTemplate); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index 6986fae027..ef8ebbf931 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models { diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index 914847aea2..d81f49fc4f 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -107,7 +107,7 @@ namespace Umbraco.Core.Models private bool _dontRender; private string _scriptFile; private string _scriptAssembly; - private string _python; + private string _scriptPath; private string _xslt; private readonly MacroPropertyCollection _properties; private readonly List _addedProperties; @@ -360,14 +360,14 @@ namespace Umbraco.Core.Models [DataMember] public string ScriptPath { - get { return _python; } + get { return _scriptPath; } set { SetPropertyValueAndDetectChanges(o => { - _python = value; - return _python; - }, _python, ScriptPathSelector); + _scriptPath = value; + return _scriptPath; + }, _scriptPath, ScriptPathSelector); } } diff --git a/src/Umbraco.Core/Models/MacroProperty.cs b/src/Umbraco.Core/Models/MacroProperty.cs index 243e5c0e91..ef0a024973 100644 --- a/src/Umbraco.Core/Models/MacroProperty.cs +++ b/src/Umbraco.Core/Models/MacroProperty.cs @@ -155,7 +155,6 @@ namespace Umbraco.Core.Models /// [DataMember] public string EditorAlias - { get { return _editorAlias; } set diff --git a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs index 4864c4bba4..f9c0c2cc1e 100644 --- a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs +++ b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Models.Mapping /// All automapper configurations are done during startup /// inside an Automapper Initialize call which is better for performance /// - internal interface IMapperConfiguration : IApplicationEventHandler + public interface IMapperConfiguration : IApplicationEventHandler { void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext); } diff --git a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs index 1e88e648cf..52683231e2 100644 --- a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs +++ b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Models.Mapping /// /// Use this class if your mapper configuration isn't also explicitly an ApplicationEventHandler. /// - internal abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration + public abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration { public abstract void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext); } diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index 2f0e96f294..cb41f2b7f8 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Logging; namespace Umbraco.Core.Models { @@ -13,50 +15,104 @@ namespace Umbraco.Core.Models public class Member : ContentBase, IMember { private readonly IMemberType _contentType; - private string _contentTypeAlias; + private readonly string _contentTypeAlias; private string _username; private string _email; - private string _password; + private string _rawPasswordValue; private object _providerUserKey; private Type _userTypeKey; + /// + /// Constructor for creating an empty Member object + /// + /// ContentType for the current Content object + public Member(IMemberType contentType) + : base("", -1, contentType, new PropertyCollection()) + { + Mandate.ParameterNotNull(contentType, "contentType"); + + _contentTypeAlias = contentType.Alias; + _contentType = contentType; + IsApproved = true; + + //this cannot be null but can be empty + _rawPasswordValue = ""; + _email = ""; + _username = ""; + } + /// /// Constructor for creating a Member object /// /// Name of the content /// ContentType for the current Content object public Member(string name, IMemberType contentType) + : this(contentType) + { + Mandate.ParameterNotNull(contentType, "contentType"); + Mandate.ParameterNotNullOrEmpty(name, "name"); + + _contentTypeAlias = contentType.Alias; + _contentType = contentType; + IsApproved = true; + + //this cannot be null but can be empty + _rawPasswordValue = ""; + _email = ""; + _username = ""; + } + + /// + /// Constructor for creating a Member object + /// + /// + /// + /// + /// + public Member(string name, string email, string username, IMemberType contentType) : base(name, -1, contentType, new PropertyCollection()) - { - _contentType = contentType; - } - - public Member(string name, string email, string username, string password, int parentId, IMemberType contentType) - : base(name, parentId, contentType, new PropertyCollection()) { Mandate.ParameterNotNull(contentType, "contentType"); + Mandate.ParameterNotNullOrEmpty(name, "name"); + Mandate.ParameterNotNullOrEmpty(email, "email"); + Mandate.ParameterNotNullOrEmpty(username, "username"); + _contentTypeAlias = contentType.Alias; _contentType = contentType; _email = email; _username = username; - _password = password; + IsApproved = true; + + //this cannot be null but can be empty + _rawPasswordValue = ""; } - public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType) - : base(name, parent, contentType, new PropertyCollection()) + /// + /// Constructor for creating a Member object + /// + /// + /// + /// + /// + /// The password value passed in to this parameter should be the encoded/encrypted/hashed format of the member's password + /// + /// + public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType) + : base(name, -1, contentType, new PropertyCollection()) { Mandate.ParameterNotNull(contentType, "contentType"); + _contentTypeAlias = contentType.Alias; _contentType = contentType; _email = email; _username = username; - _password = password; + _rawPasswordValue = rawPasswordValue; + IsApproved = true; } - private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username); private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email); - private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.Password); + private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.RawPasswordValue); private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey); private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType); @@ -95,19 +151,19 @@ namespace Umbraco.Core.Models } /// - /// Gets or sets the Password + /// Gets or sets the raw password value /// [DataMember] - public string Password + public string RawPasswordValue { - get { return _password; } + get { return _rawPasswordValue; } set { SetPropertyValueAndDetectChanges(o => { - _password = value; - return _password; - }, _password, PasswordSelector); + _rawPasswordValue = value; + return _rawPasswordValue; + }, _rawPasswordValue, PasswordSelector); } } @@ -125,7 +181,7 @@ namespace Umbraco.Core.Models /// Gets or sets the Password Question /// /// - /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias + /// Alias: umbracoMemberPasswordRetrievalQuestion /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -133,34 +189,50 @@ namespace Umbraco.Core.Models { get { + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordQuestion, "PasswordQuestion", default(string)); + if (a.Success == false) return a.Result; + return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null ? string.Empty : Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString(); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.PasswordQuestion, + "PasswordQuestion") == false) return; + Properties[Constants.Conventions.Member.PasswordQuestion].Value = value; } } /// - /// Gets or sets the Password Answer + /// Gets or sets the raw password answer value /// /// - /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias + /// For security reasons this value should be encrypted, the encryption process is handled by the memberhip provider + /// Alias: umbracoMemberPasswordRetrievalAnswer + /// /// Part of the standard properties collection. /// [IgnoreDataMember] - public string PasswordAnswer + public string RawPasswordAnswerValue { get { + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordAnswer, "PasswordAnswer", default(string)); + if (a.Success == false) return a.Result; + return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null ? string.Empty : Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString(); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.PasswordAnswer, + "PasswordAnswer") == false) return; + Properties[Constants.Conventions.Member.PasswordAnswer].Value = value; } } @@ -169,7 +241,7 @@ namespace Umbraco.Core.Models /// Gets or set the comments for the member /// /// - /// Alias: umbracoCommentPropertyTypeAlias + /// Alias: umbracoMemberComments /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -177,12 +249,19 @@ namespace Umbraco.Core.Models { get { + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.Comments, "Comments", default(string)); + if (a.Success == false) return a.Result; + return Properties[Constants.Conventions.Member.Comments].Value == null ? string.Empty : Properties[Constants.Conventions.Member.Comments].Value.ToString(); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.Comments, + "Comments") == false) return; + Properties[Constants.Conventions.Member.Comments].Value = value; } } @@ -191,7 +270,7 @@ namespace Umbraco.Core.Models /// Gets or sets a boolean indicating whether the Member is approved /// /// - /// Alias: umbracoApprovePropertyTypeAlias + /// Alias: umbracoMemberApproved /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -199,17 +278,25 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.IsApproved].Value == null) - return default(bool); + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, "IsApproved", + //This is the default value if the prop is not found + true); + if (a.Success == false) return a.Result; - if (Properties[Constants.Conventions.Member.IsApproved].Value is bool) - return (bool)Properties[Constants.Conventions.Member.IsApproved].Value; - - //TODO: Use TryConvertTo instead - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsApproved].Value, typeof(bool)); + var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + //if the property exists but it cannot be converted, we will assume true + return true; } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.IsApproved, + "IsApproved") == false) return; + Properties[Constants.Conventions.Member.IsApproved].Value = value; } } @@ -218,7 +305,7 @@ namespace Umbraco.Core.Models /// Gets or sets a boolean indicating whether the Member is locked out ///
/// - /// Alias: umbracoLockPropertyTypeAlias + /// Alias: umbracoMemberLockedOut /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -226,17 +313,23 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) - return default(bool); - - if (Properties[Constants.Conventions.Member.IsLockedOut].Value is bool) - return (bool)Properties[Constants.Conventions.Member.IsLockedOut].Value; - + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false); + if (a.Success == false) return a.Result; + + var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return false; //TODO: Use TryConvertTo instead - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsLockedOut].Value, typeof(bool)); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.IsLockedOut, + "IsLockedOut") == false) return; + Properties[Constants.Conventions.Member.IsLockedOut].Value = value; } } @@ -245,7 +338,7 @@ namespace Umbraco.Core.Models /// Gets or sets the date for last login /// /// - /// Alias: umbracoLastLoginPropertyTypeAlias + /// Alias: umbracoMemberLastLogin /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -253,17 +346,23 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastLoginDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastLoginDate].Value; - + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime)); + if (a.Success == false) return a.Result; + + var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLoginDate].Value, typeof(DateTime)); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.LastLoginDate, + "LastLoginDate") == false) return; + Properties[Constants.Conventions.Member.LastLoginDate].Value = value; } } @@ -272,7 +371,7 @@ namespace Umbraco.Core.Models /// Gest or sets the date for last password change /// /// - /// Alias: umbracoMemberLastPasswordChange + /// Alias: umbracoMemberLastPasswordChangeDate /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -280,17 +379,23 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value; + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime)); + if (a.Success == false) return a.Result; + var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value, typeof(DateTime)); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.LastPasswordChangeDate, + "LastPasswordChangeDate") == false) return; + Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value; } } @@ -299,7 +404,7 @@ namespace Umbraco.Core.Models /// Gets or sets the date for when Member was locked out /// /// - /// Alias: umbracoMemberLastLockout + /// Alias: umbracoMemberLastLockoutDate /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -307,17 +412,23 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastLockoutDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastLockoutDate].Value; - + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime)); + if (a.Success == false) return a.Result; + + var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLockoutDate].Value, typeof(DateTime)); } set { + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.LastLockoutDate, + "LastLockoutDate") == false) return; + Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; } } @@ -327,7 +438,7 @@ namespace Umbraco.Core.Models /// This is the number of times the password was entered incorrectly upon login. /// /// - /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias + /// Alias: umbracoMemberFailedPasswordAttempts /// Part of the standard properties collection. /// [IgnoreDataMember] @@ -335,18 +446,24 @@ namespace Umbraco.Core.Models { get { - if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) - return default(int); - - if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value is int) - return (int)Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value; - + var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0); + if (a.Success == false) return a.Result; + + var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(int); //TODO: Use TryConvertTo instead - return (int)Convert.ChangeType(Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value, typeof(int)); } set { - Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; + if (WarnIfPropertyTypeNotFoundOnSet( + Constants.Conventions.Member.FailedPasswordAttempts, + "FailedPasswordAttempts") == false) return; + + Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value = value; } } @@ -357,14 +474,6 @@ namespace Umbraco.Core.Models public virtual string ContentTypeAlias { get { return _contentTypeAlias; } - internal set - { - SetPropertyValueAndDetectChanges(o => - { - _contentTypeAlias = value; - return _contentTypeAlias; - }, _contentTypeAlias, DefaultContentTypeAliasSelector); - } } /// @@ -377,7 +486,7 @@ namespace Umbraco.Core.Models /// membership provider. /// [DataMember] - internal virtual object ProviderUserKey + public virtual object ProviderUserKey { get { @@ -460,5 +569,73 @@ namespace Umbraco.Core.Models internal bool BoolPropertyValue { get; set; } internal DateTime DateTimePropertyValue { get; set; } internal string PropertyTypeAlias { get; set; } + + private Attempt WarnIfPropertyTypeNotFoundOnGet(string propertyAlias, string propertyName, T defaultVal) + { + Action doLog = () => LogHelper.Warn( + "Trying to access the '" + + propertyName + + "' property on " + + typeof(Member) + + " but the " + + propertyAlias + + " property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: " + + propertyAlias + + " configured on your member type in order to use the '" + + propertyName + + "' property on the model correctly."); + + //if the property doesn't exist, then do the logging and return a failure + if (Properties.Contains(propertyAlias) == false) + { + //we'll put a warn in the log if this entity has been persisted + if (HasIdentity) + { + doLog(); + } + return Attempt.Fail(defaultVal); + } + + //if the property doesn't have an identity but we do, then do logging and return failure + var prop = Properties.Single(x => x.Alias == propertyAlias); + if (prop.HasIdentity == false && HasIdentity) + { + doLog(); + return Attempt.Fail(defaultVal); + } + + return Attempt.Succeed(); + } + + private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyname) + { + Action doLog = () => LogHelper.Warn("An attempt was made to set a value on the property '" + + propertyname + + "' on type " + + typeof(Member) + + " but the property type " + + propertyAlias + + " does not exist on the member type, ensure that this property type exists so that setting this property works correctly."); + + //if the property doesn't exist, then do the logging and return a failure + if (Properties.Contains(propertyAlias) == false) + { + if (HasIdentity) + { + doLog(); + } + return false; + } + + //if the property doesn't have an identity but we do, then do logging and return failure + var prop = Properties.Single(x => x.Alias == propertyAlias); + if (prop.HasIdentity == false && HasIdentity) + { + doLog(); + return false; + } + + return true; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MemberGroup.cs b/src/Umbraco.Core/Models/MemberGroup.cs new file mode 100644 index 0000000000..e52448a11d --- /dev/null +++ b/src/Umbraco.Core/Models/MemberGroup.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a member type + /// + [Serializable] + [DataContract(IsReference = true)] + public class MemberGroup : Entity, IMemberGroup + { + public MemberGroup() + { + AdditionalData = new Dictionary(); + } + + private string _name; + private int _creatorId; + + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); + + [DataMember] + public string Name + { + get { return _name; } + set + { + SetPropertyValueAndDetectChanges(o => + { + if (_name != value) + { + //if the name has changed, add the value to the additional data, + //this is required purely for event handlers to know the previous name of the group + //so we can keep the public access up to date. + AdditionalData["previousName"] = _name; + } + + _name = value; + return _name; + }, _name, NameSelector); + } + } + + public int CreatorId + { + get { return _creatorId; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _creatorId = value; + return _creatorId; + }, _creatorId, CreatorIdSelector); + } + } + + public IDictionary AdditionalData { get; private set; } + + /// + /// Method to call when Entity is being saved + /// + /// Created date is set and a Unique key is assigned + internal override void AddingEntity() + { + base.AddingEntity(); + + if (Key == Guid.Empty) + Key = Guid.NewGuid(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index c361891fa0..527e379d17 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -14,37 +14,50 @@ namespace Umbraco.Core.Models public class MemberType : ContentTypeCompositionBase, IMemberType { //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId - private IDictionary> _memberTypePropertyTypes; + private string _alias; public MemberType(int parentId) : base(parentId) { - _memberTypePropertyTypes = new Dictionary>(); + MemberTypePropertyTypes = new Dictionary(); } public MemberType(IContentTypeComposition parent) : base(parent) { - _memberTypePropertyTypes = new Dictionary>(); + MemberTypePropertyTypes = new Dictionary(); } - private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); /// - /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. + /// The Alias of the ContentType /// [DataMember] - internal IDictionary> MemberTypePropertyTypes + public override string Alias { - get { return _memberTypePropertyTypes; } + get { return _alias; } set { + //NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of + // "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips + // leading underscores which we don't want in this case. + // see : http://issues.umbraco.org/issue/U4-3968 + SetPropertyValueAndDetectChanges(o => { - _memberTypePropertyTypes = value; - return _memberTypePropertyTypes; - }, _memberTypePropertyTypes, MemberTypePropertyTypesSelector); + _alias = value == "_umbracoSystemDefaultProtectType" + ? value + : (value == null ? string.Empty : value.ToSafeAlias() ); + return _alias; + }, _alias, AliasSelector); } } + /// + /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias. + /// + [DataMember] + internal IDictionary MemberTypePropertyTypes { get; private set; } + /// /// Gets a boolean indicating whether a Property is editable by the Member. /// @@ -54,7 +67,7 @@ namespace Umbraco.Core.Models { if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) { - return MemberTypePropertyTypes[propertyTypeAlias].Item1; + return MemberTypePropertyTypes[propertyTypeAlias].IsEditable; } return false; @@ -69,7 +82,7 @@ namespace Umbraco.Core.Models { if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) { - return MemberTypePropertyTypes[propertyTypeAlias].Item2; + return MemberTypePropertyTypes[propertyTypeAlias].IsVisible; } return false; @@ -84,13 +97,11 @@ namespace Umbraco.Core.Models { if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) { - var tuple = MemberTypePropertyTypes[propertyTypeAlias]; - MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(value, tuple.Item2, tuple.Item3); + MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value; } else { - var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); - var tuple = new Tuple(value, false, propertyType.Id); + var tuple = new MemberTypePropertyProfileAccess(false, value); MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); } } @@ -104,13 +115,11 @@ namespace Umbraco.Core.Models { if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) { - var tuple = MemberTypePropertyTypes[propertyTypeAlias]; - MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(tuple.Item1, value, tuple.Item3); + MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value; } else { - var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); - var tuple = new Tuple(false, value, propertyType.Id); + var tuple = new MemberTypePropertyProfileAccess(value, false); MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); } } diff --git a/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs b/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs new file mode 100644 index 0000000000..fa9e0b7307 --- /dev/null +++ b/src/Umbraco.Core/Models/MemberTypePropertyProfileAccess.cs @@ -0,0 +1,17 @@ +namespace Umbraco.Core.Models +{ + /// + /// Used to track the property types that are visible/editable on member profiles + /// + internal class MemberTypePropertyProfileAccess + { + public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable) + { + IsVisible = isVisible; + IsEditable = isEditable; + } + + public bool IsVisible { get; set; } + public bool IsEditable { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/EntityPermission.cs b/src/Umbraco.Core/Models/Membership/EntityPermission.cs index c417a4985c..175e571fdf 100644 --- a/src/Umbraco.Core/Models/Membership/EntityPermission.cs +++ b/src/Umbraco.Core/Models/Membership/EntityPermission.cs @@ -5,14 +5,14 @@ /// public class EntityPermission { - public EntityPermission(object userId, int entityId, string[] assignedPermissions) + public EntityPermission(int userId, int entityId, string[] assignedPermissions) { UserId = userId; EntityId = entityId; AssignedPermissions = assignedPermissions; } - public object UserId { get; private set; } + public int UserId { get; private set; } public int EntityId { get; private set; } /// diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs index 422b47da22..b304f72777 100644 --- a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs +++ b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs @@ -4,23 +4,42 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models.Membership { - internal interface IMembershipUser : IMembershipUserId, IAggregateRoot - { - /*new object Id { get; set; }*/ + public interface IMembershipUser : IAggregateRoot + { + object ProviderUserKey { get; set; } string Username { get; set; } string Email { get; set; } - string Password { get; set; } + + /// + /// Gets or sets the raw password value + /// + string RawPasswordValue { get; set; } + string PasswordQuestion { get; set; } - string PasswordAnswer { get; set; } + + /// + /// Gets or sets the raw password answer value + /// + string RawPasswordAnswerValue { get; set; } + string Comments { get; set; } bool IsApproved { get; set; } - bool IsOnline { get; set; } bool IsLockedOut { get; set; } DateTime LastLoginDate { get; set; } DateTime LastPasswordChangeDate { get; set; } DateTime LastLockoutDate { get; set; } - object ProfileId { get; set; } - IEnumerable Groups { get; set; } + /// + /// Gets or sets the number of failed password attempts. + /// This is the number of times the password was entered incorrectly upon login. + /// + /// + /// Alias: umbracoMemberFailedPasswordAttempts + /// Part of the standard properties collection. + /// + int FailedPasswordAttempts { get; set; } + + //object ProfileId { get; set; } + //IEnumerable Groups { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs b/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs deleted file mode 100644 index 03508e1ec8..0000000000 --- a/src/Umbraco.Core/Models/Membership/IMembershipUserId.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Umbraco.Core.Models.Membership -{ - internal interface IMembershipUserId - { - object ProviderUserKey { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IProfile.cs b/src/Umbraco.Core/Models/Membership/IProfile.cs index fbbff85e7d..749c3371b8 100644 --- a/src/Umbraco.Core/Models/Membership/IProfile.cs +++ b/src/Umbraco.Core/Models/Membership/IProfile.cs @@ -3,6 +3,10 @@ /// /// Defines the the Profile interface /// + /// + /// This interface is pretty useless but has been exposed publicly from 6.x so we're stuck with it. It would make more sense + /// if the Id was an int but since it's not people have to cast it to int all of the time! + /// public interface IProfile { object Id { get; set; } diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index b0ba6817f8..46338c8c4a 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -7,31 +7,34 @@ namespace Umbraco.Core.Models.Membership /// Defines the interface for a /// /// Will be left internal until a proper Membership implementation is part of the roadmap - internal interface IUser : IMembershipUser, IUserProfile + public interface IUser : IMembershipUser { - new object Id { get; set; } - //string Name { get; set; } + string Name { get; set; } int SessionTimeout { get; set; } int StartContentId { get; set; } int StartMediaId { get; set; } string Language { get; set; } - bool NoConsole { get; set; } - IUserType UserType { get; } - /// - /// The default permissions for the user + /// Gets/sets the user type for the user + /// + IUserType UserType { get; set; } + + /// + /// The default permission set for the user /// /// - /// The default permissions are assigned to the user object based on the user type's default permissions - /// - string DefaultPermissions { get; } - } + /// Currently in umbraco each permission is a single char but with an Enumerable{string} collection this allows for flexible changes to this in the future + /// + IEnumerable DefaultPermissions { get; set; } - internal interface IUserProfile : IProfile - { IEnumerable AllowedSections { get; } void RemoveAllowedSection(string sectionAlias); void AddAllowedSection(string sectionAlias); + + /// + /// Exposes the basic profile data + /// + IProfile ProfileData { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IUserType.cs b/src/Umbraco.Core/Models/Membership/IUserType.cs index aa837bf890..fe678afd2b 100644 --- a/src/Umbraco.Core/Models/Membership/IUserType.cs +++ b/src/Umbraco.Core/Models/Membership/IUserType.cs @@ -1,13 +1,28 @@ +using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models.Membership { - internal interface IUserType : IAggregateRoot + public interface IUserType : IAggregateRoot { + /// + /// The user type alias + /// string Alias { get; set; } + + /// + /// The user type name + /// string Name { get; set; } - string Permissions { get; set; } + + /// + /// The set of default permissions for the user type + /// + /// + /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. + /// + IEnumerable Permissions { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MemberCountType.cs b/src/Umbraco.Core/Models/Membership/MemberCountType.cs new file mode 100644 index 0000000000..89b428a827 --- /dev/null +++ b/src/Umbraco.Core/Models/Membership/MemberCountType.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Models.Membership +{ + /// + /// The types of members to count + /// + public enum MemberCountType + { + All, + Online, + LockedOut, + Approved + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs similarity index 65% rename from src/Umbraco.Core/Models/Membership/MembershipExtensions.cs rename to src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs index df0ee1529a..6008c0ae02 100644 --- a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs @@ -1,20 +1,21 @@ using System; using System.Web.Security; +using Umbraco.Core.Security; using Umbraco.Core.Services; namespace Umbraco.Core.Models.Membership { - internal static class MembershipExtensions + internal static class MembershipUserExtensions { - internal static MembershipUser AsConcreteMembershipUser(this IMember member) + internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName) { - var membershipMember = new UmbracoMembershipMember(member); + var membershipMember = new UmbracoMembershipMember(member, providerName); return membershipMember; } - internal static IMember AsIMember(this MembershipUser membershipMember) + internal static IMembershipUser AsIMember(this UmbracoMembershipMember membershipMember) { - var member = membershipMember as UmbracoMembershipMember; + var member = membershipMember; if (member != null) { return member.Member; @@ -32,11 +33,12 @@ namespace Umbraco.Core.Models.Membership { if (_scenario.HasValue == false) { - if (System.Web.Security.Membership.Provider.Name == Constants.Conventions.Member.UmbracoMemberProviderName) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) { return MembershipScenario.NativeUmbraco; } - var memberType = ApplicationContext.Current.Services.MemberTypeService.GetMemberType(Constants.Conventions.MemberTypes.Member); + var memberType = ApplicationContext.Current.Services.MemberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias); return memberType != null ? MembershipScenario.CustomProviderWithUmbracoLink : MembershipScenario.StandaloneCustomProvider; diff --git a/src/Umbraco.Core/Models/Membership/Profile.cs b/src/Umbraco.Core/Models/Membership/Profile.cs deleted file mode 100644 index b3959bb9cd..0000000000 --- a/src/Umbraco.Core/Models/Membership/Profile.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models.Membership -{ - /// - /// Represents a Profile which is shared between Members and Users - /// - [Serializable] - [DataContract(IsReference = true)] - internal class Profile : TracksChangesEntityBase, IProfile, IRememberBeingDirty - { - /// - /// Initializes a new instance of the class. - /// - protected Profile() - { - ProviderUserKeyType = typeof(int); - } - - public Profile(object id, string name) - { - ProviderUserKeyType = typeof(int); - Id = id; - Name = name; - } - - private object _id; - private string _name; - private object _providerUserKey; - private Type _userTypeKey; - - private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id); - private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey); - private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType); - - [DataMember] - public virtual object Id - { - get - { - return _id; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _id = value; - return _id; - }, _id, IdSelector); - } - } - - [DataMember] - public virtual string Name - { - get - { - return _name; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _name = value; - return _name; - }, _name, NameSelector); - } - } - - [IgnoreDataMember] - public virtual object ProviderUserKey - { - get - { - return _providerUserKey; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _providerUserKey = value; - return _id; - }, _providerUserKey, ProviderUserKeySelector); - } - } - - /// - /// Gets or sets the type of the provider user key. - /// - /// - /// The type of the provider user key. - /// - [IgnoreDataMember] - internal Type ProviderUserKeyType - { - get - { - return _userTypeKey; - } - private set - { - SetPropertyValueAndDetectChanges(o => - { - _userTypeKey = value; - return _userTypeKey; - }, _userTypeKey, UserTypeKeySelector); - } - } - - /// - /// Sets the type of the provider user key. - /// - /// The type. - internal void SetProviderUserKeyType(Type type) - { - ProviderUserKeyType = type; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs index 4397f766c0..1b8c7f5393 100644 --- a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs +++ b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs @@ -1,66 +1,127 @@ -using System.Web.Security; +using System; +using System.Web.Security; namespace Umbraco.Core.Models.Membership { - //TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow. internal class UmbracoMembershipMember : MembershipUser { - private readonly IMember _member; + private readonly IMembershipUser _member; + private readonly string _userName; + private readonly object _providerUserKey; + private readonly string _passwordQuestion; + private readonly bool _isLockedOut; + private readonly DateTime _lastLockoutDate; + private readonly DateTime _creationDate; + private DateTime _lastLoginDate; + private readonly DateTime _lastPasswordChangedDate; + private readonly string _providerName; + private string _email; + private string _comment; + private bool _isApproved; + private DateTime _lastActivityDate; - public UmbracoMembershipMember(IMember member) + //NOTE: We are only overriding the properties that matter, we don't override things like IsOnline since that is handled with the sub-class and the membership providers. + + //NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor + // validation for that doesn't need to be there anyways (have checked the source). + public UmbracoMembershipMember(IMembershipUser member, string providerName) { _member = member; + //NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user + // handles data! We don't want to do anything differently there but since we cannot use their ctor we'll need to handle this logic ourselves. + if (member.Username != null) + _userName = member.Username.Trim(); + if (member.Email != null) + _email = member.Email.Trim(); + if (member.PasswordQuestion != null) + _passwordQuestion = member.PasswordQuestion.Trim(); + _providerName = providerName; + _providerUserKey = member.ProviderUserKey; + _comment = member.Comments; + _isApproved = member.IsApproved; + _isLockedOut = member.IsLockedOut; + _creationDate = member.CreateDate.ToUniversalTime(); + _lastLoginDate = member.LastLoginDate.ToUniversalTime(); + //TODO: We currently don't really have any place to store this data!! + _lastActivityDate = member.LastLoginDate.ToUniversalTime(); + _lastPasswordChangedDate = member.LastPasswordChangeDate.ToUniversalTime(); + _lastLockoutDate = member.LastLockoutDate.ToUniversalTime(); } - - internal IMember Member + + internal IMembershipUser Member { get { return _member; } } - public override string Email + public override string UserName { - get { return _member.Email; } - set { _member.Email = value; } + get { return _userName; } } public override object ProviderUserKey { - get { return _member.Key; } + get { return _providerUserKey; } } - public override System.DateTime CreationDate + public override string Email { - get { return _member.CreateDate; } + get { return _email; } + set { _email = value; } } - public override string UserName + public override string PasswordQuestion { - get { return _member.Username; } + get { return _passwordQuestion; } } public override string Comment { - get { return _member.Comments; } - set { _member.Comments = value; } + get { return _comment; } + set { _comment = value; } } public override bool IsApproved { - get { return _member.IsApproved; } - set { _member.IsApproved = value; } + get { return _isApproved; } + set { _isApproved = value; } } public override bool IsLockedOut { - get { return _member.IsLockedOut; } + get { return _isLockedOut; } } - public override System.DateTime LastLoginDate + public override DateTime LastLockoutDate { - get { return _member.LastLoginDate; } - set { _member.LastLoginDate = value; } + get { return _lastLockoutDate; } } + public override DateTime CreationDate + { + get { return _creationDate; } + } + + public override DateTime LastLoginDate + { + get { return _lastLoginDate; } + set { _lastLoginDate = value; } + } + + public override DateTime LastActivityDate + { + get { return _lastActivityDate; } + set { _lastActivityDate = value; } + } + + public override DateTime LastPasswordChangedDate + { + get { return _lastPasswordChangedDate; } + } + + public override string ProviderName + { + get { return _providerName; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 9840c25b98..2010161df0 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -1,7 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Linq; +using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; @@ -16,97 +20,501 @@ namespace Umbraco.Core.Models.Membership /// [Serializable] [DataContract(IsReference = true)] - internal class User : UserProfile, IUser + public class User : TracksChangesEntityBase, IUser { - private bool _hasIdentity; - public User(IUserType userType) { - Groups = new List { userType }; + if (userType == null) throw new ArgumentNullException("userType"); + + _userType = userType; + _defaultPermissions = _userType.Permissions; + //Groups = new List { userType }; + SessionTimeout = 60; + _sectionCollection = new ObservableCollection(); + _addedSections = new List(); + _removedSections = new List(); + _language = GlobalSettings.DefaultUILanguage; + _sectionCollection.CollectionChanged += SectionCollectionChanged; + _isApproved = true; + _isLockedOut = false; + _startContentId = -1; + _startMediaId = -1; + //cannot be null + _rawPasswordValue = ""; } + public User(string name, string email, string username, string rawPasswordValue, IUserType userType) + : this(userType) + { + _name = name; + _email = email; + _username = username; + _rawPasswordValue = rawPasswordValue; + _isApproved = true; + _isLockedOut = false; + _startContentId = -1; + _startMediaId = -1; + } + + private IUserType _userType; + private bool _hasIdentity; + private int _id; + private string _name; + private Type _userTypeKey; + private readonly List _addedSections; + private readonly List _removedSections; + private readonly ObservableCollection _sectionCollection; + private int _sessionTimeout; + private int _startContentId; + private int _startMediaId; + + private string _username; + private string _email; + private string _rawPasswordValue; + private bool _isApproved; + private bool _isLockedOut; + private string _language; + private IEnumerable _defaultPermissions; + private bool _defaultToLiveEditing; + + private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo(x => x.SessionTimeout); + private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartContentId); + private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId); + private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections); + private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id); + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType); + + private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username); + private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email); + private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.RawPasswordValue); + private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo(x => x.IsLockedOut); + private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo(x => x.IsApproved); + private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language); + private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.DefaultPermissions); + private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo(x => x.DefaultToLiveEditing); + private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.HasIdentity); + private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.UserType); + #region Implementation of IEntity [IgnoreDataMember] - public bool HasIdentity { get { return Id != null || _hasIdentity; } } - - [IgnoreDataMember] - int IEntity.Id + public bool HasIdentity { get { - return int.Parse(base.Id.ToString()); + return _hasIdentity; } - set + protected set { - base.Id = value; - _hasIdentity = true; + SetPropertyValueAndDetectChanges(o => + { + _hasIdentity = value; + return _hasIdentity; + }, _hasIdentity, HasIdentitySelector); } } + [DataMember] + public int Id + { + get + { + return _id; + } + set + { + SetPropertyValueAndDetectChanges(o => + { + _id = value; + HasIdentity = true; //set the has Identity + return _id; + }, _id, IdSelector); + } + } + + //this doesn't get used + [IgnoreDataMember] public Guid Key { get; set; } #endregion #region Implementation of IMembershipUser + [IgnoreDataMember] + public object ProviderUserKey + { + get { return Id; } + set { throw new NotSupportedException("Cannot set the provider user key for a user"); } + } + + /// + /// Gets or sets the type of the provider user key. + /// + /// + /// The type of the provider user key. + /// + [IgnoreDataMember] + internal Type ProviderUserKeyType + { + get + { + return _userTypeKey; + } + private set + { + SetPropertyValueAndDetectChanges(o => + { + _userTypeKey = value; + return _userTypeKey; + }, _userTypeKey, UserTypeKeySelector); + } + } + + /// + /// Sets the type of the provider user key. + /// + /// The type. + internal void SetProviderUserKeyType(Type type) + { + ProviderUserKeyType = type; + } + [DataMember] - public string Username { get; set; } + public string Username + { + get { return _username; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _username = value; + return _username; + }, _username, UsernameSelector); + } + } [DataMember] - public string Email { get; set; } + public string Email + { + get { return _email; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _email = value; + return _email; + }, _email, EmailSelector); + } + } [DataMember] - public string Password { get; set; } + public string RawPasswordValue + { + get { return _rawPasswordValue; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _rawPasswordValue = value; + return _rawPasswordValue; + }, _rawPasswordValue, PasswordSelector); + } + } + [DataMember] + public bool IsApproved + { + get { return _isApproved; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _isApproved = value; + return _isApproved; + }, _isApproved, IsApprovedSelector); + } + } + + [DataMember] + public bool IsLockedOut + { + get { return _isLockedOut; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _isLockedOut = value; + return _isLockedOut; + }, _isLockedOut, IsLockedOutSelector); + } + } + + //TODO: Figure out how to support all of this! - we cannot have NotImplementedExceptions because these get used by the IMembershipMemberService service so + // we'll just have them as generic get/set which don't interact with the db. + + [IgnoreDataMember] public string PasswordQuestion { get; set; } - [DataMember] - public string PasswordAnswer { get; set; } - [DataMember] + [IgnoreDataMember] + public string RawPasswordAnswerValue { get; set; } + [IgnoreDataMember] public string Comments { get; set; } - [DataMember] - public bool IsApproved { get; set; } - [DataMember] - public bool IsOnline { get; set; } - [DataMember] - public bool IsLockedOut { get; set; } - [DataMember] + [IgnoreDataMember] public DateTime CreateDate { get; set; } - [DataMember] + [IgnoreDataMember] public DateTime UpdateDate { get; set; } - [DataMember] + [IgnoreDataMember] public DateTime LastLoginDate { get; set; } - [DataMember] + [IgnoreDataMember] public DateTime LastPasswordChangeDate { get; set; } - [DataMember] + [IgnoreDataMember] public DateTime LastLockoutDate { get; set; } - - [DataMember] - public object ProfileId { get; set; } - [DataMember] - public IEnumerable Groups { get; set; } - + [IgnoreDataMember] + public int FailedPasswordAttempts { get; set; } + #endregion - + #region Implementation of IUser [DataMember] - public string Language { get; set; } + public string Name + { + get { return _name; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _name = value; + return _name; + }, _name, NameSelector); + } + } + + public IEnumerable AllowedSections + { + get { return _sectionCollection; } + } + + public void RemoveAllowedSection(string sectionAlias) + { + _sectionCollection.Remove(sectionAlias); + } + + public void AddAllowedSection(string sectionAlias) + { + if (_sectionCollection.Contains(sectionAlias) == false) + { + _sectionCollection.Add(sectionAlias); + } + } + + public IProfile ProfileData + { + get { return new UserProfile(this); } + } + + /// + /// Used internally to check if we need to add a section in the repository to the db + /// + internal IEnumerable AddedSections + { + get { return _addedSections; } + } + + /// + /// Used internally to check if we need to remove a section in the repository to the db + /// + internal IEnumerable RemovedSections + { + get { return _removedSections; } + } + + /// + /// Gets or sets the session timeout. + /// + /// + /// The session timeout. + /// + [DataMember] + public int SessionTimeout + { + get { return _sessionTimeout; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _sessionTimeout = value; + return _sessionTimeout; + }, _sessionTimeout, SessionTimeoutSelector); + } + } + + /// + /// Gets or sets the start content id. + /// + /// + /// The start content id. + /// + [DataMember] + public int StartContentId + { + get { return _startContentId; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _startContentId = value; + return _startContentId; + }, _startContentId, StartContentIdSelector); + } + } + + /// + /// Gets or sets the start media id. + /// + /// + /// The start media id. + /// + [DataMember] + public int StartMediaId + { + get { return _startMediaId; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _startMediaId = value; + return _startMediaId; + }, _startMediaId, StartMediaIdSelector); + } + } [DataMember] - public string DefaultPermissions { get; set; } - + public string Language + { + get { return _language; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _language = value; + return _language; + }, _language, LanguageSelector); + } + } + [DataMember] - public bool NoConsole { get; set; } + public IEnumerable DefaultPermissions + { + get { return _defaultPermissions; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _defaultPermissions = value; + return _defaultPermissions; + }, _defaultPermissions, DefaultPermissionsSelector); + } + } + + [IgnoreDataMember] + internal bool DefaultToLiveEditing + { + get { return _defaultToLiveEditing; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _defaultToLiveEditing = value; + return _defaultToLiveEditing; + }, _defaultToLiveEditing, DefaultToLiveEditingSelector); + } + } [IgnoreDataMember] public IUserType UserType { - get + get { return _userType; } + set { - var type = Groups.FirstOrDefault(); - return type as IUserType; + if (value.HasIdentity == false) + { + throw new InvalidOperationException("Cannot assign a User Type that has not been persisted"); + } + + SetPropertyValueAndDetectChanges(o => + { + _userType = value; + return _userType; + }, _userType, UserTypeSelector); } } #endregion + + /// + /// Whenever resetting occurs, clear the remembered add/removed collections, even if + /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still + /// be flagged as dirty. + /// + /// + public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + { + _addedSections.Clear(); + _removedSections.Clear(); + base.ResetDirtyProperties(rememberPreviouslyChangedProperties); + } + + /// + /// Handles the collection changed event in order for us to flag the AllowedSections property as changed + /// + /// + /// + void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged(AllowedSectionsSelector); + + if (e.Action == NotifyCollectionChangedAction.Add) + { + //remove from the removed/added sections (since people could add/remove all they want in one request) + _removedSections.RemoveAll(s => s == e.NewItems.Cast().First()); + _addedSections.RemoveAll(s => s == e.NewItems.Cast().First()); + + //add to the added sections + _addedSections.Add(e.NewItems.Cast().First()); + } + else if (e.Action == NotifyCollectionChangedAction.Remove) + { + //remove from the removed/added sections (since people could add/remove all they want in one request) + _removedSections.RemoveAll(s => s == e.OldItems.Cast().First()); + _addedSections.RemoveAll(s => s == e.OldItems.Cast().First()); + + //add to the added sections + _removedSections.Add(e.OldItems.Cast().First()); + } + } + + /// + /// Internal class used to wrap the user in a profile + /// + private class UserProfile : IProfile + { + private readonly IUser _user; + + public UserProfile(IUser user) + { + _user = user; + } + + public object Id + { + get { return _user.Id; } + set { _user.Id = (int)value; } + } + + public string Name + { + get { return _user.Name; } + set { _user.Name = value; } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs deleted file mode 100644 index 61a0d68ba7..0000000000 --- a/src/Umbraco.Core/Models/Membership/UserProfile.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models.Membership -{ - /// - /// Represents the Profile implementation for a backoffice User - /// - /// - /// Should be internal until a proper user/membership implementation - /// is part of the roadmap. - /// - [Serializable] - [DataContract(IsReference = true)] - internal class UserProfile : Profile, IUserProfile - { - public UserProfile() - { - SessionTimeout = 60; - _sectionCollection = new ObservableCollection(); - _addedSections = new List(); - _removedSections = new List(); - _sectionCollection.CollectionChanged += SectionCollectionChanged; - } - - private readonly List _addedSections; - private readonly List _removedSections; - private readonly ObservableCollection _sectionCollection; - private int _sessionTimeout; - private int _startContentId; - private int _startMediaId; - - private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo(x => x.SessionTimeout); - private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartContentId); - private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo(x => x.StartMediaId); - private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedSections); - - /// - /// Gets or sets the session timeout. - /// - /// - /// The session timeout. - /// - [DataMember] - public int SessionTimeout - { - get - { - return _sessionTimeout; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _sessionTimeout = value; - return _sessionTimeout; - }, _sessionTimeout, SessionTimeoutSelector); - } - } - - /// - /// Gets or sets the start content id. - /// - /// - /// The start content id. - /// - [DataMember] - public int StartContentId - { - get - { - return _startContentId; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _startContentId = value; - return _startContentId; - }, _startContentId, StartContentIdSelector); - } - } - - /// - /// Gets or sets the start media id. - /// - /// - /// The start media id. - /// - [DataMember] - public int StartMediaId - { - get - { - return _startMediaId; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _startMediaId = value; - return _startMediaId; - }, _startMediaId, StartMediaIdSelector); - } - } - - public IEnumerable AllowedSections - { - get { return _sectionCollection; } - } - - public void RemoveAllowedSection(string sectionAlias) - { - _sectionCollection.Remove(sectionAlias); - } - - public void AddAllowedSection(string sectionAlias) - { - if (!_sectionCollection.Contains(sectionAlias)) - { - _sectionCollection.Add(sectionAlias); - } - } - - /// - /// Whenever resetting occurs, clear the remembered add/removed collections, even if - /// rememberPreviouslyChangedProperties is true, the AllowedSections property will still - /// be flagged as dirty. - /// - /// - public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) - { - _addedSections.Clear(); - _removedSections.Clear(); - base.ResetDirtyProperties(rememberPreviouslyChangedProperties); - } - - /// - /// Used internally to check if we need to add a section in the repository to the db - /// - internal IEnumerable AddedSections - { - get { return _addedSections; } - } - - /// - /// Used internally to check if we need to remove a section in the repository to the db - /// - internal IEnumerable RemovedSections - { - get { return _removedSections; } - } - - /// - /// Handles the collection changed event in order for us to flag the AllowedSections property as changed - /// - /// - /// - void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - OnPropertyChanged(AllowedSectionsSelector); - - if (e.Action == NotifyCollectionChangedAction.Add) - { - //remove from the removed/added sections (since people could add/remove all they want in one request) - _removedSections.RemoveAll(s => s == e.NewItems.Cast().First()); - _addedSections.RemoveAll(s => s == e.NewItems.Cast().First()); - - //add to the added sections - _addedSections.Add(e.NewItems.Cast().First()); - } - else if (e.Action == NotifyCollectionChangedAction.Remove) - { - //remove from the removed/added sections (since people could add/remove all they want in one request) - _removedSections.RemoveAll(s => s == e.OldItems.Cast().First()); - _addedSections.RemoveAll(s => s == e.OldItems.Cast().First()); - - //add to the added sections - _removedSections.Add(e.OldItems.Cast().First()); - } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UserType.cs b/src/Umbraco.Core/Models/Membership/UserType.cs index b09a568c22..b5553c10d2 100644 --- a/src/Umbraco.Core/Models/Membership/UserType.cs +++ b/src/Umbraco.Core/Models/Membership/UserType.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; @@ -22,7 +23,13 @@ namespace Umbraco.Core.Models.Membership [DataMember] public string Name { get; set; } + /// + /// The set of default permissions for the user type + /// + /// + /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. + /// [DataMember] - public string Permissions { get; set; } + public IEnumerable Permissions { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Notification.cs b/src/Umbraco.Core/Models/Notification.cs new file mode 100644 index 0000000000..e5d3236b5e --- /dev/null +++ b/src/Umbraco.Core/Models/Notification.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Models +{ + internal class Notification + { + public Notification(int entityId, int userId, string action, Guid entityType) + { + EntityId = entityId; + UserId = userId; + Action = action; + EntityType = entityType; + } + + public int EntityId { get; private set; } + public int UserId { get; private set; } + public string Action { get; private set; } + public Guid EntityType { get; private set; } + } +} diff --git a/src/Umbraco.Core/Models/PreValue.cs b/src/Umbraco.Core/Models/PreValue.cs index 05b7a5f5df..a9e539a960 100644 --- a/src/Umbraco.Core/Models/PreValue.cs +++ b/src/Umbraco.Core/Models/PreValue.cs @@ -5,10 +5,17 @@ /// public class PreValue { + public PreValue(int id, string value, int sortOrder) + { + Id = id; + Value = value; + SortOrder = sortOrder; + } + public PreValue(int id, string value) { - Value = value; - Id = id; + Id = id; + Value = value; } public PreValue(string value) @@ -25,5 +32,10 @@ /// The database id for the pre-value field value /// public int Id { get; private set; } + + /// + /// The sort order stored for the pre-value field value + /// + public int SortOrder { get; private set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 3b2801aa24..1721efe5de 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo(x => x.Value); private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo(x => x.Version); - + /// /// Returns the instance of the tag support, by default tags are not enabled /// @@ -68,9 +68,14 @@ namespace Umbraco.Core.Models /// /// Returns the DatabaseType that the underlaying DataType is using to store its values /// - /// Only used internally when saving the property value + /// + /// Only used internally when saving the property value. + /// [IgnoreDataMember] - internal DataTypeDatabaseType DataTypeDatabaseType { get { return _propertyType.DataTypeDatabaseType; } } + internal DataTypeDatabaseType DataTypeDatabaseType + { + get { return _propertyType.DataTypeDatabaseType; } + } /// /// Returns the PropertyType, which this Property is based on diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index c1526a9ffb..b275a6912f 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class PropertyType : Entity, IEquatable { + private readonly bool _isExplicitDbType; private string _name; private string _alias; private string _description; @@ -30,16 +31,29 @@ namespace Umbraco.Core.Models public PropertyType(IDataTypeDefinition dataTypeDefinition) { if(dataTypeDefinition.HasIdentity) - DataTypeDefinitionId = dataTypeDefinition.Id; + _dataTypeDefinitionId = dataTypeDefinition.Id; - PropertyEditorAlias = dataTypeDefinition.PropertyEditorAlias; - DataTypeDatabaseType = dataTypeDefinition.DatabaseType; + _propertyEditorAlias = dataTypeDefinition.PropertyEditorAlias; + _dataTypeDatabaseType = dataTypeDefinition.DatabaseType; } internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType) + : this(propertyEditorAlias, dataTypeDatabaseType, false) { PropertyEditorAlias = propertyEditorAlias; - DataTypeDatabaseType = dataTypeDatabaseType; + } + + /// + /// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is. + /// + /// + /// + /// + internal PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType) + { + _isExplicitDbType = isExplicitDbType; + _propertyEditorAlias = propertyEditorAlias; + _dataTypeDatabaseType = dataTypeDatabaseType; } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -165,6 +179,9 @@ namespace Umbraco.Core.Models get { return _dataTypeDatabaseType; } set { + //don't allow setting this if an explicit declaration has been made in the ctor + if (_isExplicitDbType) return; + SetPropertyValueAndDetectChanges(o => { _dataTypeDatabaseType = value; @@ -377,8 +394,16 @@ namespace Umbraco.Core.Models //Check against Regular Expression for Legacy DataTypes - Validation exists and value is not null: if(string.IsNullOrEmpty(ValidationRegExp) == false && (value != null && string.IsNullOrEmpty(value.ToString()) == false)) { - var regexPattern = new Regex(ValidationRegExp); - return regexPattern.IsMatch(value.ToString()); + try + { + var regexPattern = new Regex(ValidationRegExp); + return regexPattern.IsMatch(value.ToString()); + } + catch + { + throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias)); + } + } //TODO Add PropertyEditor validation when its relevant to introduce diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 5a3c79ebc1..676eb05ef0 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -141,9 +141,21 @@ namespace Umbraco.Core.Models.PublishedContent if (GetPublishedContentTypeCallback != null) return GetPublishedContentTypeCallback(alias); - var contentType = itemType == PublishedItemType.Content - ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) - : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + IContentTypeComposition contentType; + switch (itemType) + { + case PublishedItemType.Content: + contentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias); + break; + case PublishedItemType.Media: + contentType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + break; + case PublishedItemType.Member: + contentType = ApplicationContext.Current.Services.MemberTypeService.Get(alias); + break; + default: + throw new ArgumentOutOfRangeException("itemType"); + } if (contentType == null) throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".", diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 295cbdef66..8ef077a968 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -80,11 +80,8 @@ namespace Umbraco.Core.Models.PublishedContent private void InitializeConverters() { - var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); - - var defaultConverters = converters - .Where(x => x.GetType().GetCustomAttribute(false) != null) - .ToArray(); + var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); + var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters; _converter = null; @@ -98,8 +95,14 @@ namespace Umbraco.Core.Models.PublishedContent else if (foundConverters.Length > 1) { //more than one was found, we need to first figure out if one of these is an Umbraco default value type converter - var nonDefault = foundConverters.Except(defaultConverters).ToArray(); - if (nonDefault.Length > 1) + //get the non-default and see if we have one + var nonDefault = foundConverters.Except(defaultConvertersWithAttributes.Select(x => x.Item1)).ToArray(); + if (nonDefault.Length == 1) + { + //there's only 1 custom converter registered that so use it + _converter = nonDefault[0]; + } + else if (nonDefault.Length > 1) { //this is not allowed, there cannot be more than 1 custom converter throw new InvalidOperationException( @@ -109,9 +112,31 @@ namespace Umbraco.Core.Models.PublishedContent ContentType.Alias, PropertyTypeAlias, nonDefault[1].GetType().FullName, nonDefault[0].GetType().FullName)); } + else + { + //we need to remove any converters that have been shadowed by another converter + var foundDefaultConvertersWithAttributes = defaultConvertersWithAttributes.Where(x => foundConverters.Contains(x.Item1)); + var shadowedTypes = foundDefaultConvertersWithAttributes.SelectMany(x => x.Item2.DefaultConvertersToShadow); + var shadowedDefaultConverters = foundConverters.Where(x => shadowedTypes.Contains(x.GetType())); + var nonShadowedDefaultConverters = foundConverters.Except(shadowedDefaultConverters).ToArray(); - //there's only 1 custom converter registered that so use it - _converter = nonDefault[0]; + if (nonShadowedDefaultConverters.Length == 1) + { + //assign to the single default converter + _converter = nonShadowedDefaultConverters[0]; + } + else if (nonShadowedDefaultConverters.Length > 1) + { + //this is not allowed, there cannot be more than 1 custom converter + throw new InvalidOperationException( + string.Format("Type '{2}' cannot be an IPropertyValueConverter" + + " for property '{1}' of content type '{0}' because type '{3}' has already been detected as a converter" + + " for that property, and only one converter can exist for a property.", + ContentType.Alias, PropertyTypeAlias, + nonShadowedDefaultConverters[1].GetType().FullName, nonShadowedDefaultConverters[0].GetType().FullName)); + } + } + } // get the cache levels, quietely fixing the inconsistencies (no need to throw, really) @@ -244,7 +269,7 @@ namespace Umbraco.Core.Models.PublishedContent : Enumerable.Empty(); } - class CompatConverter : PropertyValueConverterBase + private class CompatConverter : PropertyValueConverterBase { private readonly IPropertyEditorValueConverter _converter; diff --git a/src/Umbraco.Core/Models/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedItemType.cs index fd0f218cce..677205f61a 100644 --- a/src/Umbraco.Core/Models/PublishedItemType.cs +++ b/src/Umbraco.Core/Models/PublishedItemType.cs @@ -13,6 +13,11 @@ namespace Umbraco.Core.Models /// /// A media. /// - Media + Media, + + /// + /// A member. + /// + Member } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs b/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs index f0f3ebffd0..d981855f24 100644 --- a/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PreviewXmlDto.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.Rdbms internal class PreviewXmlDto { [Column("nodeId")] - [PrimaryKeyColumn(AutoIncrement = false, Name = "", OnColumns = "nodeId, versionId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsContentPreviewXml", OnColumns = "nodeId, versionId")] [ForeignKey(typeof(ContentDto), Column = "nodeId")] public int NodeId { get; set; } diff --git a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs index e9c79ae57e..9833d9ab74 100644 --- a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs @@ -30,7 +30,6 @@ namespace Umbraco.Core.Models.Rdbms public DateTime DateRegistered { get; set; } [Column("lastNotifiedDate")] - [Constraint(Default = "getdate()")] public DateTime LastNotified { get; set; } [Column("isActive")] diff --git a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs b/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs index 3315d1f7c1..6826377856 100644 --- a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms internal class UserLoginDto { [Column("contextID")] - [Index(IndexTypes.Clustered, Name = "umbracoUserLogins_Index")] + [Index(IndexTypes.Clustered, Name = "IX_umbracoUserLogins_Index")] public Guid ContextId { get; set; } [Column("userID")] diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs index e1f5b4a388..ae20482229 100644 --- a/src/Umbraco.Core/Models/RelationType.cs +++ b/src/Umbraco.Core/Models/RelationType.cs @@ -21,9 +21,18 @@ namespace Umbraco.Core.Models public RelationType(Guid childObjectType, Guid parentObjectType, string @alias) { + Mandate.ParameterNotNullOrEmpty(@alias, "alias"); _childObjectType = childObjectType; _parentObjectType = parentObjectType; _alias = alias; + Name = _alias; + } + + public RelationType(Guid childObjectType, Guid parentObjectType, string @alias, string name) + :this(childObjectType, parentObjectType, @alias) + { + Mandate.ParameterNotNullOrEmpty(name, "name"); + Name = name; } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs index 33bc3e6b86..387a345039 100644 --- a/src/Umbraco.Core/Models/Template.cs +++ b/src/Umbraco.Core/Models/Template.cs @@ -183,5 +183,11 @@ namespace Umbraco.Core.Models if (Key == Guid.Empty) Key = Guid.NewGuid(); } + + + public void SetMasterTemplate(ITemplate masterTemplate) + { + MasterTemplateId = new Lazy(() => masterTemplate.Id); + } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index b735f385d8..24261b4989 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Reflection; using Umbraco.Core.Models.EntityBase; @@ -53,6 +54,13 @@ namespace Umbraco.Core.Models Trashed = trashed; } + // for MySql + public UmbracoEntity(UInt64 trashed) + { + AdditionalData = new Dictionary(); + Trashed = trashed == 1; + } + public int CreatorId { get { return _creatorId; } diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs new file mode 100644 index 0000000000..edcc25ade8 --- /dev/null +++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.UI.WebControls; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + internal static class UmbracoEntityExtensions + { + + public static object GetAdditionalDataValueIgnoreCase(this IUmbracoEntity entity, string key, object defaultVal) + { + if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultVal; + return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal); + } + + public static bool IsContainer(this IUmbracoEntity entity) + { + if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false; + var val = entity.AdditionalData.GetValueIgnoreCase("IsContainer", null); + if (val is bool && (bool) val) + { + return true; + } + return false; + } + + } +} diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 603c508e0f..90d173b49a 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -246,12 +246,14 @@ namespace Umbraco.Core else if (destinationType == typeof(Double)) { Double value; - return Double.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + var input2 = NormalizeNumberDecimalSeparator(input); + return Double.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); } else if (destinationType == typeof(Single)) { Single value; - return Single.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + var input2 = NormalizeNumberDecimalSeparator(input); + return Single.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); } else if (destinationType == typeof(Char)) { @@ -292,7 +294,20 @@ namespace Umbraco.Core else if (destinationType == typeof(DateTime)) { DateTime value; - return DateTime.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + if (DateTime.TryParse(input, out value)) + { + switch (value.Kind) + { + case DateTimeKind.Unspecified: + case DateTimeKind.Utc: + return Attempt.Succeed(value); + case DateTimeKind.Local: + return Attempt.Succeed(value.ToUniversalTime()); + default: + throw new ArgumentOutOfRangeException(); + } + } + return Attempt.Fail(); } else if (destinationType == typeof(DateTimeOffset)) { @@ -307,7 +322,8 @@ namespace Umbraco.Core else if (destinationType == typeof(Decimal)) { Decimal value; - return Decimal.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + var input2 = NormalizeNumberDecimalSeparator(input); + return Decimal.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); } else if (destinationType == typeof(Version)) { @@ -319,6 +335,14 @@ namespace Umbraco.Core return null; // we can't decide... } + private readonly static char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','}; + + private static string NormalizeNumberDecimalSeparator(string s) + { + var normalized = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator[0]; + return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized); + } + internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname) { //TODO: Localise this exception @@ -526,7 +550,7 @@ namespace Umbraco.Core if (type == typeof(bool)) return XmlConvert.ToString((bool)value); if (type == typeof(byte)) return XmlConvert.ToString((byte)value); if (type == typeof(char)) return XmlConvert.ToString((char)value); - if (type == typeof(DateTime)) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.RoundtripKind); + if (type == typeof(DateTime)) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Unspecified); if (type == typeof(DateTimeOffset)) return XmlConvert.ToString((DateTimeOffset)value); if (type == typeof(decimal)) return XmlConvert.ToString((decimal)value); if (type == typeof(double)) return XmlConvert.ToString((double)value); diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs index 875fcd051f..a31598bfc8 100644 --- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs +++ b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using umbraco.interfaces; namespace Umbraco.Core.ObjectResolution @@ -8,7 +9,10 @@ namespace Umbraco.Core.ObjectResolution /// /// A resolver to return all IApplicationEvents objects /// - internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase + /// + /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances. + /// + internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable { private readonly LegacyStartupHandlerResolver _legacyResolver; @@ -53,7 +57,7 @@ namespace Umbraco.Core.ObjectResolution protected override bool SupportsClear { - get { return false; } + get { return false; } } protected override bool SupportsInsert @@ -61,7 +65,7 @@ namespace Umbraco.Core.ObjectResolution get { return false; } } - private class LegacyStartupHandlerResolver : ManyObjectsResolverBase + private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable { internal LegacyStartupHandlerResolver(IEnumerable legacyStartupHandlers) : base(legacyStartupHandlers) @@ -73,7 +77,72 @@ namespace Umbraco.Core.ObjectResolution { get { return Values; } } - } + public void Dispose() + { + ResetCollections(); + } + } + + private bool _disposed; + private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); + + /// + /// Gets a value indicating whether this instance is disposed. + /// + /// + /// true if this instance is disposed; otherwise, false. + /// + public bool IsDisposed + { + get { return _disposed; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// 2 + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass of this type implements a finalizer. + GC.SuppressFinalize(this); + } + + ~ApplicationEventsResolver() + { + // Run dispose but let the class know it was due to the finalizer running. + Dispose(false); + } + + private void Dispose(bool disposing) + { + // Only operate if we haven't already disposed + if (IsDisposed || disposing == false) return; + + using (new WriteLock(_disposalLocker)) + { + // Check again now we're inside the lock + if (IsDisposed) return; + + // Call to actually release resources. This method is only + // kept separate so that the entire disposal logic can be used as a VS snippet + DisposeResources(); + + // Indicate that the instance has been disposed. + _disposed = true; + } + } + + /// + /// Clear out all of the instances, we don't want them hanging around and cluttering up memory + /// + private void DisposeResources() + { + _legacyResolver.Dispose(); + ResetCollections(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs index 6aaf59106c..016a9c8f11 100644 --- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs +++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.ObjectResolution /// Important notes about this resolver: it does not support Insert or Remove and therefore does not support any ordering unless /// the types are marked with the WeightedPluginAttribute. /// - internal abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase + public abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase where TResolved : class where TResolver : ResolverBase { diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 0edaf68243..8bb5995891 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -322,7 +322,7 @@ namespace Umbraco.Core.ObjectResolution } /// - /// Clears the list of types. + /// Clears the list of types /// /// the resolver does not support clearing types. public virtual void Clear() @@ -336,6 +336,20 @@ namespace Umbraco.Core.ObjectResolution } } + /// + /// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances + /// created. Typically only used if a resolver is no longer used in an application and memory is to be GC'd + /// + internal void ResetCollections() + { + using (new WriteLock(_lock)) + { + _instanceTypes.Clear(); + _sortedValues = null; + _applicationInstances = null; + } + } + /// /// Inserts a type at the specified index. /// diff --git a/src/Umbraco.Core/Packaging/Models/InstallAction.cs b/src/Umbraco.Core/Packaging/Models/InstallAction.cs new file mode 100644 index 0000000000..257f0c5a03 --- /dev/null +++ b/src/Umbraco.Core/Packaging/Models/InstallAction.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.Serialization; +using System.Xml; + +namespace Umbraco.Core.Packaging.Models +{ + [Serializable] + [DataContract(IsReference = true)] + internal class InstallAction + { + public string Alias { get; set; } + + public string PackageName { get; set; } + + public string RunAt { get; set; }//NOTE Should this default to "install" + + public bool Undo { get; set; } + + public XmlNode XmlData { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs b/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs new file mode 100644 index 0000000000..7ee077855d --- /dev/null +++ b/src/Umbraco.Core/Packaging/Models/InstallationSummary.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Packaging.Models +{ + [Serializable] + [DataContract(IsReference = true)] + internal class InstallationSummary + { + public MetaData MetaData { get; set; } + public IEnumerable DataTypesInstalled { get; set; } + public IEnumerable LanguagesInstalled { get; set; } + public IEnumerable DictionaryItemsInstalled { get; set; } + public IEnumerable MacrosInstalled { get; set; } + public IEnumerable> FilesInstalled { get; set;} + public IEnumerable TemplatesInstalled { get; set; } + public IEnumerable DocumentTypesInstalled { get; set; } + public IEnumerable StylesheetsInstalled { get; set; } + public IEnumerable DocumentsInstalled { get; set; } + public IEnumerable InstallActions { get; set; } + public IEnumerable UninstallActions { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/Models/MetaData.cs b/src/Umbraco.Core/Packaging/Models/MetaData.cs new file mode 100644 index 0000000000..28f1af230e --- /dev/null +++ b/src/Umbraco.Core/Packaging/Models/MetaData.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Packaging.Models +{ + [Serializable] + [DataContract(IsReference = true)] + internal class MetaData + { + public string Name { get; set; } + public string Version { get; set; } + public string Url { get; set; } + public string License { get; set; } + public string LicenseUrl { get; set; } + public int ReqMajor { get; set; } + public int ReqMinor { get; set; } + public int ReqPatch { get; set; } + public string AuthorName { get; set; } + public string AuthorUrl { get; set; } + public string Readme { get; set; } + public string Control { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/Models/UninstallAction.cs b/src/Umbraco.Core/Packaging/Models/UninstallAction.cs new file mode 100644 index 0000000000..886c10afba --- /dev/null +++ b/src/Umbraco.Core/Packaging/Models/UninstallAction.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.Serialization; +using System.Xml; + +namespace Umbraco.Core.Packaging.Models +{ + [Serializable] + [DataContract(IsReference = true)] + internal class UninstallAction + { + public string Alias { get; set; } + + public string PackageName { get; set; } + + public string RunAt { get; set; }//NOTE Should this default to "install" + + public bool Undo { get; set; }//NOTE: Should thid default to "False"? + + public XmlNode XmlData { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageBuilding.cs b/src/Umbraco.Core/Packaging/PackageBuilding.cs new file mode 100644 index 0000000000..6cbb17b158 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageBuilding.cs @@ -0,0 +1,18 @@ +using Umbraco.Core.Services; + +namespace Umbraco.Core.Packaging +{ + internal interface IPackageBuilding + { + } + + internal class PackageBuilding : IPackageBuilding + { + private readonly PackagingService _packagingService; + + public PackageBuilding(PackagingService packagingService) + { + _packagingService = packagingService; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageExtraction.cs b/src/Umbraco.Core/Packaging/PackageExtraction.cs new file mode 100644 index 0000000000..d3de2a3da4 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageExtraction.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using Umbraco.Core.IO; + +namespace Umbraco.Core.Packaging +{ + internal interface IPackageExtraction + { + bool Extract(string packageFilePath, string destinationFolder); + string ExtractToTemporaryFolder(string packageFilePath); + string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml"); + } + + internal class PackageExtraction : IPackageExtraction + { + public bool Extract(string packageFilePath, string destinationFolder) + { + return true; + } + + public string ExtractToTemporaryFolder(string packageFilePath) + { + string tempDir = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), Guid.NewGuid().ToString("D")); + Directory.CreateDirectory(tempDir); + Extract(packageFilePath, tempDir); + return tempDir; + } + + public string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml") + { + return string.Empty; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs new file mode 100644 index 0000000000..5d8c292098 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -0,0 +1,35 @@ +using Umbraco.Core.Packaging.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Core.Packaging +{ + internal interface IPackageInstallation + { + InstallationSummary InstallPackage(string packageFilePath, int userId = 0); + MetaData GetPackageMetaData(string packageFilePath); + } + + internal class PackageInstallation : IPackageInstallation + { + private readonly PackagingService _packagingService; + private readonly PackageExtraction _packageExtraction; + + public PackageInstallation(PackagingService packagingService, PackageExtraction packageExtraction) + { + _packagingService = packagingService; + _packageExtraction = packageExtraction; + } + + public InstallationSummary InstallPackage(string packageFilePath, int userId = 0) + { + var summary = new InstallationSummary(); + return summary; + } + + public MetaData GetPackageMetaData(string packageFilePath) + { + var metaData = new MetaData(); + return metaData; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs index 60e176096f..273d605bef 100644 --- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs @@ -151,6 +151,21 @@ namespace Umbraco.Core.Persistence.Caching _keyTracker.Remove(key); } + public void Delete(Type type, int entityId) + { + var key = GetCompositeId(type, entityId); + if (_memoryCache != null) + { + _memoryCache.Remove(key); + } + else + { + HttpRuntime.Cache.Remove(key); + } + + _keyTracker.Remove(key); + } + /// /// Clear cache by type /// diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs new file mode 100644 index 0000000000..e495d61550 --- /dev/null +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Persistence.DatabaseModelDefinitions +{ + /// + /// Represents a database index definition retreived by querying the database + /// + internal class DbIndexDefinition + { + public virtual string IndexName { get; set; } + public virtual string TableName { get; set; } + public virtual string ColumnName { get; set; } + public virtual bool IsUnique { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs new file mode 100644 index 0000000000..f7bd9e31c1 --- /dev/null +++ b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Persistence +{ + internal static class DbConnectionExtensions + { + + public static DatabaseProviders DetectProviderFromConnectionString(string connString) + { + var builder = new DbConnectionStringBuilder {ConnectionString = connString}; + var allKeys = builder.Keys.Cast(); + + var mySql = new[] {"Server", "Database", "Uid", "Pwd"}; + if (mySql.All(x => allKeys.InvariantContains(x))) + { + return DatabaseProviders.MySql; + } + + if (allKeys.InvariantContains("Data Source") + //this dictionary is case insensitive + && builder["Data source"].ToString().InvariantContains(".sdf")) + { + return DatabaseProviders.SqlServerCE; + } + + return DatabaseProviders.SqlServer; + } + + public static bool IsConnectionAvailable(string connString, DatabaseProviders provider) + { + DbProviderFactory factory; + switch (provider) + { + case DatabaseProviders.SqlServer: + case DatabaseProviders.SqlAzure: + factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + break; + case DatabaseProviders.SqlServerCE: + factory = DbProviderFactories.GetFactory("System.Data.SqlServerCe.4.0"); + break; + case DatabaseProviders.MySql: + factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient"); + break; + case DatabaseProviders.PostgreSQL: + case DatabaseProviders.Oracle: + case DatabaseProviders.SQLite: + default: + throw new NotSupportedException("The provider " + provider + " is not supported"); + } + + var conn = factory.CreateConnection(); + if (conn == null) + { + throw new InvalidOperationException("Could not create a connection for provider " + provider); + } + conn.ConnectionString = connString; + using (var connection = conn) + { + return connection.IsAvailable(); + } + } + + public static bool IsAvailable(this IDbConnection connection) + { + try + { + connection.Open(); + connection.Close(); + } + catch (SqlException) + { + return false; + } + + return true; + } + + + } +} diff --git a/src/Umbraco.Core/Persistence/EntityNotFoundException.cs b/src/Umbraco.Core/Persistence/EntityNotFoundException.cs new file mode 100644 index 0000000000..1cc66a2ba2 --- /dev/null +++ b/src/Umbraco.Core/Persistence/EntityNotFoundException.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Persistence +{ + + //TODO: Would be good to use this exception type anytime we cannot find an entity + + /// + /// An exception used to indicate that an umbraco entity could not be found + /// + public class EntityNotFoundException : Exception + { + public object Id { get; private set; } + private readonly string _msg; + + public EntityNotFoundException(object id, string msg) + { + Id = id; + _msg = msg; + } + + public EntityNotFoundException(string msg) + { + _msg = msg; + } + + public override string Message + { + get { return _msg; } + } + + public override string ToString() + { + var result = base.ToString(); + + if (Id != null) + { + return "Umbraco entity (id: " + Id + ") not found. " + result; + } + + return result; + } + } +} diff --git a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs index 188a834b62..b64ceba9aa 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Factories NodeId = entity.Id, Email = entity.Email, LoginName = entity.Username, - Password = entity.Password, + Password = entity.RawPasswordValue, ContentVersionDto = BuildDto(entity as Member) }; return member; diff --git a/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs new file mode 100644 index 0000000000..207eea83b3 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs @@ -0,0 +1,64 @@ +using System; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberGroupFactory : IEntityFactory + { + + private readonly Guid _nodeObjectTypeId; + + public MemberGroupFactory() + { + _nodeObjectTypeId = new Guid(Constants.ObjectTypes.MemberGroup); + } + + #region Implementation of IEntityFactory + + public IMemberGroup BuildEntity(NodeDto dto) + { + var template = new MemberGroup + { + CreateDate = dto.CreateDate, + Id = dto.NodeId, + Key = dto.UniqueId.Value, + Name = dto.Text + }; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + template.ResetDirtyProperties(false); + return template; + } + + public NodeDto BuildDto(IMemberGroup entity) + { + var dto = new NodeDto + { + CreateDate = entity.CreateDate, + NodeId = entity.Id, + Level = 0, + NodeObjectType = _nodeObjectTypeId, + ParentId = -1, + Path = "", + SortOrder = 0, + Text = entity.Name, + Trashed = false, + UniqueId = entity.Key, + UserId = entity.CreatorId + }; + + if (entity.HasIdentity) + { + dto.NodeId = entity.Id; + dto.Path = "-1," + entity.Id; + } + + return dto; + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index 225b6173f5..7ff7c53b45 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -19,13 +19,13 @@ namespace Umbraco.Core.Persistence.Factories { var properties = CreateProperties(_memberTypes[dto.ContentTypeAlias], dto.Properties, dto.CreateDate); - var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, dto.ParentId, _memberTypes[dto.ContentTypeAlias]) + var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, _memberTypes[dto.ContentTypeAlias]) { Id = dto.NodeId, CreateDate = dto.CreateDate, UpdateDate = dto.UpdateDate, Name = dto.Text, - ProviderUserKey = dto.UniqueId, + ProviderUserKey = dto.NodeId, Trashed = dto.Trashed, Key = dto.UniqueId.Value, CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, @@ -33,7 +33,6 @@ namespace Umbraco.Core.Persistence.Factories Path = dto.Path, SortOrder = dto.SortOrder, Version = dto.VersionId, - ContentTypeAlias = dto.ContentTypeAlias, Properties = new PropertyCollection(properties) }; diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index 5caa8db61a..2a59f564e2 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.Factories { @@ -10,6 +11,8 @@ namespace Umbraco.Core.Persistence.Factories { public IMemberType BuildEntity(MemberTypeReadOnlyDto dto) { + var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var memberType = new MemberType(dto.ParentId) { Alias = dto.Alias, @@ -31,12 +34,12 @@ namespace Umbraco.Core.Persistence.Factories AllowedContentTypes = Enumerable.Empty() }; - var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType); + var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType, standardPropertyTypes); memberType.PropertyGroups = propertyTypeGroupCollection; - var propertyTypes = GetPropertyTypes(dto, memberType); - //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. - var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs; + var propertyTypes = GetPropertyTypes(dto, memberType, standardPropertyTypes); + + //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. foreach (var standardPropertyType in standardPropertyTypes) { if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue; @@ -46,16 +49,17 @@ namespace Umbraco.Core.Persistence.Factories //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key, - new Tuple(false, false, default(int))); + new MemberTypePropertyProfileAccess(false, false)); } memberType.PropertyTypes = propertyTypes; return memberType; } - private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType) + private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { - var propertyGroups = new PropertyGroupCollection(); + var propertyGroups = new PropertyGroupCollection(); + foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) { var group = new PropertyGroup(); @@ -85,11 +89,23 @@ namespace Umbraco.Core.Persistence.Factories { //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType memberType.MemberTypePropertyTypes.Add(typeDto.Alias, - new Tuple(typeDto.CanEdit, typeDto.ViewOnProfile, typeDto.Id.Value)); + new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit)); var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.PropertyEditorAlias, - typeDto.DbType.EnumParse(true)) + + //ensures that any built-in membership properties have their correct dbtype assigned no matter + //what the underlying data type is + var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty( + typeDto.Alias, + typeDto.DbType.EnumParse(true), + standardProps); + + var propertyType = new PropertyType( + typeDto.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) { Alias = typeDto.Alias, DataTypeDefinitionId = typeDto.DataTypeId, @@ -118,32 +134,47 @@ namespace Umbraco.Core.Persistence.Factories return propertyGroups; } - private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType) + + + private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { //Find PropertyTypes that does not belong to a PropertyTypeGroup var propertyTypes = new List(); - foreach (var propertyType in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) + foreach (var typeDto in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) { //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType - memberType.MemberTypePropertyTypes.Add(propertyType.Alias, - new Tuple(propertyType.CanEdit, propertyType.ViewOnProfile, propertyType.Id.Value)); - //PropertyType Collection - propertyTypes.Add(new PropertyType(propertyType.PropertyEditorAlias, - propertyType.DbType.EnumParse(true)) - { - Alias = propertyType.Alias, - DataTypeDefinitionId = propertyType.DataTypeId, - Description = propertyType.Description, - HelpText = propertyType.HelpText, - Id = propertyType.Id.Value, - Mandatory = propertyType.Mandatory, - Name = propertyType.Name, - SortOrder = propertyType.SortOrder, - ValidationRegExp = propertyType.ValidationRegExp, - PropertyGroupId = new Lazy(() => default(int)), - CreateDate = dto.CreateDate, - UpdateDate = dto.CreateDate - }); + memberType.MemberTypePropertyTypes.Add(typeDto.Alias, + new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit)); + + //ensures that any built-in membership properties have their correct dbtype assigned no matter + //what the underlying data type is + var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty( + typeDto.Alias, + typeDto.DbType.EnumParse(true), + standardProps); + + var propertyType = new PropertyType( + typeDto.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) + { + Alias = typeDto.Alias, + DataTypeDefinitionId = typeDto.DataTypeId, + Description = typeDto.Description, + HelpText = typeDto.HelpText, + Id = typeDto.Id.Value, + Mandatory = typeDto.Mandatory, + Name = typeDto.Name, + SortOrder = typeDto.SortOrder, + ValidationRegExp = typeDto.ValidationRegExp, + PropertyGroupId = new Lazy(() => default(int)), + CreateDate = dto.CreateDate, + UpdateDate = dto.CreateDate + }; + + propertyTypes.Add(propertyType); } return propertyTypes; } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index adbb3ad7f5..a7afd2bafc 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -11,17 +11,21 @@ namespace Umbraco.Core.Persistence.Factories private readonly int _id; private readonly DateTime _createDate; private readonly DateTime _updateDate; + //a callback to create a property type which can be injected via a contructor + private readonly Func _propertyTypeCtor; public PropertyGroupFactory(int id) { _id = id; + _propertyTypeCtor = (propertyEditorAlias, dbType, alias) => new PropertyType(propertyEditorAlias, dbType); } - public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate) + public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor) { _id = id; _createDate = createDate; _updateDate = updateDate; + _propertyTypeCtor = propertyTypeCtor; } #region Implementation of IEntityFactory,IEnumerable> @@ -55,22 +59,23 @@ namespace Umbraco.Core.Persistence.Factories foreach (var typeDto in typeDtos) { var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.DataTypeDto.PropertyEditorAlias, - typeDto.DataTypeDto.DbType.EnumParse(true)) - { - Alias = typeDto.Alias, - DataTypeDefinitionId = typeDto.DataTypeId, - Description = typeDto.Description, - Id = typeDto.Id, - Name = typeDto.Name, - HelpText = typeDto.HelpText, - Mandatory = typeDto.Mandatory, - SortOrder = typeDto.SortOrder, - ValidationRegExp = typeDto.ValidationRegExp, - PropertyGroupId = new Lazy(() => tempGroupDto.Id), - CreateDate = _createDate, - UpdateDate = _updateDate - }; + var propertyType = _propertyTypeCtor(typeDto.DataTypeDto.PropertyEditorAlias, + typeDto.DataTypeDto.DbType.EnumParse(true), + typeDto.Alias); + + propertyType.Alias = typeDto.Alias; + propertyType.DataTypeDefinitionId = typeDto.DataTypeId; + propertyType.Description = typeDto.Description; + propertyType.Id = typeDto.Id; + propertyType.Name = typeDto.Name; + propertyType.HelpText = typeDto.HelpText; + propertyType.Mandatory = typeDto.Mandatory; + propertyType.SortOrder = typeDto.SortOrder; + propertyType.ValidationRegExp = typeDto.ValidationRegExp; + propertyType.PropertyGroupId = new Lazy(() => tempGroupDto.Id); + propertyType.CreateDate = _createDate; + propertyType.UpdateDate = _updateDate; + //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 propertyType.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs index 8aac081c33..f8fa4bed16 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs @@ -17,12 +17,12 @@ namespace Umbraco.Core.Persistence.Factories public IRelation BuildEntity(RelationDto dto) { var entity = new Relation(dto.ParentId, dto.ChildId, _relationType) - { - Comment = dto.Comment, - CreateDate = dto.Datetime, - Id = dto.Id, - UpdateDate = dto.Datetime - }; + { + Comment = dto.Comment, + CreateDate = dto.Datetime, + Id = dto.Id, + UpdateDate = dto.Datetime + }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 entity.ResetDirtyProperties(false); @@ -32,13 +32,13 @@ namespace Umbraco.Core.Persistence.Factories public RelationDto BuildDto(IRelation entity) { var dto = new RelationDto - { - ChildId = entity.ChildId, - Comment = string.IsNullOrEmpty(entity.Comment) ? string.Empty : entity.Comment, - Datetime = entity.CreateDate, - ParentId = entity.ParentId, - RelationType = entity.RelationType.Id - }; + { + ChildId = entity.ChildId, + Comment = string.IsNullOrEmpty(entity.Comment) ? string.Empty : entity.Comment, + Datetime = entity.CreateDate, + ParentId = entity.ParentId, + RelationType = entity.RelationType.Id + }; if (entity.HasIdentity) dto.Id = entity.Id; diff --git a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs index 81020fa593..08a80397ea 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs @@ -10,11 +10,11 @@ namespace Umbraco.Core.Persistence.Factories public IRelationType BuildEntity(RelationTypeDto dto) { var entity = new RelationType(dto.ChildObjectType, dto.ParentObjectType, dto.Alias) - { - Id = dto.Id, - IsBidirectional = dto.Dual, - Name = dto.Name - }; + { + Id = dto.Id, + IsBidirectional = dto.Dual, + Name = dto.Name + }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 entity.ResetDirtyProperties(false); @@ -24,14 +24,14 @@ namespace Umbraco.Core.Persistence.Factories public RelationTypeDto BuildDto(IRelationType entity) { var dto = new RelationTypeDto - { - Alias = entity.Alias, - ChildObjectType = entity.ChildObjectType, - Dual = entity.IsBidirectional, - Name = entity.Name, - ParentObjectType = entity.ParentObjectType - }; - if(entity.HasIdentity) + { + Alias = entity.Alias, + ChildObjectType = entity.ChildObjectType, + Dual = entity.IsBidirectional, + Name = entity.Name, + ParentObjectType = entity.ParentObjectType + }; + if (entity.HasIdentity) dto.Id = entity.Id; return dto; diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 572bc7e624..51de2b2cab 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; @@ -18,24 +19,23 @@ namespace Umbraco.Core.Persistence.Factories public IUser BuildEntity(UserDto dto) { + var guidId = dto.Id.ToGuid(); var user = new User(_userType) - { - Id = dto.Id, - ProfileId = dto.Id, - StartContentId = dto.ContentStartId, - StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1, - Password = dto.Password, - Username = dto.Login, - Name = dto.UserName, - IsLockedOut = dto.Disabled, - IsApproved = dto.Disabled == false, - Email = dto.Email, - Language = dto.UserLanguage, - NoConsole = dto.NoConsole, - - //NOTE: The default permission come from the user type's default permissions - DefaultPermissions = _userType.Permissions - }; + { + Id = dto.Id, + Key = guidId, + StartContentId = dto.ContentStartId, + StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1, + RawPasswordValue = dto.Password, + Username = dto.Login, + Name = dto.UserName, + IsLockedOut = dto.NoConsole, + IsApproved = dto.Disabled == false, + Email = dto.Email, + Language = dto.UserLanguage, + //NOTE: The default permission come from the user type's default permissions + DefaultPermissions = _userType.Permissions + }; foreach (var app in dto.User2AppDtos) { @@ -58,8 +58,8 @@ namespace Umbraco.Core.Persistence.Factories Disabled = entity.IsApproved == false, Email = entity.Email, Login = entity.Username, - NoConsole = entity.NoConsole, - Password = entity.Password, + NoConsole = entity.IsLockedOut, + Password = entity.RawPasswordValue, UserLanguage = entity.Language, UserName = entity.Name, Type = short.Parse(entity.UserType.Id.ToString(CultureInfo.InvariantCulture)), diff --git a/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs index 66e79fd0cf..ad81d2b584 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserTypeFactory.cs @@ -1,4 +1,6 @@ -using Umbraco.Core.Models.Membership; +using System.Globalization; +using System.Linq; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories @@ -10,12 +12,14 @@ namespace Umbraco.Core.Persistence.Factories public IUserType BuildEntity(UserTypeDto dto) { var userType = new UserType - { - Alias = dto.Alias, - Id = dto.Id, - Name = dto.Name, - Permissions = dto.DefaultPermissions - }; + { + Alias = dto.Alias, + Id = dto.Id, + Name = dto.Name, + Permissions = dto.DefaultPermissions.IsNullOrWhiteSpace() + ? Enumerable.Empty() + : dto.DefaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)) + }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 userType.ResetDirtyProperties(false); @@ -27,7 +31,7 @@ namespace Umbraco.Core.Persistence.Factories var userType = new UserTypeDto { Alias = entity.Alias, - DefaultPermissions = entity.Permissions, + DefaultPermissions = entity.Permissions == null ? "" : string.Join("", entity.Permissions), Name = entity.Name }; diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs new file mode 100644 index 0000000000..da57738403 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs @@ -0,0 +1,34 @@ +using System.Collections.Concurrent; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + [MapperFor(typeof (IMemberGroup))] + [MapperFor(typeof (MemberGroup))] + public sealed class MemberGroupMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public MemberGroupMapper() + { + BuildMap(); + } + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.CreatorId, dto => dto.UserId); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Key, dto => dto.UniqueId); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs index d2bbeb0792..b5cb21c683 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs @@ -48,12 +48,12 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Email, dto => dto.Email); CacheMap(src => src.Username, dto => dto.LoginName); - CacheMap(src => src.Password, dto => dto.Password); + CacheMap(src => src.RawPasswordValue, dto => dto.Password); CacheMap(src => src.IsApproved, dto => dto.Integer); CacheMap(src => src.IsLockedOut, dto => dto.Integer); CacheMap(src => src.Comments, dto => dto.Text); - CacheMap(src => src.PasswordAnswer, dto => dto.VarChar); + CacheMap(src => src.RawPasswordAnswerValue, dto => dto.VarChar); CacheMap(src => src.PasswordQuestion, dto => dto.VarChar); CacheMap(src => src.FailedPasswordAttempts, dto => dto.Integer); CacheMap(src => src.LastLockoutDate, dto => dto.Date); diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs index 5d596fefe0..944e30c381 100644 --- a/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.ParentId, dto => dto.ParentId); CacheMap(src => src.RelationTypeId, dto => dto.RelationType); } - + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs index b417b7fb53..8748bd9033 100644 --- a/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Name, dto => dto.Name); CacheMap(src => src.ParentObjectType, dto => dto.ParentObjectType); } - + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs new file mode 100644 index 0000000000..74a0a05063 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/TagMapper.cs @@ -0,0 +1,43 @@ +using System.Collections.Concurrent; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + [MapperFor(typeof(Tag))] + [MapperFor(typeof(ITag))] + public sealed class TagMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public TagMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + if (PropertyInfoCache.IsEmpty) + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.Text, dto => dto.Tag); + CacheMap(src => src.Group, dto => dto.Group); + } + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs index f8bebb32d2..9c51f53596 100644 --- a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs @@ -31,14 +31,14 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Id, dto => dto.Id); CacheMap(src => src.Email, dto => dto.Email); CacheMap(src => src.Username, dto => dto.Login); - CacheMap(src => src.Password, dto => dto.Password); + CacheMap(src => src.RawPasswordValue, dto => dto.Password); CacheMap(src => src.Name, dto => dto.UserName); //NOTE: This column in the db is *not* used! //CacheMap(src => src.DefaultPermissions, dto => dto.DefaultPermissions); CacheMap(src => src.StartMediaId, dto => dto.MediaStartId); CacheMap(src => src.StartContentId, dto => dto.ContentStartId); CacheMap(src => src.IsApproved, dto => dto.Disabled); - CacheMap(src => src.NoConsole, dto => dto.NoConsole); + CacheMap(src => src.IsLockedOut, dto => dto.NoConsole); CacheMap(src => src.UserType, dto => dto.Type); CacheMap(src => src.Language, dto => dto.UserLanguage); } diff --git a/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs b/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs new file mode 100644 index 0000000000..cb4ea9120a --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/DataLossException.cs @@ -0,0 +1,22 @@ +using System; + +namespace Umbraco.Core.Persistence.Migrations +{ + /// + /// Used if a migration has executed but the whole process has failed and cannot be rolled back + /// + internal class DataLossException : Exception + { + public DataLossException(string msg) + : base(msg) + { + + } + + public DataLossException(string msg, Exception inner) + : base(msg, inner) + { + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index db3fbdcd4c..ace2178a55 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -120,7 +120,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("2b24165f-9782-4aa3-b459-1de4a4d21f60"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1040, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1040", SortOrder = 2, UniqueId = new Guid("21e798da-e06e-4eda-a511-ed257f78d4fa"), Text = "Related Links", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1041, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1041", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); - _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.Member, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now }); + _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); //TODO: We're not creating these for 7.0 @@ -135,7 +135,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "mediaPhoto.png" }); _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "mediaFile.png" }); - _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.Member, Icon = "icon-user", Thumbnail = "folder.png" }); + _database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "folder.png" }); } private void CreateUmbracoUserData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index d0d34478a4..7ad88f0739 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -79,7 +79,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial try { - _database.DropTable(tableName); + if (_database.TableExist(tableName)) + { + _database.DropTable(tableName); + } } catch (Exception ex) { @@ -121,67 +124,60 @@ namespace Umbraco.Core.Persistence.Migrations.Initial { var result = new DatabaseSchemaResult(); + //get the db index defs + result.DbIndexDefinitions = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(_database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + foreach (var item in OrderedTables.OrderBy(x => x.Key)) { var tableDefinition = DefinitionFactory.GetTableDefinition(item.Value); result.TableDefinitions.Add(tableDefinition); } - //Check tables in configured database against tables in schema - var tablesInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetTablesInSchema(_database).ToList(); - var tablesInSchema = result.TableDefinitions.Select(x => x.Name).ToList(); - //Add valid and invalid table differences to the result object - var validTableDifferences = tablesInDatabase.Intersect(tablesInSchema); - foreach (var tableName in validTableDifferences) - { - result.ValidTables.Add(tableName); - } - var invalidTableDifferences = - tablesInDatabase.Except(tablesInSchema) - .Union(tablesInSchema.Except(tablesInDatabase)); - foreach (var tableName in invalidTableDifferences) - { - result.Errors.Add(new Tuple("Table", tableName)); - } + ValidateDbTables(result); - //Check columns in configured database against columns in schema - var columnsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(_database); - var columnsPerTableInDatabase = columnsInDatabase.Select(x => string.Concat(x.TableName, ",", x.ColumnName)).ToList(); - var columnsPerTableInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => string.Concat(y.TableName, ",", y.Name))).ToList(); - //Add valid and invalid column differences to the result object - var validColumnDifferences = columnsPerTableInDatabase.Intersect(columnsPerTableInSchema); - foreach (var column in validColumnDifferences) - { - result.ValidColumns.Add(column); - } - var invalidColumnDifferences = columnsPerTableInDatabase.Except(columnsPerTableInSchema); - foreach (var column in invalidColumnDifferences) - { - result.Errors.Add(new Tuple("Column", column)); - } + ValidateDbColumns(result); + ValidateDbIndexes(result); + + ValidateDbConstraints(result); + + return result; + } + + private void ValidateDbConstraints(DatabaseSchemaResult result) + { //MySql doesn't conform to the "normal" naming of constraints, so there is currently no point in doing these checks. - //NOTE: At a later point we do other checks for MySql, but ideally it should be necessary to do special checks for different providers. + //TODO: At a later point we do other checks for MySql, but ideally it should be necessary to do special checks for different providers. + // ALso note that to get the constraints for MySql we have to open a connection which we currently have not. if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider) - return result; + return; //Check constraints in configured database against constraints in schema var constraintsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(_database).DistinctBy(x => x.Item3).ToList(); - var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("FK_")).Select(x => x.Item3).ToList(); - var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("PK_")).Select(x => x.Item3).ToList(); - var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.StartsWith("IX_")).Select(x => x.Item3).ToList(); + var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("FK_")).Select(x => x.Item3).ToList(); + var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("PK_")).Select(x => x.Item3).ToList(); + var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("IX_")).Select(x => x.Item3).ToList(); + var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList(); var unknownConstraintsInDatabase = constraintsInDatabase.Where( x => - x.Item3.StartsWith("FK_") == false && x.Item3.StartsWith("PK_") == false && - x.Item3.StartsWith("IX_") == false).Select(x => x.Item3).ToList(); + x.Item3.InvariantStartsWith("FK_") == false && x.Item3.InvariantStartsWith("PK_") == false && + x.Item3.InvariantStartsWith("IX_") == false).Select(x => x.Item3).ToList(); var foreignKeysInSchema = result.TableDefinitions.SelectMany(x => x.ForeignKeys.Select(y => y.Name)).ToList(); - var primaryKeysInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => y.PrimaryKeyName)).ToList(); - var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList(); + var primaryKeysInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => y.PrimaryKeyName)) + .Where(x => x.IsNullOrWhiteSpace() == false).ToList(); + //Add valid and invalid foreign key differences to the result object foreach (var unknown in unknownConstraintsInDatabase) { - if (foreignKeysInSchema.Contains(unknown) || primaryKeysInSchema.Contains(unknown) || indexesInSchema.Contains(unknown)) + if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown)) { result.ValidConstraints.Add(unknown); } @@ -190,40 +186,122 @@ namespace Umbraco.Core.Persistence.Migrations.Initial result.Errors.Add(new Tuple("Unknown", unknown)); } } - var validForeignKeyDifferences = foreignKeysInDatabase.Intersect(foreignKeysInSchema); + + //Foreign keys: + + var validForeignKeyDifferences = foreignKeysInDatabase.Intersect(foreignKeysInSchema, StringComparer.InvariantCultureIgnoreCase); foreach (var foreignKey in validForeignKeyDifferences) { result.ValidConstraints.Add(foreignKey); } - var invalidForeignKeyDifferences = foreignKeysInDatabase.Except(foreignKeysInSchema); + var invalidForeignKeyDifferences = + foreignKeysInDatabase.Except(foreignKeysInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(foreignKeysInSchema.Except(foreignKeysInDatabase, StringComparer.InvariantCultureIgnoreCase)); foreach (var foreignKey in invalidForeignKeyDifferences) { result.Errors.Add(new Tuple("Constraint", foreignKey)); } + + + //Primary keys: + //Add valid and invalid primary key differences to the result object - var validPrimaryKeyDifferences = primaryKeysInDatabase.Intersect(primaryKeysInSchema); + var validPrimaryKeyDifferences = primaryKeysInDatabase.Intersect(primaryKeysInSchema, StringComparer.InvariantCultureIgnoreCase); foreach (var primaryKey in validPrimaryKeyDifferences) { result.ValidConstraints.Add(primaryKey); } - var invalidPrimaryKeyDifferences = primaryKeysInDatabase.Except(primaryKeysInSchema); + var invalidPrimaryKeyDifferences = + primaryKeysInDatabase.Except(primaryKeysInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(primaryKeysInSchema.Except(primaryKeysInDatabase, StringComparer.InvariantCultureIgnoreCase)); foreach (var primaryKey in invalidPrimaryKeyDifferences) { result.Errors.Add(new Tuple("Constraint", primaryKey)); } + + //Constaints: + + //NOTE: SD: The colIndex checks above should really take care of this but I need to keep this here because it was here before + // and some schema validation checks might rely on this data remaining here! //Add valid and invalid index differences to the result object - var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema); + var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase); foreach (var index in validIndexDifferences) { result.ValidConstraints.Add(index); } - var invalidIndexDifferences = indexesInDatabase.Except(indexesInSchema); + var invalidIndexDifferences = + indexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(indexesInSchema.Except(indexesInDatabase, StringComparer.InvariantCultureIgnoreCase)); foreach (var index in invalidIndexDifferences) { result.Errors.Add(new Tuple("Constraint", index)); } + } - return result; + private void ValidateDbColumns(DatabaseSchemaResult result) + { + //Check columns in configured database against columns in schema + var columnsInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(_database); + var columnsPerTableInDatabase = columnsInDatabase.Select(x => string.Concat(x.TableName, ",", x.ColumnName)).ToList(); + var columnsPerTableInSchema = result.TableDefinitions.SelectMany(x => x.Columns.Select(y => string.Concat(y.TableName, ",", y.Name))).ToList(); + //Add valid and invalid column differences to the result object + var validColumnDifferences = columnsPerTableInDatabase.Intersect(columnsPerTableInSchema, StringComparer.InvariantCultureIgnoreCase); + foreach (var column in validColumnDifferences) + { + result.ValidColumns.Add(column); + } + + var invalidColumnDifferences = + columnsPerTableInDatabase.Except(columnsPerTableInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(columnsPerTableInSchema.Except(columnsPerTableInDatabase, StringComparer.InvariantCultureIgnoreCase)); + foreach (var column in invalidColumnDifferences) + { + result.Errors.Add(new Tuple("Column", column)); + } + } + + private void ValidateDbTables(DatabaseSchemaResult result) + { + //Check tables in configured database against tables in schema + var tablesInDatabase = SqlSyntaxContext.SqlSyntaxProvider.GetTablesInSchema(_database).ToList(); + var tablesInSchema = result.TableDefinitions.Select(x => x.Name).ToList(); + //Add valid and invalid table differences to the result object + var validTableDifferences = tablesInDatabase.Intersect(tablesInSchema, StringComparer.InvariantCultureIgnoreCase); + foreach (var tableName in validTableDifferences) + { + result.ValidTables.Add(tableName); + } + + var invalidTableDifferences = + tablesInDatabase.Except(tablesInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(tablesInSchema.Except(tablesInDatabase, StringComparer.InvariantCultureIgnoreCase)); + foreach (var tableName in invalidTableDifferences) + { + result.Errors.Add(new Tuple("Table", tableName)); + } + } + + private void ValidateDbIndexes(DatabaseSchemaResult result) + { + //These are just column indexes NOT constraints or Keys + //var colIndexesInDatabase = result.DbIndexDefinitions.Where(x => x.IndexName.InvariantStartsWith("IX_")).Select(x => x.IndexName).ToList(); + var colIndexesInDatabase = result.DbIndexDefinitions.Select(x => x.IndexName).ToList(); + var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList(); + + //Add valid and invalid index differences to the result object + var validColIndexDifferences = colIndexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase); + foreach (var index in validColIndexDifferences) + { + result.ValidIndexes.Add(index); + } + + var invalidColIndexDifferences = + colIndexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase) + .Union(indexesInSchema.Except(colIndexesInDatabase, StringComparer.InvariantCultureIgnoreCase)); + foreach (var index in invalidColIndexDifferences) + { + result.Errors.Add(new Tuple("Index", index)); + } } #region Events diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs index bc7f955902..9151ffd598 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,6 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial ValidTables = new List(); ValidColumns = new List(); ValidConstraints = new List(); + ValidIndexes = new List(); } public List> Errors { get; set; } @@ -29,6 +30,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial public List ValidConstraints { get; set; } + public List ValidIndexes { get; set; } + + internal IEnumerable DbIndexDefinitions { get; set; } + /// /// Determines the version of the currently installed database. /// @@ -39,43 +44,53 @@ namespace Umbraco.Core.Persistence.Migrations.Initial public Version DetermineInstalledVersion() { //If (ValidTables.Count == 0) database is empty and we return -> new Version(0, 0, 0); - if(ValidTables.Count == 0) + if (ValidTables.Count == 0) return new Version(0, 0, 0); //If Errors is empty or if TableDefinitions tables + columns correspond to valid tables + columns then we're at current version - if (!Errors.Any() || + if (Errors.Any() == false || (TableDefinitions.All(x => ValidTables.Contains(x.Name)) && TableDefinitions.SelectMany(definition => definition.Columns).All(x => ValidColumns.Contains(x.Name)))) return UmbracoVersion.Current; //If Errors contains umbracoApp or umbracoAppTree its pre-6.0.0 -> new Version(4, 10, 0); - if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.Equals("umbracoApp") || x.Item2.Equals("umbracoAppTree")))) + if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoApp") || x.Item2.InvariantEquals("umbracoAppTree")))) { //If Errors contains umbracoUser2app or umbracoAppTree foreignkey to umbracoApp exists its pre-4.8.0 -> new Version(4, 7, 0); if (Errors.Any(x => x.Item1.Equals("Constraint") - && (x.Item2.Contains("umbracoUser2app_umbracoApp") - || x.Item2.Contains("umbracoAppTree_umbracoApp")))) + && (x.Item2.InvariantContains("umbracoUser2app_umbracoApp") + || x.Item2.InvariantContains("umbracoAppTree_umbracoApp")))) { return new Version(4, 7, 0); } return new Version(4, 9, 0); } - + //if the error is for umbracoServer - if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.Equals("umbracoServer")))) + if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoServer")))) { return new Version(6, 0, 0); } - //if the error indicates a problem with the column cmsMacroProperty.macroPropertyType then it is not version 7 and the - // last db change we made was the umbracoServer in 6.1 - if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.Equals("cmsMacroProperty,macroPropertyType")))) + //if the error indicates a problem with the column cmsMacroProperty.macroPropertyType then it is not version 7 + // since these columns get removed in v7 + if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMacroProperty,macroPropertyType")))) { - return new Version(6, 1, 0); + //if the error is for this IX_umbracoNodeTrashed which is added in 6.2 AND in 7.1 but we do not have the above columns + // then it must mean that we aren't on 6.2 so must be 6.1 + if (Errors.Any(x => x.Item1.Equals("Index") && (x.Item2.InvariantEquals("IX_umbracoNodeTrashed")))) + { + return new Version(6, 1, 0); + } + else + { + //if there are no errors for that index, then the person must have 6.2 installed + return new Version(6, 2, 0); + } } - + return UmbracoVersion.Current; } @@ -113,6 +128,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial sb.AppendLine(string.Join(",", Errors.Where(x => x.Item1.Equals("Constraint")).Select(x => x.Item2))); sb.AppendLine(" "); } + //Index error summary + if (Errors.Any(x => x.Item1.Equals("Index"))) + { + sb.AppendLine("The following indexes were found in the database, but are not in the current schema:"); + sb.AppendLine(string.Join(",", Errors.Where(x => x.Item1.Equals("Index")).Select(x => x.Item2))); + sb.AppendLine(" "); + } //Unknown constraint error summary if (Errors.Any(x => x.Item1.Equals("Unknown"))) { diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs index b7e16614e6..b42b4aa6b8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationAttribute.cs @@ -6,8 +6,8 @@ namespace Umbraco.Core.Persistence.Migrations /// Represents the Migration attribute, which is used to mark classes as /// database migrations with Up/Down methods for pushing changes UP or pulling them DOWN. /// - [AttributeUsage(AttributeTargets.Class)] - public class MigrationAttribute : Attribute + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class MigrationAttribute : Attribute { public MigrationAttribute(string targetVersion, int sortOrder, string product) { @@ -16,8 +16,21 @@ namespace Umbraco.Core.Persistence.Migrations ProductName = product; } + public MigrationAttribute(string minimumCurrentVersion, string targetVersion, int sortOrder, string product) + { + TargetVersion = new Version(targetVersion); + MinimumCurrentVersion = new Version(minimumCurrentVersion); + SortOrder = sortOrder; + ProductName = product; + } + /// - /// Gets or sets the target version of this migration. + /// Gets the minimum current version for which this migration is allowed to execute + /// + public Version MinimumCurrentVersion { get; private set; } + + /// + /// Gets the target version of this migration. /// public Version TargetVersion { get; private set; } diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs index 9d7d845646..3d28e99c56 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs @@ -27,26 +27,8 @@ namespace Umbraco.Core.Persistence.Migrations /// public IEnumerable Migrations { - get { return GetSortedValues(); } + get { return Values; } } - /// - /// Override how we determine object weight, for this resolver we use the MigrationAttribute attribute - /// - /// - /// - protected override int GetObjectWeight(object o) - { - var type = o.GetType(); - var attr = type.GetCustomAttribute(true); - return attr == null ? DefaultPluginWeight : attr.SortOrder; - } - - protected override int DefaultPluginWeight - { - get { return 0; } //set's the default to 0 - set { base.DefaultPluginWeight = value; } - } - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs index ad5fcdebc5..cacfd87fbd 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -3,22 +3,23 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase; namespace Umbraco.Core.Persistence.Migrations { - /// + /// /// Represents the Migration Runner, which is used to apply migrations to /// the umbraco database. /// public class MigrationRunner { - private readonly Version _configuredVersion; + private readonly Version _currentVersion; private readonly Version _targetVersion; private readonly string _productName; - public MigrationRunner(Version configuredVersion, Version targetVersion, string productName) + public MigrationRunner(Version currentVersion, Version targetVersion, string productName) { - _configuredVersion = configuredVersion; + _currentVersion = currentVersion; _targetVersion = targetVersion; _productName = productName; } @@ -45,33 +46,50 @@ namespace Umbraco.Core.Persistence.Migrations { LogHelper.Info("Initializing database migrations"); - var foundMigrations = MigrationResolver.Current.Migrations; + var foundMigrations = MigrationResolver.Current.Migrations.ToArray(); + //filter all non-schema migrations var migrations = isUpgrade ? OrderedUpgradeMigrations(foundMigrations).ToList() : OrderedDowngradeMigrations(foundMigrations).ToList(); - if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _targetVersion, true), this)) + //SD: Why do we want this? + if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this)) return false; //Loop through migrations to generate sql - var context = new MigrationContext(databaseProvider, database); - foreach (MigrationBase migration in migrations) + var migrationContext = InitializeMigrations(migrations, database, databaseProvider, isUpgrade); + + try { - if (isUpgrade) + ExecuteMigrations(migrationContext, database); + } + catch (Exception ex) + { + //if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case, + //since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html + //so in that case we have to downgrade + + if (databaseProvider == DatabaseProviders.MySql) { - migration.GetUpExpressions(context); - LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name)); - } - else - { - migration.GetDownExpressions(context); - LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name)); + throw new DataLossException( + "An error occurred running a schema migration but the changes could not be rolled back. Error: " + ex.Message + ". In some cases, it may be required that the database be restored to it's original state before running this upgrade process again.", + ex); } + + //continue throwing the exception + throw; } + Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this); + + return true; + } + + private void ExecuteMigrations(IMigrationContext context, Database database) + { //Transactional execution of the sql that was generated from the found migrations - using (Transaction transaction = database.GetTransaction()) + using (var transaction = database.GetTransaction()) { int i = 1; foreach (var expression in context.Expressions) @@ -90,38 +108,89 @@ namespace Umbraco.Core.Persistence.Migrations transaction.Complete(); } + } - Migrated.RaiseEvent(new MigrationEventArgs(migrations, context, _configuredVersion, _targetVersion, false), this); + internal MigrationContext InitializeMigrations(List migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true) + { + //Loop through migrations to generate sql + var context = new MigrationContext(databaseProvider, database); + + foreach (var migration in migrations) + { + var baseMigration = migration as MigrationBase; + if (baseMigration != null) + { + if (isUpgrade) + { + baseMigration.GetUpExpressions(context); + LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", baseMigration.GetType().Name)); + } + else + { + baseMigration.GetDownExpressions(context); + LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", baseMigration.GetType().Name)); + } + } + else + { + //this is just a normal migration so we can only call Up/Down + if (isUpgrade) + { + migration.Up(); + LogHelper.Info(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name)); + } + else + { + migration.Down(); + LogHelper.Info(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name)); + } + } + } - return true; - } + return context; + } + /// + /// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version + /// + /// + /// internal IEnumerable OrderedUpgradeMigrations(IEnumerable foundMigrations) { var migrations = (from migration in foundMigrations - let migrationAttribute = migration.GetType().FirstAttribute() - where migrationAttribute != null - where - migrationAttribute.TargetVersion > _configuredVersion && - migrationAttribute.TargetVersion <= _targetVersion && - migrationAttribute.ProductName == _productName - orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending - select migration); + let migrationAttributes = migration.GetType().GetCustomAttributes(false) + from migrationAttribute in migrationAttributes + where migrationAttribute != null + where + migrationAttribute.TargetVersion > _currentVersion && + migrationAttribute.TargetVersion <= _targetVersion && + migrationAttribute.ProductName == _productName && + //filter if the migration specifies a minimum current version for which to execute + (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion) + orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending + select migration).Distinct(); return migrations; } + /// + /// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version + /// + /// + /// public IEnumerable OrderedDowngradeMigrations(IEnumerable foundMigrations) { var migrations = (from migration in foundMigrations - let migrationAttribute = migration.GetType().FirstAttribute() - where migrationAttribute != null - where - migrationAttribute.TargetVersion > _configuredVersion && - migrationAttribute.TargetVersion <= _targetVersion && - migrationAttribute.ProductName == _productName - orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending - - select migration); + let migrationAttributes = migration.GetType().GetCustomAttributes(false) + from migrationAttribute in migrationAttributes + where migrationAttribute != null + where + migrationAttribute.TargetVersion > _currentVersion && + migrationAttribute.TargetVersion <= _targetVersion && + migrationAttribute.ProductName == _productName && + //filter if the migration specifies a minimum current version for which to execute + (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion) + orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending + select migration).Distinct(); return migrations; } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs index 75eb5d1d1f..76a6a35db3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; @@ -41,7 +42,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions if (string.IsNullOrEmpty(ForeignKey.Name)) { - ForeignKey.Name = string.Format("FK_{0}_{1}", ForeignKey.ForeignTable, ForeignKey.PrimaryTable); + ForeignKey.Name = string.Format("FK_{0}_{1}_{2}", ForeignKey.ForeignTable, ForeignKey.PrimaryTable, ForeignKey.PrimaryColumns.First()); } return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint, diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs index df70d81b0a..6521d043a7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Column; +using System; +using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Column; using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Constraint; using Umbraco.Core.Persistence.Migrations.Syntax.Delete.DefaultConstraint; using Umbraco.Core.Persistence.Migrations.Syntax.Delete.ForeignKey; @@ -11,6 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete void Table(string tableName); IDeleteColumnFromTableSyntax Column(string columnName); IDeleteForeignKeyFromTableSyntax ForeignKey(); + [Obsolete("Do not use this construct as it does not work with MySql, use the syntax: Delete.ForeignKey().FromTable(\"umbracoUser2app\").ForeignColumn(... instead")] IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName); IDeleteDataSyntax FromTable(string tableName); IDeleteIndexForTableSyntax Index(); diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs index efcb5d2415..244884017c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs @@ -8,9 +8,21 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero { public override void Up() { - Delete.ForeignKey().FromTable("umbracoUser2app").ForeignColumn("app").ToTable("umbracoApp").PrimaryColumn("appAlias"); + //This will work on mysql and should work on mssql however the old keys were not named consistently with how the keys are + // structured now. So we need to do a check and manually remove them based on their old aliases. - Delete.ForeignKey().FromTable("umbracoAppTree").ForeignColumn("appAlias").ToTable("umbracoApp").PrimaryColumn("appAlias"); + if (this.Context.CurrentDatabaseProvider == DatabaseProviders.MySql) + { + Delete.ForeignKey().FromTable("umbracoUser2app").ForeignColumn("app").ToTable("umbracoApp").PrimaryColumn("appAlias"); + Delete.ForeignKey().FromTable("umbracoAppTree").ForeignColumn("appAlias").ToTable("umbracoApp").PrimaryColumn("appAlias"); + } + else + { + //These are the old aliases + Delete.ForeignKey("FK_umbracoUser2app_umbracoApp").OnTable("umbracoUser2app"); + Delete.ForeignKey("FK_umbracoUser2app_umbracoUser").OnTable("umbracoUser2app"); + } + } public override void Down() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs index 7ba5224e5a..20480ad534 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs @@ -1,5 +1,8 @@ using System; +using System.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { @@ -9,18 +12,45 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven [Migration("7.0.0", 5, GlobalSettings.UmbracoMigrationName)] public class AddIndexToCmsMacroPropertyTable : MigrationBase { + private readonly bool _skipIndexCheck; + + internal AddIndexToCmsMacroPropertyTable(bool skipIndexCheck) + { + _skipIndexCheck = skipIndexCheck; + } + + public AddIndexToCmsMacroPropertyTable() + { + + } + public override void Up() { - Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty") + var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[]{} : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + + //make sure it doesn't already exist + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacroProperty_Alias")) == false) + { + Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty") .OnColumn("macro") .Ascending() .OnColumn("macroPropertyAlias") .Unique(); + } + + } public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + Delete.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs index 3b4f1f8817..1be642b9d1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs @@ -1,5 +1,9 @@ using System; +using System.CodeDom; +using System.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { @@ -9,14 +13,40 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven [Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)] public class AddIndexToCmsMacroTable : MigrationBase { + private readonly bool _skipIndexCheck; + + internal AddIndexToCmsMacroTable(bool skipIndexCheck) + { + _skipIndexCheck = skipIndexCheck; + } + + public AddIndexToCmsMacroTable() + { + + } + public override void Up() { - Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique(); + var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + + //make sure it doesn't already exist + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacro_Alias")) == false) + { + Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique(); + } + } public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + Delete.Index("IX_cmsMacro_Alias").OnTable("cmsMacro"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs index 93bb3e4193..06fe7456d9 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + Delete.Column("propertyEditorAlias").FromTable("cmsDataType"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs index 2ab9673fdc..93afeee7bb 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs @@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty"); - Delete.ForeignKey("FK_cmsMacroProperty_cmsMacroPropertyType_id").OnTable("cmsMacroProperty"); - + Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id"); + Alter.Table("cmsMacroProperty").AddColumn("editorAlias").AsString(255).NotNullable().WithDefaultValue(""); //we need to get the data and create the migration scripts before we change the actual schema bits below! @@ -52,7 +52,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs index 4a549c7af3..07c2a02475 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs @@ -4,6 +4,8 @@ using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { @@ -25,12 +27,28 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { //create a new col which we will make a foreign key, but first needs to be populated with data. Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable(); - + //drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql. - Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship"); - //we need to drop the primary key - Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship"); + Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id"); + + //we need to drop the primary key, this is sql specific since MySQL has never had primary keys on this table + // at least since 6.0 and the new installation way but perhaps it had them way back in 4.x so we need to check + // it exists before trying to drop it. + if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql) + { + var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); + //this will let us know if this pk exists on this table + if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) > 0) + { + Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship"); + } + } + else + { + Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship"); + } + } private void Upgrade() @@ -115,7 +133,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException(); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } /// diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs index 581fd7ef07..b52dd4baed 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs @@ -1,6 +1,9 @@ using System; using System.Data; +using System.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { @@ -9,6 +12,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { public override void Up() { + var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + //add a foreign key to the parent id column too! Create.ForeignKey("FK_cmsTags_cmsTags") .FromTable("cmsTags") @@ -16,15 +28,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven .ToTable("cmsTags") .PrimaryColumn("id") .OnDelete(Rule.None) - .OnUpdate(Rule.None); + .OnUpdate(Rule.None); - //add an index to tag/group since it's queried often - Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered(); + //make sure it doesn't already exist + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsTags")) == false) + { + //add an index to tag/group since it's queried often + Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered(); + } + } public override void Down() { - throw new NotImplementedException(); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs index 4c2acdd5ae..a9b8e382d0 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs index df3a4f0450..2148b0ccbc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs index c7adc2af89..9264fdc646 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs index 976e2d05b1..12ad0b4f34 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs @@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Down() { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs index d16f64cc90..776aa47c12 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using System.Web.Script.Serialization; using System.Xml; +using System.Xml.Linq; using Newtonsoft.Json; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -26,81 +27,86 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { if (database != null) { - try + var dtSql = new Sql().Select("nodeId").From().Where(dto => dto.PropertyEditorAlias == Constants.PropertyEditors.RelatedLinksAlias); + var dataTypeIds = database.Fetch(dtSql); + + var propertyData = + database.Fetch( + "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID IN (@dataTypeIds))", new { dataTypeIds = dataTypeIds }); + if (!propertyData.Any()) return string.Empty; + + var nodesIdsWithProperty = propertyData.Select(x => x.NodeId).Distinct(); + var cmsContentXmlEntries = database.Fetch( + "WHERE nodeId in (@nodeIds)", new { nodeIds = nodesIdsWithProperty }); + var propertyTypeIds = propertyData.Select(x => x.PropertyTypeId).Distinct(); + var propertyTypes = database.Fetch( + "WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds }); + + foreach (var data in propertyData) { - var propertyData = - database.Fetch( - "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID = 1040)"); - foreach (var data in propertyData) + if (string.IsNullOrEmpty(data.Text) == false) { - if (!string.IsNullOrEmpty(data.Text)) + //fetch the current data (that's in xml format) + var xml = new XmlDocument(); + xml.LoadXml(data.Text); + + var links = new List(); + + //loop all the stored links + foreach (XmlNode node in xml.DocumentElement.ChildNodes) { - //var cs = ApplicationContext.Current.Services.ContentService; + var title = node.Attributes["title"].Value; + var type = node.Attributes["type"].Value; + var newwindow = node.Attributes["newwindow"].Value.Equals("1") ? true : false; + var lnk = node.Attributes["link"].Value; - //fetch the current data (that's in xml format) - var xml = new XmlDocument(); - xml.LoadXml(data.Text); + //create the links in the format the new prop editor expects it to be + var link = new ExpandoObject() as IDictionary; + link.Add("title", title); + link.Add("caption", title); + link.Add("link", lnk); + link.Add("newWindow", newwindow); + link.Add("type", type.Equals("internal") ? "internal" : "external"); + link.Add("internal", type.Equals("internal") ? lnk : null); - if (xml != null) + link.Add("edit", false); + link.Add("isInternal", type.Equals("internal")); + + links.Add((ExpandoObject)link); + } + + //store the serialized data + data.Text = JsonConvert.SerializeObject(links); + + database.Update(data); + + //now we need to update the cmsContentXml table + var propertyType = propertyTypes.SingleOrDefault(x => x.Id == data.PropertyTypeId); + if (propertyType != null) + { + var xmlItem = cmsContentXmlEntries.SingleOrDefault(x => x.NodeId == data.NodeId); + if (xmlItem != null) { - var links = new List(); - - //loop all the stored links - foreach (XmlNode node in xml.DocumentElement.ChildNodes) + var x = XElement.Parse(xmlItem.Xml); + var prop = x.Element(propertyType.Alias); + if (prop != null) { - var title = node.Attributes["title"].Value; - var type = node.Attributes["type"].Value; - var newwindow = node.Attributes["newwindow"].Value.Equals("1") ? true : false; - var lnk = node.Attributes["link"].Value; - - //create the links in the format the new prop editor expects it to be - var link = new ExpandoObject() as IDictionary; - link.Add("title", title); - link.Add("caption", title); - link.Add("link", lnk); - link.Add("newWindow", newwindow); - link.Add("type", type.Equals("internal") ? "internal" : "external"); - link.Add("internal", type.Equals("internal") ? lnk : null); - - link.Add("edit", false); - link.Add("isInternal", type.Equals("internal")); - - //try - //{ - // if (type.Equals("internal")) - // { - // int nodeId; - // if (int.TryParse(lnk, out nodeId)) - // link.Add("internalName", cs.GetById(nodeId).Name); - // } - //} - //catch (Exception ex) - //{ - // LogHelper.Error("Exception was thrown when trying to update related links property data, fetching internal node id", ex); - //} - - links.Add((ExpandoObject) link); + prop.ReplaceAll(new XCData(data.Text)); + database.Update(xmlItem); } - - //store the serialized data - data.Text = JsonConvert.SerializeObject(links); - - database.Update(data); } } + + } } - catch (Exception ex) - { - LogHelper.Error("Exception was thrown when trying to update related links property data", ex); - } } return string.Empty; } public override void Down() { - throw new NotImplementedException(); + throw new DataLossException("Cannot downgrade from a version 7 database to a prior version, the database schema has already been modified"); } } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs new file mode 100644 index 0000000000..bb5bf56386 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenOneZero +{ + //see: http://issues.umbraco.org/issue/U4-4430 - Except this one is specific to 7 because + // we've modified the tagRelationship PK already. + //We have to target this specifically however to ensure this DOES NOT execute if upgrading from a version previous to 7.0, + // this is because when the 7.0.0 migrations are executed, this primary key get's created so if this migration is also executed + // we will get exceptions because it is trying to create the PK two times. + + [Migration("7.0.0", "7.1.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AssignMissingPrimaryForMySqlKeys : MigrationBase + { + public override void Up() + { + if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql) + { + var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); + + //This should be 3 because this table has 3 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsTagRelationship") + .OnTable("cmsTagRelationship") + .Columns(new[] { "nodeId", "propertyTypeId", "tagId" }); + } + + } + } + + public override void Down() + { + //don't do anything, these keys should have always existed! + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs index 567f641e5c..05edf00062 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs @@ -2,6 +2,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { + [Migration("6.0.0", 10, GlobalSettings.UmbracoMigrationName)] public class DeleteAppTables : MigrationBase { @@ -14,6 +15,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix public override void Down() { + //This cannot be rolled back!! + throw new DataLossException("Cannot rollback migration " + typeof(DeleteAppTables) + " the db tables umbracoAppTree and umbracoApp have been droppped"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs index 2bd03f15bc..25efd73082 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs @@ -10,15 +10,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { Alter.Table("cmsPropertyTypeGroup").AddColumn("parentGroupId").AsInt16().Nullable(); - Create.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup") + Create.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id") .FromTable("cmsPropertyTypeGroup").ForeignColumn("parentGroupId") .ToTable("cmsPropertyTypeGroup").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); } public override void Down() { - Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup").OnTable("cmsPropertyTypeGroup"); - + Delete.ForeignKey().FromTable("cmsPropertyTypeGroup").ForeignColumn("parentGroupId").ToTable("cmsPropertyTypeGroup").PrimaryColumn("id"); + Delete.Column("parentGroupId").FromTable("cmsPropertyTypeGroup"); } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs index 757e5fd4f0..5c39f92b21 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs @@ -13,11 +13,13 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixOneZero { public override void Up() { - base.Context.Database.CreateTable(); + //NOTE: This isn't the correct way to do this but to manually create this table with the Create syntax is a pain in the arse + Context.Database.CreateTable(); } public override void Down() { + Delete.Table("umbracoServer"); } } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs index fa1b24d1f6..5c36826d02 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs @@ -4,7 +4,8 @@ using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("6.2.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("6.2.0", 3, GlobalSettings.UmbracoMigrationName)] public class AddChangeDocumentTypePermission : MigrationBase { public override void Up() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs index e4d2496903..446c35d3ad 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs @@ -1,37 +1,65 @@ using System; +using System.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Migrations.Initial; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("6.2.0", 1, GlobalSettings.UmbracoMigrationName)] public class AdditionalIndexesAndKeys : MigrationBase { public override void Up() - { - Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered(); + { + + var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + + //do not create any indexes if they already exist in the database + + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeTrashed")) == false) + { + Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered(); + } + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContentVersion_ContentId")) == false) + { + Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered(); + } + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDocument_published")) == false) + { + Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered(); + } + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDocument_newest")) == false) + { + Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered(); + } + + //we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so + // it follows the standard naming convention + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("umbracoUserLogins_Index"))) + { + Delete.Index("umbracoUserLogins_Index").OnTable("umbracoUserLogins"); + } + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoUserLogins_Index")) == false) + { + Create.Index("IX_umbracoUserLogins_Index").OnTable("umbracoUserLogins").OnColumn("contextID").Ascending().WithOptions().Clustered(); + } } public override void Down() { - throw new NotImplementedException(); - } - } - - [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)] - public class ChangePasswordColumn : MigrationBase - { - public override void Up() - { - //up to 500 chars - Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable(); - } - - public override void Down() - { - throw new NotImplementedException(); + Delete.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode"); + Delete.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion"); + Delete.Index("IX_cmsDocument_published").OnTable("cmsDocument"); + Delete.Index("IX_cmsDocument_newest").OnTable("cmsDocument"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs new file mode 100644 index 0000000000..a972b7eeb0 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs @@ -0,0 +1,90 @@ +using System.Linq; +using System.Web.UI; +using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero +{ + //see: http://issues.umbraco.org/issue/U4-4430 + [Migration("7.1.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AssignMissingPrimaryForMySqlKeys : MigrationBase + { + public override void Up() + { + if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql) + { + var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsContentType2ContentType") + .OnTable("cmsContentType2ContentType") + .Columns(new[] {"parentContentTypeId", "childContentTypeId"}); + } + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentTypeAllowedContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsContentTypeAllowedContentType") + .OnTable("cmsContentTypeAllowedContentType") + .Columns(new[] { "Id", "AllowedId" }); + } + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsDocumentType") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsDocumentType") + .OnTable("cmsDocumentType") + .Columns(new[] { "contentTypeNodeId", "templateNodeId" }); + } + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsMember2MemberGroup") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsMember2MemberGroup") + .OnTable("cmsMember2MemberGroup") + .Columns(new[] { "Member", "MemberGroup" }); + } + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("cmsPreviewXml") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsContentPreviewXml") + .OnTable("cmsPreviewXml") + .Columns(new[] { "nodeId", "versionId" }); + } + + //This should be 2 because this table has 2 keys + if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2app") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_user2app") + .OnTable("umbracoUser2app") + .Columns(new[] { "user", "app" }); + } + + //This should be 2 because this table has 3 keys + if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodeNotify") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_umbracoUser2NodeNotify") + .OnTable("umbracoUser2NodeNotify") + .Columns(new[] { "userId", "nodeId", "action" }); + } + + //This should be 2 because this table has 3 keys + if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodePermission") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_umbracoUser2NodePermission") + .OnTable("umbracoUser2NodePermission") + .Columns(new[] { "userId", "nodeId", "permission" }); + } + } + } + + public override void Down() + { + //don't do anything, these keys should have always existed! + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs new file mode 100644 index 0000000000..437a188013 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs @@ -0,0 +1,22 @@ +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero +{ + [Migration("7.1.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)] + public class ChangePasswordColumn : MigrationBase + { + public override void Up() + { + //up to 500 chars + Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable(); + } + + public override void Down() + { + //back to 125 chars + Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(125).NotNullable(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs new file mode 100644 index 0000000000..555c0bcdf1 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero +{ + [Migration("7.1.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("6.2.0", 4, GlobalSettings.UmbracoMigrationName)] + public class UpdateToNewMemberPropertyAliases : MigrationBase + { + public override void Up() + { + Execute.Code(Update); + } + + internal static string Update(Database database) + { + if (database != null) + { + var aliasMap = new Dictionary + { + {"umbracoPasswordRetrievalQuestionPropertyTypeAlias", Constants.Conventions.Member.PasswordQuestion}, + {"umbracoPasswordRetrievalAnswerPropertyTypeAlias", Constants.Conventions.Member.PasswordAnswer}, + {"umbracoCommentPropertyTypeAlias", Constants.Conventions.Member.Comments}, + {"umbracoApprovePropertyTypeAlias", Constants.Conventions.Member.IsApproved}, + {"umbracoLockPropertyTypeAlias", Constants.Conventions.Member.IsLockedOut}, + {"umbracoLastLoginPropertyTypeAlias", Constants.Conventions.Member.LastLoginDate}, + {"umbracoMemberLastPasswordChange", Constants.Conventions.Member.LastPasswordChangeDate}, + {"umbracoMemberLastLockout", Constants.Conventions.Member.LastLockoutDate}, + {"umbracoFailedPasswordAttemptsPropertyTypeAlias", Constants.Conventions.Member.FailedPasswordAttempts} + }; + + + //This query is structured to work with MySql, SQLCE and SqlServer: + // http://issues.umbraco.org/issue/U4-3876 + + const string propertyTypeUpdateSql = @"UPDATE cmsPropertyType +SET Alias = @newAlias +WHERE Alias = @oldAlias AND contentTypeId IN ( +SELECT nodeId FROM (SELECT DISTINCT cmsContentType.nodeId FROM cmsPropertyType +INNER JOIN cmsContentType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId +INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id +WHERE umbracoNode.nodeObjectType = @objectType) x)"; + + const string xmlSelectSql = @"SELECT cmsContentXml.* FROM cmsContentXml +INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id +WHERE umbracoNode.nodeObjectType = @objectType"; + + using (var trans = database.GetTransaction()) + { + try + { + + //Upate all of the property type aliases + foreach (var map in aliasMap) + { + database.Execute(propertyTypeUpdateSql, new { newAlias = map.Value, oldAlias = map.Key, objectType = Constants.ObjectTypes.MemberType }); + } + + //Update all of the XML + var items = database.Fetch(xmlSelectSql, new { objectType = Constants.ObjectTypes.Member }); + foreach (var item in items) + { + foreach (var map in aliasMap) + { + item.Xml = item.Xml.Replace("<" + map.Key + ">", "<" + map.Value + ">"); + item.Xml = item.Xml.Replace("", ""); + } + database.Update(item); + } + + trans.Complete(); + } + catch (Exception ex) + { + LogHelper.Error("Exception was thrown when trying to upgrade old member aliases to the new ones", ex); + throw; + } + } + + + } + return string.Empty; + } + + public override void Down() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index 7ae81aaa16..6d479b9eb1 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -21,14 +21,18 @@ namespace Umbraco.Core.Persistence /// /// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter /// - /// /// /// - public static string EscapeAtSymbols(this Database db, string value) + public static string EscapeAtSymbols(string value) { - //this fancy regex will only match a single @ not a double, etc... - var regex = new Regex("(?(this Database db) @@ -53,11 +57,20 @@ namespace Umbraco.Core.Persistence using (var tr = db.GetTransaction()) { - db.BulkInsertRecords(collection, tr); + db.BulkInsertRecords(collection, tr, true); } } - public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr) + /// + /// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction + /// when finished + /// + /// + /// + /// + /// + /// + public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr, bool commitTrans = false) { //don't do anything if there are no records. if (collection.Any() == false) @@ -91,11 +104,17 @@ namespace Umbraco.Core.Persistence } } - tr.Complete(); + if (commitTrans) + { + tr.Complete(); + } } catch { - tr.Dispose(); + if (commitTrans) + { + tr.Dispose(); + } throw; } } @@ -336,6 +355,14 @@ namespace Umbraco.Core.Persistence creation.UninstallDatabaseSchema(); } + internal static void CreateDatabaseSchemaDo(this Database db, bool guardConfiguration) + { + if (guardConfiguration && ApplicationContext.Current.IsConfigured) + throw new Exception("Umbraco is already configured!"); + + CreateDatabaseSchemaDo(db); + } + internal static void CreateDatabaseSchemaDo(this Database db) { NewTable += PetaPocoExtensions_NewTable; diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs index 378ade59dd..4e1a3de28f 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs @@ -37,7 +37,8 @@ namespace Umbraco.Core.Persistence var tableNameAttribute = type.FirstAttribute(); string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value; - var syntax = string.Format("{0}.{1}", + //need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177 + var syntax = string.Format("({0}.{1})", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName), SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(columnName)); diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs index 44570adcd4..1a087ad8d7 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Querying { @@ -8,6 +9,41 @@ namespace Umbraco.Core.Persistence.Querying /// internal class BaseExpressionHelper { + protected string HandleStringComparison(string col, string val, string verb, TextColumnType columnType) + { + switch (verb) + { + case "SqlWildcard": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, RemoveQuote(val), columnType); + case "Equals": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, RemoveQuote(val), columnType); + case "StartsWith": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, RemoveQuote(val), columnType); + case "EndsWith": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, RemoveQuote(val), columnType); + case "Contains": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, RemoveQuote(val), columnType); + case "InvariantEquals": + case "SqlEquals": + //recurse + return HandleStringComparison(col, val, "Equals", columnType); + case "InvariantStartsWith": + case "SqlStartsWith": + //recurse + return HandleStringComparison(col, val, "StartsWith", columnType); + case "InvariantEndsWith": + case "SqlEndsWith": + //recurse + return HandleStringComparison(col, val, "EndsWith", columnType); + case "InvariantContains": + case "SqlContains": + //recurse + return HandleStringComparison(col, val, "Contains", columnType); + default: + throw new ArgumentOutOfRangeException("verb"); + } + } + public virtual string GetQuotedValue(object value, Type fieldType, Func escapeCallback = null, Func shouldQuoteCallback = null) { if (value == null) return "NULL"; @@ -25,7 +61,7 @@ namespace Umbraco.Core.Persistence.Querying { //if (TypeSerializer.CanCreateFromString(fieldType)) //{ - // return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'"; + // return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'"; //} throw new NotSupportedException( @@ -46,33 +82,24 @@ namespace Umbraco.Core.Persistence.Querying if (fieldType == typeof(DateTime)) { - return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'"; + return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'"; } if (fieldType == typeof(bool)) return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture); - return ShouldQuoteValue(fieldType) - ? "'" + EscapeParam(value) + "'" + return shouldQuoteCallback(fieldType) + ? "'" + escapeCallback(value) + "'" : value.ToString(); } public virtual string EscapeParam(object paramValue) { - return paramValue.ToString().Replace("'", "''"); + return paramValue == null + ? string.Empty + : SqlSyntaxContext.SqlSyntaxProvider.EscapeString(paramValue.ToString()); } - - public virtual string EscapeAtArgument(string exp) - { - /*if (exp.StartsWith("@")) - return string.Concat("@", exp);*/ - - if (exp.Contains("@")) - return exp.Replace("@", "@@"); - - return exp; - } - + public virtual bool ShouldQuoteValue(Type fieldType) { return true; diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 738cc8a23c..e3ff272cee 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -2,9 +2,11 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; +using System.Linq; using System.Linq.Expressions; using System.Text; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Querying { @@ -203,13 +205,12 @@ namespace Umbraco.Core.Persistence.Querying { if (c.Value == null) return "null"; - else if (c.Value.GetType() == typeof(bool)) + if (c.Value is bool) { object o = GetQuotedValue(c.Value, c.Value.GetType()); return string.Format("({0}={1})", GetQuotedTrueValue(), o); } - else - return GetQuotedValue(c.Value, c.Value.GetType()); + return GetQuotedValue(c.Value, c.Value.GetType()); } protected virtual string VisitUnary(UnaryExpression u) @@ -245,12 +246,31 @@ namespace Umbraco.Core.Persistence.Querying return string.Format("upper({0})", r); case "ToLower": return string.Format("lower({0})", r); + case "SqlWildcard": case "StartsWith": - return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper()))); case "EndsWith": - return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper())); case "Contains": - return string.Format("{0} like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper())); + case "Equals": + case "SqlStartsWith": + case "SqlEndsWith": + case "SqlContains": + case "SqlEquals": + case "InvariantStartsWith": + case "InvariantEndsWith": + case "InvariantContains": + case "InvariantEquals": + //default + var colType = TextColumnType.NVarchar; + //then check if this arg has been passed in + if (m.Arguments.Count > 1) + { + var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType)); + if (colTypeArg != null) + { + colType = (TextColumnType) ((ConstantExpression) colTypeArg).Value; + } + } + return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType); case "Substring": var startIndex = Int32.Parse(args[0].ToString()) + 1; if (args.Count == 2) diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs index 555b87f6e4..9a27cc8183 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs @@ -243,18 +243,41 @@ namespace Umbraco.Core.Persistence.Querying args.RemoveAt(0); } + //TODO: We should probably add the same logic we've done for ModelToSqlExpressionHelper with checking for: + // InvariantStartsWith, InvariantEndsWith, SqlWildcard, etc... + // since we should be able to easily handle that with the Poco objects too. + switch (m.Method.Name) { case "ToUpper": return string.Format("upper({0})", r); case "ToLower": return string.Format("lower({0})", r); + case "SqlWildcard": case "StartsWith": - return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper()))); case "EndsWith": - return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper())); case "Contains": - return string.Format("upper({0}) like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper())); + case "Equals": + case "SqlStartsWith": + case "SqlEndsWith": + case "SqlContains": + case "SqlEquals": + case "InvariantStartsWith": + case "InvariantEndsWith": + case "InvariantContains": + case "InvariantEquals": + //default + var colType = TextColumnType.NVarchar; + //then check if this arg has been passed in + if (m.Arguments.Count > 1) + { + var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType)); + if (colTypeArg != null) + { + colType = (TextColumnType)((ConstantExpression)colTypeArg).Value; + } + } + return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType); case "Substring": var startIndex = Int32.Parse(args[0].ToString()) + 1; if (args.Count == 2) diff --git a/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs new file mode 100644 index 0000000000..cbecc0a591 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// String extension methods used specifically to translate into SQL + /// + internal static class SqlStringExtensions + { + public static bool SqlWildcard(this string str, string txt, TextColumnType columnType) + { + var wildcardmatch = new Regex("^" + Regex.Escape(txt). + //deal with any wildcard chars % + Replace(@"\%", ".*") + "$"); + + return wildcardmatch.IsMatch(str); + } + + public static bool SqlContains(this string str, string txt, TextColumnType columnType) + { + return str.InvariantContains(txt); + } + + public static bool SqlEquals(this string str, string txt, TextColumnType columnType) + { + return str.InvariantEquals(txt); + } + + public static bool SqlStartsWith(this string str, string txt, TextColumnType columnType) + { + return str.InvariantStartsWith(txt); + } + + public static bool SqlEndsWith(this string str, string txt, TextColumnType columnType) + { + return str.InvariantEndsWith(txt); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs new file mode 100644 index 0000000000..f45efb412a --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// Determines how to match a string property value + /// + public enum StringPropertyMatchType + { + Exact, + Contains, + StartsWith, + EndsWith, + //Deals with % as wildcard chars in a string + Wildcard + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs b/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs new file mode 100644 index 0000000000..f33022fcbe --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/TextColumnType.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Persistence.Querying +{ + public enum TextColumnType + { + NVarchar, + NText + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs new file mode 100644 index 0000000000..f0c70a069d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// Determine how to match a number or data value + /// + public enum ValuePropertyMatchType + { + Exact, + GreaterThan, + LessThan, + GreaterThanOrEqualTo, + LessThanOrEqualTo + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs b/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs index 31fd430118..79486b6e5b 100644 --- a/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs +++ b/src/Umbraco.Core/Persistence/Relators/PropertyDataRelator.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Relators @@ -20,8 +21,12 @@ namespace Umbraco.Core.Persistence.Relators // Is this the same MemberReadOnlyDto as the current one we're processing if (Current != null && Current.UniqueId == a.UniqueId) { - // Yes, just add this PropertyDataReadOnlyDto to the current MemberReadOnlyDto's collection - Current.Properties.Add(p); + //This property may already be added so we need to check for that + if (Current.Properties.Any(x => x.Id == p.Id) == false) + { + // Yes, just add this PropertyDataReadOnlyDto to the current MemberReadOnlyDto's collection + Current.Properties.Add(p); + } // Return null to indicate we're not done with this MemberReadOnlyDto yet return null; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index fe05d8d3b8..3a9e06616b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -24,8 +24,9 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IContentTypeRepository _contentTypeRepository; private readonly ITemplateRepository _templateRepository; private readonly ITagsRepository _tagRepository; + private readonly CacheHelper _cacheHelper; - public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository) + public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository, CacheHelper cacheHelper) : base(work) { if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository"); @@ -34,11 +35,12 @@ namespace Umbraco.Core.Persistence.Repositories _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; _tagRepository = tagRepository; + _cacheHelper = cacheHelper; EnsureUniqueNaming = true; } - public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository) + public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository, CacheHelper cacheHelper) : base(work, cache) { if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository"); @@ -47,6 +49,7 @@ namespace Umbraco.Core.Persistence.Repositories _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; _tagRepository = tagRepository; + _cacheHelper = cacheHelper; EnsureUniqueNaming = true; } @@ -139,6 +142,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM cmsTask WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", "DELETE FROM umbracoRelation WHERE parentId = @Id", @@ -273,8 +277,8 @@ namespace Umbraco.Core.Persistence.Repositories //Assign the same permissions to it as the parent node - // http://issues.umbraco.org/issue/U4-2161 - var permissionsRepo = new PermissionRepository(UnitOfWork); + // http://issues.umbraco.org/issue/U4-2161 + var permissionsRepo = new PermissionRepository(UnitOfWork, _cacheHelper); var parentPermissions = permissionsRepo.GetPermissionsForEntity(entity.ParentId).ToArray(); //if there are parent permissions then assign them, otherwise leave null and permissions will become the // user's default permissions. @@ -283,9 +287,9 @@ namespace Umbraco.Core.Persistence.Repositories var userPermissions = ( from perm in parentPermissions from p in perm.AssignedPermissions - select new Tuple(perm.UserId, p)).ToList(); - - permissionsRepo.AssignEntityPermissions(entity, userPermissions); + select new Tuple(perm.UserId, p)).ToList(); + + permissionsRepo.ReplaceEntityPermissions(entity, userPermissions); //flag the entity's permissions changed flag so we can track those changes. //Currently only used for the cache refreshers to detect if we should refresh all user permissions cache. ((Content) entity).PermissionsChanged = true; @@ -429,7 +433,7 @@ namespace Umbraco.Core.Persistence.Repositories } else { - //In order to update the ContentVersion we need to retreive its primary key id + //In order to update the ContentVersion we need to retrieve its primary key id var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { Version = entity.Version }); contentVersionDto.Id = contentVerDto.Id; @@ -555,15 +559,21 @@ namespace Umbraco.Core.Persistence.Repositories return GetByVersion(dto.ContentVersionDto.VersionId); } - public void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds) + /// + /// Assigns a single permission to the current content item for the specified user ids + /// + /// + /// + /// + public void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds) { - var repo = new PermissionRepository(UnitOfWork); - repo.AssignEntityPermissions(entity, permission, userIds); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); + repo.AssignEntityPermission(entity, permission, userIds); } public IEnumerable GetPermissionsForEntity(int entityId) { - var repo = new PermissionRepository(UnitOfWork); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); return repo.GetPermissionsForEntity(entityId); } @@ -596,33 +606,6 @@ namespace Umbraco.Core.Persistence.Repositories return content; } - private PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentType contentType, DateTime createDate, DateTime updateDate) - { - var sql = new Sql(); - sql.Select("*") - .From() - .InnerJoin() - .On(left => left.PropertyTypeId, right => right.Id) - .Where(x => x.NodeId == id) - .Where(x => x.VersionId == versionId); - - var propertyDataDtos = Database.Fetch(sql); - var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate); - var properties = propertyFactory.BuildEntity(propertyDataDtos); - - var newProperties = properties.Where(x => x.HasIdentity == false); - foreach (var property in newProperties) - { - var propertyDataDto = new PropertyDataDto{ NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = versionId }; - int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto)); - - property.Version = versionId; - property.Id = primaryKey; - } - - return new PropertyCollection(properties); - } - private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { if (EnsureUniqueNaming == false) @@ -655,4 +638,4 @@ namespace Umbraco.Core.Persistence.Repositories return currentName; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 6afcd8005f..b88b928e9c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; @@ -10,6 +12,7 @@ using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Relators; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -54,23 +57,25 @@ namespace Umbraco.Core.Persistence.Repositories yield return dto.ContentTypeNodeId; } } - - /// - /// We need to override this method to ensure that any content cache is cleared - /// - /// - /// - /// see: http://issues.umbraco.org/issue/U4-1963 - /// - public override void PersistUpdatedItem(IEntity entity) + + protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, DataTypeDatabaseType dbType, string propertyTypeAlias) { - InMemoryCacheProvider.Current.Clear(typeof(IContent)); - RuntimeCacheProvider.Current.Clear(typeof(IContent)); - base.PersistUpdatedItem(entity); + return new PropertyType(propertyEditorAlias, dbType); } protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) { + //Cannot add a duplicate content type type + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType +INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id +WHERE cmsContentType." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("alias") + @"= @alias +AND umbracoNode.nodeObjectType = @objectType", + new { alias = entity.Alias, objectType = NodeObjectTypeId }); + if (exists > 0) + { + throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists"); + } + //Logic for setting Path, Level and SortOrder var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); int level = parent.Level + 1; @@ -158,8 +163,7 @@ namespace Umbraco.Core.Persistence.Repositories //If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int)) { - var datatype = Database.FirstOrDefault("WHERE propertyEditorAlias = @alias", new { alias = propertyType.PropertyEditorAlias }); - propertyType.DataTypeDefinitionId = datatype.DataTypeId; + AssignDataTypeFromPropertyEditor(propertyType); } var propertyTypeDto = propertyFactory.BuildPropertyTypeDto(tabId, propertyType); int typePrimaryKey = Convert.ToInt32(Database.Insert(propertyTypeDto)); @@ -174,6 +178,19 @@ namespace Umbraco.Core.Persistence.Repositories protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) { + + //Cannot update to a duplicate alias + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType +INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id +WHERE cmsContentType." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("alias") + @"= @alias +AND umbracoNode.nodeObjectType = @objectType +AND umbracoNode.id <> @id", + new { id = dto.NodeId, alias = entity.Alias, objectType = NodeObjectTypeId }); + if (exists > 0) + { + throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists"); + } + var propertyGroupFactory = new PropertyGroupFactory(entity.Id); var nodeDto = dto.NodeDto; @@ -316,9 +333,12 @@ namespace Umbraco.Core.Persistence.Repositories //If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int)) { - var datatype = Database.FirstOrDefault("WHERE propertyEditorAlias = @alias", new { alias = propertyType.PropertyEditorAlias }); - propertyType.DataTypeDefinitionId = datatype.DataTypeId; + AssignDataTypeFromPropertyEditor(propertyType); } + + //validate the alias! + ValidateAlias(propertyType); + var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(tabId, propertyType); int typePrimaryKey = propertyType.HasIdentity ? Database.Update(propertyTypeDto) @@ -355,7 +375,7 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql); - var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate); + var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate, CreatePropertyType); var propertyGroups = propertyGroupFactory.BuildEntity(dtos); return new PropertyGroupCollection(propertyGroups); } @@ -372,29 +392,88 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(sql); //TODO Move this to a PropertyTypeFactory - var list = (from dto in dtos - where (dto.PropertyTypeGroupId > 0) == false - select - new PropertyType(dto.DataTypeDto.PropertyEditorAlias, - dto.DataTypeDto.DbType.EnumParse(true)) - { - Alias = dto.Alias, - DataTypeDefinitionId = dto.DataTypeId, - Description = dto.Description, - Id = dto.Id, - Name = dto.Name, - HelpText = dto.HelpText, - Mandatory = dto.Mandatory, - SortOrder = dto.SortOrder, - ValidationRegExp = dto.ValidationRegExp, - CreateDate = createDate, - UpdateDate = updateDate - }).ToList(); - + var list = new List(); + foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false)) + { + var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias); + propType.Alias = dto.Alias; + propType.DataTypeDefinitionId = dto.DataTypeId; + propType.Description = dto.Description; + propType.Id = dto.Id; + propType.Name = dto.Name; + propType.HelpText = dto.HelpText; + propType.Mandatory = dto.Mandatory; + propType.SortOrder = dto.SortOrder; + propType.ValidationRegExp = dto.ValidationRegExp; + propType.CreateDate = createDate; + propType.UpdateDate = updateDate; + list.Add(propType); + } //Reset dirty properties Parallel.ForEach(list, currentFile => currentFile.ResetDirtyProperties(false)); return new PropertyTypeCollection(list); } + + protected void ValidateAlias(PropertyType pt) + { + Mandate.That(string.IsNullOrEmpty(pt.Alias) == false, + () => + { + var message = + string.Format( + "{0} '{1}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", + "Property Type", + pt.Name); + var exception = new InvalidOperationException(message); + + LogHelper.Error>(message, exception); + throw exception; + }); + } + + protected void ValidateAlias(TEntity entity) + { + Mandate.That(string.IsNullOrEmpty(entity.Alias) == false, + () => + { + var message = + string.Format( + "{0} '{1}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", + typeof(TEntity).Name, + entity.Name); + var exception = new InvalidOperationException(message); + + LogHelper.Error>(message, exception); + throw exception; + }); + } + + /// + /// Try to set the data type id based on its ControlId + /// + /// + private void AssignDataTypeFromPropertyEditor(PropertyType propertyType) + { + //we cannot try to assign a data type of it's empty + if (propertyType.PropertyEditorAlias.IsNullOrWhiteSpace() == false) + { + var sql = new Sql() + .Select("*") + .From() + .Where("propertyEditorAlias = @propertyEditorAlias", new { propertyEditorAlias = propertyType.PropertyEditorAlias }) + .OrderBy(typeDto => typeDto.DataTypeId); + var datatype = Database.FirstOrDefault(sql); + //we cannot assign a data type if one was not found + if (datatype != null) + { + propertyType.DataTypeDefinitionId = datatype.DataTypeId; + } + else + { + LogHelper.Warn>("Could not assign a data type for the property type " + propertyType.Alias + " since no data type was found with a property editor " + propertyType.PropertyEditorAlias); + } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index 3bf810cd13..ec5f810152 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -229,18 +229,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IContentType entity) { - Mandate.That(string.IsNullOrEmpty(entity.Alias) == false, - () => - { - var message = - string.Format( - "ContentType '{0}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", - entity.Name); - var exception = new Exception(message); - - LogHelper.Error(message, exception); - throw exception; - }); + ValidateAlias(entity); //Updates Modified date ((ContentType)entity).UpdatingEntity(); diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 7cd7069751..3798bb53d9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Linq; using Umbraco.Core.Models; @@ -8,6 +9,7 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -119,6 +121,15 @@ namespace Umbraco.Core.Persistence.Repositories { ((DataTypeDefinition)entity).AddingEntity(); + //Cannot add a duplicate data type + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType +INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id +WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + "= @name", new {name = entity.Name}); + if (exists > 0) + { + throw new DuplicateNameException("A data type with the name " + entity.Name + " already exists"); + } + var factory = new DataTypeDefinitionFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); @@ -154,6 +165,18 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IDataTypeDefinition entity) { + + //Cannot change to a duplicate alias + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType +INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id +WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @"= @name +AND umbracoNode.id <> @id", + new { id = entity.Id, name = entity.Name }); + if (exists > 0) + { + throw new DuplicateNameException("A data type with the name " + entity.Name + " already exists"); + } + //Updates Modified date and Version Guid ((DataTypeDefinition)entity).UpdatingEntity(); diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 0b456cda91..f98c0f1e71 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -50,6 +50,8 @@ namespace Umbraco.Core.Persistence.Repositories foreach (var textDto in dto.LanguageTextDtos) { var language = _languageRepository.Get(textDto.LanguageId); + if (language == null) + continue; var translationFactory = new DictionaryTranslationFactory(dto.UniqueId, language); list.Add(translationFactory.BuildEntity(textDto)); } @@ -180,7 +182,7 @@ namespace Umbraco.Core.Persistence.Repositories } else { - translation.Id = Convert.ToInt32(Database.Insert(dto)); + translation.Id = Convert.ToInt32(Database.Insert(textDto)); translation.Key = entity.Key; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index d70aa9510c..cd36f75369 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -33,6 +33,16 @@ namespace Umbraco.Core.Persistence.Repositories get { return _fileSystem; } } + internal virtual void AddFolder(string folderPath) + { + _work.RegisterAdded(new Folder(folderPath), this); + } + + internal virtual void DeleteFolder(string folderPath) + { + _work.RegisterRemoved(new Folder(folderPath), this); + } + #region Implementation of IRepository public virtual void AddOrUpdate(TEntity entity) @@ -68,7 +78,16 @@ namespace Umbraco.Core.Persistence.Repositories public void PersistNewItem(IEntity entity) { - PersistNewItem((TEntity)entity); + //special case for folder + var folder = entity as Folder; + if (folder != null) + { + PersistNewFolder(folder); + } + else + { + PersistNewItem((TEntity)entity); + } } public void PersistUpdatedItem(IEntity entity) @@ -78,23 +97,46 @@ namespace Umbraco.Core.Persistence.Repositories public void PersistDeletedItem(IEntity entity) { - PersistDeletedItem((TEntity)entity); + //special case for folder + var folder = entity as Folder; + if (folder != null) + { + PersistDeletedFolder(folder); + } + else + { + PersistDeletedItem((TEntity)entity); + } } #endregion - #region Abstract IUnitOfWorkRepository Methods + internal virtual void PersistNewFolder(Folder entity) + { + _fileSystem.CreateFolder(entity.Path); + } + internal virtual void PersistDeletedFolder(Folder entity) + { + _fileSystem.DeleteDirectory(entity.Path); + } + + #region Abstract IUnitOfWorkRepository Methods + protected virtual void PersistNewItem(TEntity entity) { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content)); - FileSystem.AddFile(entity.Name, stream, true); + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content))) + { + FileSystem.AddFile(entity.Path, stream, true); + } } protected virtual void PersistUpdatedItem(TEntity entity) { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content)); - FileSystem.AddFile(entity.Name, stream, true); + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(entity.Content))) + { + FileSystem.AddFile(entity.Path, stream, true); + } } protected virtual void PersistDeletedItem(TEntity entity) diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 06beebda9e..3a343febae 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -23,8 +23,19 @@ namespace Umbraco.Core.Persistence.Repositories /// An enumerable list of IEnumerable GetByPublishedVersion(IQuery query); - void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds); + /// + /// Assigns a single permission to the current content item for the specified user ids + /// + /// + /// + /// + void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds); + /// + /// Gets the list of permissions for the content item + /// + /// + /// IEnumerable GetPermissionsForEntity(int entityId); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs new file mode 100644 index 0000000000..2750457271 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IMemberGroupRepository : IRepositoryQueryable + { + /// + /// Gets a member group by it's name + /// + /// + /// + IMemberGroup GetByName(string name); + + /// + /// Creates the new member group if it doesn't already exist + /// + /// + IMemberGroup CreateIfNotExists(string roleName); + + /// + /// Returns the member groups for a given member + /// + /// + /// + IEnumerable GetMemberGroupsForMember(int memberId); + + /// + /// Returns the member groups for a given member + /// + /// + /// + IEnumerable GetMemberGroupsForMember(string username); + + void AssignRoles(string[] usernames, string[] roleNames); + + void DissociateRoles(string[] usernames, string[] roleNames); + + void AssignRoles(int[] memberIds, string[] roleNames); + + void DissociateRoles(int[] memberIds, string[] roleNames); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index 17c697ecdd..f9c42511d9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -1,10 +1,23 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories { - internal interface IMemberRepository : IRepositoryVersionable + public interface IMemberRepository : IRepositoryVersionable { + /// + /// Finds members in a given role + /// + /// + /// + /// + /// + IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + /// /// Get all members in a specific group /// @@ -19,5 +32,27 @@ namespace Umbraco.Core.Persistence.Repositories /// bool Exists(string username); + /// + /// Gets the count of items based on a complex query + /// + /// + /// + int GetCountByQuery(IQuery query); + + /// + /// Gets paged member results + /// + /// + /// + /// + /// + /// + /// + IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); + + IEnumerable GetPagedResultsByQuery( + Sql sql, int pageIndex, int pageSize, out int totalRecords, + Func, int[]> resolveIds); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs index de7ff3ba66..f6c7fce4a2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationRepository.cs @@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IRelationRepository : IRepositoryQueryable { - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs index 5fa35df140..d28f81bce0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs @@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IRelationTypeRepository : IRepositoryQueryable { - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs index 45bde95b3f..8c675a7d3a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs @@ -4,6 +4,6 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IScriptRepository : IRepository { - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index b64815188b..e40d996637 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -1,27 +1,59 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories { - internal interface IUserRepository : IRepositoryQueryable + public interface IUserRepository : IRepositoryQueryable { - //IProfile GetProfileById(int id); - IProfile GetProfileByUserName(string username); - IUser GetUserByUserName(string username); - + /// + /// Gets the count of items based on a complex query + /// + /// + /// + int GetCountByQuery(IQuery query); + + /// + /// Checks if a user with the username exists + /// + /// + /// + bool Exists(string username); + /// /// This is useful when an entire section is removed from config /// /// IEnumerable GetUsersAssignedToSection(string sectionAlias); + /// + /// Gets paged member results + /// + /// + /// + /// + /// + /// + /// + IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); + + /// /// Gets the user permissions for the specified entities /// /// /// /// - IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds); + IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds); + /// + /// Replaces the same permission set for a single user to any number of entities + /// + /// + /// + /// + void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs index efa50f6641..43b6709aa7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserTypeRepository.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Persistence.Repositories { - internal interface IUserTypeRepository : IRepositoryQueryable + public interface IUserTypeRepository : IRepositoryQueryable { } diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index d52a2549f4..0c891d512d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { Id = id }); - var languageDto = Database.First(sql); + var languageDto = Database.FirstOrDefault(sql); if (languageDto == null) return null; diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 1746e6b68a..27bd68c1f0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -131,6 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM cmsTask WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", "DELETE FROM umbracoRelation WHERE parentId = @Id", @@ -298,7 +299,7 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(newContentDto); } - //In order to update the ContentVersion we need to retreive its primary key id + //In order to update the ContentVersion we need to retrieve its primary key id var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { Version = entity.Version }); dto.Id = contentVerDto.Id; //Updates the current version - cmsContentVersion @@ -370,33 +371,6 @@ namespace Umbraco.Core.Persistence.Repositories #endregion - private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType, DateTime createDate, DateTime updateDate) - { - var sql = new Sql(); - sql.Select("*") - .From() - .InnerJoin() - .On(left => left.PropertyTypeId, right => right.Id) - .Where(x => x.NodeId == id) - .Where(x => x.VersionId == versionId); - - var propertyDataDtos = Database.Fetch(sql); - var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate); - var properties = propertyFactory.BuildEntity(propertyDataDtos); - - var newProperties = properties.Where(x => x.HasIdentity == false); - foreach (var property in newProperties) - { - var propertyDataDto = new PropertyDataDto { NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = versionId }; - int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto)); - - property.Version = versionId; - property.Id = primaryKey; - } - - return new PropertyCollection(properties); - } - private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { if (EnsureUniqueNaming == false) @@ -429,4 +403,4 @@ namespace Umbraco.Core.Persistence.Repositories return currentName; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 515557c4ef..18ff7e8e4c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; @@ -162,6 +163,8 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IMediaType entity) { + ValidateAlias(entity); + //Updates Modified date ((MediaType)entity).UpdatingEntity(); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index a93355ceb3..8e051e77e5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -1,7 +1,384 @@ -namespace Umbraco.Core.Persistence.Repositories +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using Umbraco.Core.Cache; + +namespace Umbraco.Core.Persistence.Repositories { - public class MemberGroupRepository + + + internal class MemberGroupRepository : PetaPocoRepositoryBase, IMemberGroupRepository { - + private readonly CacheHelper _cacheHelper; + + public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper) + : base(work) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _cacheHelper = cacheHelper; + } + + public MemberGroupRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, CacheHelper cacheHelper) + : base(work, cache) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _cacheHelper = cacheHelper; + } + + private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory(); + + protected override IMemberGroup PerformGet(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + + var dto = Database.Fetch(sql).FirstOrDefault(); + + return dto == null ? null : _modelFactory.BuildEntity(dto); + } + + protected override IEnumerable PerformGetAll(params int[] ids) + { + if (ids.Any()) + { + var sql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode.id in (@ids)", new { ids = ids }); + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + else + { + var sql = new Sql() + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId); + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + } + + protected override IEnumerable PerformGetByQuery(IQuery query) + { + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + + protected override Sql GetBaseQuery(bool isCount) + { + var sql = new Sql(); + sql.Select(isCount ? "COUNT(*)" : "*") + .From() + .Where(x => x.NodeObjectType == NodeObjectTypeId); + return sql; + } + + protected override string GetBaseWhereClause() + { + return "umbracoNode.id = @Id"; + } + + protected override IEnumerable GetDeleteClauses() + { + var list = new[] + { + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", + "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoRelation WHERE parentId = @Id", + "DELETE FROM umbracoRelation WHERE childId = @Id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", + "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @Id", + "DELETE FROM umbracoNode WHERE id = @Id" + }; + return list; + } + + protected override Guid NodeObjectTypeId + { + get { return new Guid(Constants.ObjectTypes.MemberGroup); } + } + + protected override void PersistNewItem(IMemberGroup entity) + { + //Save to db + var group = (MemberGroup)entity; + group.AddingEntity(); + var dto = _modelFactory.BuildDto(group); + var o = Database.IsNew(dto) ? Convert.ToInt32(Database.Insert(dto)) : Database.Update(dto); + + //Update with new correct path and id + dto.Path = string.Concat("-1,", dto.NodeId); + Database.Update(dto); + //assign to entity + group.Id = o; + group.ResetDirtyProperties(); + } + + protected override void PersistUpdatedItem(IMemberGroup entity) + { + var dto = _modelFactory.BuildDto(entity); + + Database.Update(dto); + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + public IMemberGroup GetByName(string name) + { + return _cacheHelper.RuntimeCache.GetCacheItem( + string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name), + () => + { + var qry = new Query().Where(group => group.Name.Equals(name)); + var result = GetByQuery(qry); + return result.FirstOrDefault(); + }, + //cache for 5 mins since that is the default in the RuntimeCacheProvider + TimeSpan.FromMinutes(5), + //sliding is true + true); + } + + public IMemberGroup CreateIfNotExists(string roleName) + { + using (var transaction = Database.GetTransaction()) + { + var qry = new Query().Where(group => group.Name.Equals(roleName)); + var result = GetByQuery(qry); + + if (result.Any()) return null; + + var grp = new MemberGroup + { + Name = roleName + }; + PersistNewItem(grp); + + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(grp), this)) + { + return null; + } + + transaction.Complete(); + + SavedMemberGroup.RaiseEvent(new SaveEventArgs(grp), this); + + return grp; + } + } + + public IEnumerable GetMemberGroupsForMember(int memberId) + { + var sql = new Sql(); + sql.Select("umbracoNode.*") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where(x => x.Member == memberId); + + return Database.Fetch(sql) + .DistinctBy(dto => dto.NodeId) + .Select(x => _modelFactory.BuildEntity(x)); + } + + public IEnumerable GetMemberGroupsForMember(string username) + { + //find the member by username + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + var escapedUsername = PetaPocoExtensions.EscapeAtSymbols(username); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where(x => x.LoginName == escapedUsername); + var memberIdUsername = Database.Fetch(memberSql).FirstOrDefault(); + if (memberIdUsername.HasValue == false) + { + return Enumerable.Empty(); + } + + var sql = new Sql(); + sql.Select("umbracoNode.*") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where(x => x.Member == memberIdUsername.Value); + + return Database.Fetch(sql) + .DistinctBy(dto => dto.NodeId) + .Select(x => _modelFactory.BuildEntity(x)); + } + + public void AssignRoles(string[] usernames, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + //first get the member ids based on the usernames + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames }); + var memberIds = Database.Fetch(memberSql).ToArray(); + + AssignRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void DissociateRoles(string[] usernames, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + //first get the member ids based on the usernames + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames }); + var memberIds = Database.Fetch(memberSql).ToArray(); + + DissociateRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void AssignRoles(int[] memberIds, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + AssignRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void AssignRolesInternal(int[] memberIds, string[] roleNames) + { + //create the missing roles first + + var existingSql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + var existingRoles = Database.Fetch(existingSql).Select(x => x.Text); + var missingRoles = roleNames.Except(existingRoles); + var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray(); + + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this)) + { + return; + } + foreach (var m in missingGroups) + { + PersistNewItem(m); + } + SavedMemberGroup.RaiseEvent(new SaveEventArgs(missingGroups), this); + + //now go get all the dto's for roles with these role names + var rolesForNames = Database.Fetch(existingSql).ToArray(); + + //get the groups that are currently assigned to any of these members + + var assignedSql = new Sql(); + assignedSql.Select(string.Format( + "{0},{1},{2}", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text"), + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Member"), + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("MemberGroup"))) + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where("cmsMember2MemberGroup.Member in (@ids)", new { ids = memberIds }); + + var currentlyAssigned = Database.Fetch(assignedSql).ToArray(); + + //assign the roles for each member id + + foreach (var memberId in memberIds) + { + //find any roles for the current member that are currently assigned that + //exist in the roleNames list, then determine which ones are not currently assigned. + var mId = memberId; + var found = currentlyAssigned.Where(x => x.MemberId == mId).ToArray(); + var assignedRoles = found.Where(x => roleNames.Contains(x.RoleName)).Select(x => x.RoleName); + var nonAssignedRoles = roleNames.Except(assignedRoles); + foreach (var toAssign in nonAssignedRoles) + { + var groupId = rolesForNames.First(x => x.Text == toAssign).NodeId; + Database.Insert(new Member2MemberGroupDto { Member = mId, MemberGroup = groupId }); + } + } + } + + public void DissociateRoles(int[] memberIds, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + DissociateRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + private void DissociateRolesInternal(int[] memberIds, string[] roleNames) + { + var existingSql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + var existingRolesIds = Database.Fetch(existingSql).Select(x => x.NodeId).ToArray(); + + Database.Execute("DELETE FROM cmsMember2MemberGroup WHERE Member IN (@memberIds) AND MemberGroup IN (@memberGroups)", + new { memberIds = memberIds, memberGroups = existingRolesIds }); + } + + private class AssignedRolesDto + { + [Column("text")] + public string RoleName { get; set; } + + [Column("Member")] + public int MemberId { get; set; } + + [Column("MemberGroup")] + public int MemberGroupId { get; set; } + } + + /// + /// Occurs before Save + /// + internal static event TypedEventHandler> SavingMemberGroup; + + /// + /// Occurs after Save + /// + internal static event TypedEventHandler> SavedMemberGroup; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index f186bc066c..b3563f4e5e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Linq.Expressions; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Models.EntityBase; @@ -9,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Relators; using Umbraco.Core.Persistence.UnitOfWork; @@ -22,23 +24,26 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IMemberTypeRepository _memberTypeRepository; private readonly ITagsRepository _tagRepository; + private readonly IMemberGroupRepository _memberGroupRepository; - public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository, ITagsRepository tagRepository) + public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagsRepository tagRepository) : base(work) { if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository"); if (tagRepository == null) throw new ArgumentNullException("tagRepository"); _memberTypeRepository = memberTypeRepository; _tagRepository = tagRepository; + _memberGroupRepository = memberGroupRepository; } - public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository, ITagsRepository tagRepository) + public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagsRepository tagRepository) : base(work, cache) { if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository"); if (tagRepository == null) throw new ArgumentNullException("tagRepository"); _memberTypeRepository = memberTypeRepository; _tagRepository = tagRepository; + _memberGroupRepository = memberGroupRepository; } #region Overrides of RepositoryBase @@ -167,6 +172,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM cmsTask WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", "DELETE FROM umbracoRelation WHERE parentId = @Id", @@ -240,7 +246,12 @@ namespace Umbraco.Core.Persistence.Repositories //Create the PropertyData for this version - cmsPropertyData var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id); - var propertyDataDtos = propertyFactory.BuildDto(((Member)entity).Properties); + //Add Properties + // - don't try to save the property if it doesn't exist (or doesn't have an ID) on the content type + // - this can occur if the member type doesn't contain the built-in properties that the + // - member object contains. + var propsToPersist = entity.Properties.Where(x => x.PropertyType.HasIdentity).ToArray(); + var propertyDataDtos = propertyFactory.BuildDto(propsToPersist); var keyDictionary = new Dictionary(); //Add Properties @@ -251,7 +262,7 @@ namespace Umbraco.Core.Persistence.Repositories } //Update Properties with its newly set Id - foreach (var property in ((Member)entity).Properties) + foreach (var property in propsToPersist) { property.Id = keyDictionary[property.PropertyTypeId]; } @@ -299,7 +310,7 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(newContentDto); } - //In order to update the ContentVersion we need to retreive its primary key id + //In order to update the ContentVersion we need to retrieve its primary key id var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { Version = entity.Version }); dto.ContentVersionDto.Id = contentVerDto.Id; //Updates the current version - cmsContentVersion @@ -307,7 +318,10 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(dto.ContentVersionDto); //Updates the cmsMember entry if it has changed + + //NOTE: these cols are the REAL column names in the db var changedCols = new List(); + if (dirtyEntity.IsPropertyDirty("Email")) { changedCols.Add("Email"); @@ -317,7 +331,7 @@ namespace Umbraco.Core.Persistence.Repositories changedCols.Add("LoginName"); } // DO NOT update the password if it is null or empty - if (dirtyEntity.IsPropertyDirty("Password") && entity.Password.IsNullOrWhiteSpace() == false) + if (dirtyEntity.IsPropertyDirty("RawPasswordValue") && entity.RawPasswordValue.IsNullOrWhiteSpace() == false) { changedCols.Add("Password"); } @@ -336,12 +350,10 @@ namespace Umbraco.Core.Persistence.Repositories //Add Properties // - don't try to save the property if it doesn't exist (or doesn't have an ID) on the content type // - this can occur if the member type doesn't contain the built-in properties that the - // - member object contains. - var existingProperties = entity.Properties - .Where(property => entity.ContentType.PropertyTypes.Any(x => x.Alias == property.Alias && x.HasIdentity)) - .ToList(); + // - member object contains. + var propsToPersist = entity.Properties.Where(x => x.PropertyType.HasIdentity).ToArray(); - var propertyDataDtos = propertyFactory.BuildDto(existingProperties); + var propertyDataDtos = propertyFactory.BuildDto(propsToPersist); foreach (var propertyDataDto in propertyDataDtos) { @@ -426,6 +438,57 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + public IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + { + //get the group id + var grpQry = new Query().Where(group => group.Name.Equals(roleName)); + var memberGroup = _memberGroupRepository.GetByQuery(grpQry).FirstOrDefault(); + if (memberGroup == null) return Enumerable.Empty(); + + // get the members by username + var query = new Query(); + switch (matchType) + { + case StringPropertyMatchType.Exact: + query.Where(member => member.Username.Equals(usernameToMatch)); + break; + case StringPropertyMatchType.Contains: + query.Where(member => member.Username.Contains(usernameToMatch)); + break; + case StringPropertyMatchType.StartsWith: + query.Where(member => member.Username.StartsWith(usernameToMatch)); + break; + case StringPropertyMatchType.EndsWith: + query.Where(member => member.Username.EndsWith(usernameToMatch)); + break; + case StringPropertyMatchType.Wildcard: + query.Where(member => member.Username.SqlWildcard(usernameToMatch, TextColumnType.NVarchar)); + break; + default: + throw new ArgumentOutOfRangeException("matchType"); + } + var matchedMembers = GetByQuery(query).ToArray(); + + var membersInGroup = new List(); + //then we need to filter the matched members that are in the role + //since the max sql params are 2100 on sql server, we'll reduce that to be safe for potentially other servers and run the queries in batches + var inGroups = matchedMembers.InGroupsOf(1000); + foreach (var batch in inGroups) + { + var memberIdBatch = batch.Select(x => x.Id); + var sql = new Sql().Select("*").From() + .Where(dto => dto.MemberGroup == memberGroup.Id) + .Where("Member IN (@memberIds)", new { memberIds = memberIdBatch }); + var memberIdsInGroup = Database.Fetch(sql) + .Select(x => x.Member).ToArray(); + + membersInGroup.AddRange(matchedMembers.Where(x => memberIdsInGroup.Contains(x.Id))); + } + + return membersInGroup; + + } + /// /// Get all members in a specific group /// @@ -433,9 +496,13 @@ namespace Umbraco.Core.Persistence.Repositories /// public IEnumerable GetByMemberGroup(string groupName) { - var subquery = GetSubquery().Where(x => x.Text == groupName); + var grpQry = new Query().Where(group => group.Name.Equals(groupName)); + var memberGroup = _memberGroupRepository.GetByQuery(grpQry).FirstOrDefault(); + if (memberGroup == null) return Enumerable.Empty(); + var subQuery = new Sql().Select("Member").From().Where(dto => dto.MemberGroup == memberGroup.Id); + var sql = GetBaseQuery(false) - .Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments)) + .Append(new Sql("WHERE umbracoNode.id IN (" + subQuery.SQL + ")", subQuery.Arguments)) .OrderByDescending(x => x.VersionDate) .OrderBy(x => x.SortOrder); @@ -449,7 +516,7 @@ namespace Umbraco.Core.Persistence.Repositories public bool Exists(string username) { var sql = new Sql(); - var escapedUserName = Database.EscapeAtSymbols(username); + var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username); sql.Select("COUNT(*)") .From() .Where(x => x.LoginName == escapedUserName); @@ -457,6 +524,85 @@ namespace Umbraco.Core.Persistence.Repositories return Database.ExecuteScalar(sql) > 0; } + public int GetCountByQuery(IQuery query) + { + var sqlSubquery = GetSubquery(); + var translator = new SqlTranslator(sqlSubquery, query); + var subquery = translator.Translate(); + //get the COUNT base query + var sql = GetBaseQuery(true) + .Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments)); + + return Database.ExecuteScalar(sql); + } + + /// + /// Gets paged member results + /// + /// + /// The where clause, if this is null all records are queried + /// + /// + /// + /// + /// + /// + /// + /// The query supplied will ONLY work with data specifically on the cmsMember table because we are using PetaPoco paging (SQL paging) + /// + public IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy) + { + if (orderBy == null) throw new ArgumentNullException("orderBy"); + + var sql = new Sql(); + sql.Select("*").From(); + + Sql resultQuery; + if (query != null) + { + var translator = new SqlTranslator(sql, query); + resultQuery = translator.Translate(); + } + else + { + resultQuery = sql; + } + + //get the referenced column name + var expressionMember = ExpressionHelper.GetMemberInfo(orderBy); + //now find the mapped column name + var mapper = MappingResolver.Current.ResolveMapperByType(typeof(IMember)); + var mappedField = mapper.Map(expressionMember.Name); + if (mappedField.IsNullOrWhiteSpace()) + { + throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); + } + //need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177 + resultQuery.OrderBy(string.Format("({0})", mappedField)); + + var result = GetPagedResultsByQuery(resultQuery, pageIndex, pageSize, out totalRecords, + dtos => dtos.Select(x => x.NodeId).ToArray()); + + //now we need to ensure this result is also ordered by the same order by clause + return result.OrderBy(orderBy.Compile()); + } + + public IEnumerable GetPagedResultsByQuery( + Sql sql, int pageIndex, int pageSize, out int totalRecords, + Func, int[]> resolveIds) + { + var pagedResult = Database.Page(pageIndex + 1, pageSize, sql); + + totalRecords = Convert.ToInt32(pagedResult.TotalItems); + + //now that we have the member dto's we need to construct true members from the list. + if (totalRecords == 0) + { + return Enumerable.Empty(); + } + return GetAll(resolveIds(pagedResult.Items)).ToArray(); + } + private IMember BuildFromDto(List dtos) { if (dtos == null || dtos.Any() == false) @@ -474,6 +620,8 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberReadOnlyFactory(memberTypes); var member = factory.BuildEntity(dto); + member.Properties = GetPropertyCollection(dto.NodeId, dto.VersionId, member.ContentType, dto.CreateDate, dto.UpdateDate); + return member; } @@ -487,8 +635,15 @@ namespace Umbraco.Core.Persistence.Repositories var memberTypeList = _memberTypeRepository.GetAll(); memberTypeList.ForEach(x => memberTypes.Add(x.Alias, x)); + var entities = new List(); var factory = new MemberReadOnlyFactory(memberTypes); - return dtos.Select(factory.BuildEntity); + foreach (var dto in dtos) + { + var entity = factory.BuildEntity(dto); + entity.Properties = GetPropertyCollection(dto.NodeId, dto.VersionId, entity.ContentType, dto.CreateDate, dto.UpdateDate); + entities.Add(entity); + } + return entities; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 0f5b963e5c..2ea9814f26 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; @@ -17,17 +18,17 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class MemberTypeRepository : ContentTypeBaseRepository, IMemberTypeRepository { - public MemberTypeRepository(IDatabaseUnitOfWork work) + public MemberTypeRepository(IDatabaseUnitOfWork work) : base(work) { } - public MemberTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + public MemberTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) { } - #region Overrides of RepositoryBase + #region Overrides of RepositoryBase protected override IMemberType PerformGet(int id) { @@ -39,13 +40,13 @@ namespace Umbraco.Core.Persistence.Repositories Database.Fetch( new PropertyTypePropertyGroupRelator().Map, sql); - if (dtos == null || dtos.Any() == false) - return null; + if (dtos == null || dtos.Any() == false) + return null; - var factory = new MemberTypeReadOnlyFactory(); - var member = factory.BuildEntity(dtos.First()); + var factory = new MemberTypeReadOnlyFactory(); + var member = factory.BuildEntity(dtos.First()); - return member; + return member; } protected override IEnumerable PerformGetAll(params int[] ids) @@ -98,7 +99,7 @@ namespace Umbraco.Core.Persistence.Repositories return sql; } - sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias", + sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias", "cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.helpText", "cmsPropertyType.mandatory", "cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", @@ -165,18 +166,23 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistNewItem(IMemberType entity) { - ((MemberType)entity).AddingEntity(); + ValidateAlias(entity); + ((MemberType)entity).AddingEntity(); + //By Convention we add 9 stnd PropertyTypes to an Umbraco MemberType - var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs; + entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName); + var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); foreach (var standardPropertyType in standardPropertyTypes) { - entity.AddPropertyType(standardPropertyType.Value); + entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName); } var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); + EnsureExplicitDataTypeForBuiltInProperties(entity); + PersistNewBaseContentType(dto, entity); //Handles the MemberTypeDto (cmsMemberType table) @@ -191,6 +197,8 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IMemberType entity) { + ValidateAlias(entity); + //Updates Modified date ((MemberType)entity).UpdatingEntity(); @@ -210,10 +218,12 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); + EnsureExplicitDataTypeForBuiltInProperties(entity); + PersistUpdatedBaseContentType(dto, entity); //Remove existing entries before inserting new ones - Database.Delete("WHERE NodeId = @Id", new {Id = entity.Id}); + Database.Delete("WHERE NodeId = @Id", new { Id = entity.Id }); //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); @@ -227,7 +237,50 @@ namespace Umbraco.Core.Persistence.Repositories #endregion - private IEnumerable BuildFromDtos(List dtos) + /// + /// Override so we can specify explicit db type's on any property types that are built-in. + /// + /// + /// + /// + /// + protected override PropertyType CreatePropertyType(string propertyEditorAlias, DataTypeDatabaseType dbType, string propertyTypeAlias) + { + //custom property type constructor logic to set explicit dbtype's for built in properties + var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var propDbType = GetDbTypeForBuiltInProperty(propertyTypeAlias, dbType, stdProps); + 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); + } + + /// + /// Ensure that all the built-in membership provider properties have their correct data type + /// and property editors assigned. This occurs prior to saving so that the correct values are persisted. + /// + /// + private static void EnsureExplicitDataTypeForBuiltInProperties(IContentTypeBase memberType) + { + var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + foreach (var propertyType in memberType.PropertyTypes) + { + var dbTypeAttempt = GetDbTypeForBuiltInProperty(propertyType.Alias, propertyType.DataTypeDatabaseType, stdProps); + if (dbTypeAttempt) + { + //this reset's it's current data type reference which will be re-assigned based on the property editor assigned on the next line + propertyType.DataTypeDefinitionId = 0; + propertyType.DataTypeId = GetPropertyEditorForBuiltInProperty(propertyType.Alias, propertyType.DataTypeId, stdProps).Result; + } + } + } + + /// + /// Builds a collection of entities from a collection of Dtos + /// + /// + /// + private static IEnumerable BuildFromDtos(List dtos) { if (dtos == null || dtos.Any() == false) return Enumerable.Empty(); @@ -235,5 +288,58 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeReadOnlyFactory(); return dtos.Select(factory.BuildEntity); } + + /// + /// If this is one of our internal properties - we will manually assign the data type since they must + /// always correspond to the correct db type no matter what the backing data type is assigned. + /// + /// + /// + /// + /// + /// Successful attempt if it was a built in property + /// + internal static Attempt GetDbTypeForBuiltInProperty( + string propAlias, + DataTypeDatabaseType dbType, + Dictionary standardProps) + { + var aliases = standardProps.Select(x => x.Key).ToArray(); + + //check if it is built in + if (aliases.Contains(propAlias)) + { + //return the pre-determined db type for this property + return Attempt.Succeed(standardProps.Single(x => x.Key == propAlias).Value.DataTypeDatabaseType); + } + + return Attempt.Fail(dbType); + } + + /// + /// + /// + /// + /// + /// + /// + /// Successful attempt if it was a built in property + /// + internal static Attempt GetPropertyEditorForBuiltInProperty( + string propAlias, + Guid propertyEditor, + Dictionary standardProps) + { + var aliases = standardProps.Select(x => x.Key).ToArray(); + + //check if it is built in + if (aliases.Contains(propAlias)) + { + //return the pre-determined db type for this property + return Attempt.Succeed(standardProps.Single(x => x.Key == propAlias).Value.DataTypeId); + } + + return Attempt.Fail(propertyEditor); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs new file mode 100644 index 0000000000..a15a7e1521 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal class NotificationsRepository + { + private readonly IDatabaseUnitOfWork _unitOfWork; + + public NotificationsRepository(IDatabaseUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public IEnumerable GetUserNotifications(IUser user) + { + var sql = new Sql() + .Select("DISTINCT umbracoNode.id, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => dto.UserId == (int)user.Id) + .OrderBy(dto => dto.NodeId); + + var dtos = _unitOfWork.Database.Fetch(sql); + //need to map the results + return dtos.Select(d => new Notification(d.id, d.userId, d.action, d.nodeObjectType)).ToList(); + } + + public IEnumerable GetEntityNotifications(IEntity entity) + { + var sql = new Sql() + .Select("DISTINCT umbracoNode.id, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => dto.NodeId == entity.Id) + .OrderBy(dto => dto.NodeId); + + var dtos = _unitOfWork.Database.Fetch(sql); + //need to map the results + return dtos.Select(d => new Notification(d.id, d.userId, d.action, d.nodeObjectType)).ToList(); + } + + public int DeleteNotifications(IEntity entity) + { + return _unitOfWork.Database.Delete("WHERE nodeId = @nodeId", new { nodeId = entity.Id }); + } + + public int DeleteNotifications(IUser user) + { + return _unitOfWork.Database.Delete("WHERE userId = @userId", new { userId = user.Id }); + } + + public int DeleteNotifications(IUser user, IEntity entity) + { + // delete all settings on the node for this user + return _unitOfWork.Database.Delete("WHERE userId = @userId AND nodeId = @nodeId", new { userId = user.Id, nodeId = entity.Id }); + } + + public Notification CreateNotification(IUser user, IEntity entity, string action) + { + var sql = new Sql() + .Select("DISTINCT nodeObjectType") + .From() + .Where(nodeDto => nodeDto.NodeId == entity.Id); + var nodeType = _unitOfWork.Database.ExecuteScalar(sql); + + var dto = new User2NodeNotifyDto() + { + Action = action, + NodeId = entity.Id, + UserId = (int)user.Id + }; + _unitOfWork.Database.Insert(dto); + return new Notification(dto.NodeId, dto.UserId, dto.Action, nodeType); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index bdc7e5779d..d3b6a586fe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -1,14 +1,20 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Globalization; using System.Linq; using System.Text; +using System.Web.Caching; +using Umbraco.Core.Events; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using CacheKeys = Umbraco.Core.Cache.CacheKeys; +using Umbraco.Core.Cache; namespace Umbraco.Core.Persistence.Repositories { @@ -20,10 +26,12 @@ namespace Umbraco.Core.Persistence.Repositories where TEntity : class, IAggregateRoot { private readonly IDatabaseUnitOfWork _unitOfWork; + private readonly CacheHelper _cache; - internal PermissionRepository(IDatabaseUnitOfWork unitOfWork) + internal PermissionRepository(IDatabaseUnitOfWork unitOfWork, CacheHelper cache) { _unitOfWork = unitOfWork; + _cache = cache; } /// @@ -32,39 +40,57 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - internal IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds) + public IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) + { + var entityIdKey = string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture))); + return _cache.RuntimeCache.GetCacheItem>( + string.Format("{0}{1}{2}", CacheKeys.UserPermissionsCacheKey, userId, entityIdKey), + () => { - var whereBuilder = new StringBuilder(); + + var whereBuilder = new StringBuilder(); - //where userId = @userId AND - whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("userId")); - whereBuilder.Append("="); - whereBuilder.Append(userId); - whereBuilder.Append(" AND "); + //where userId = @userId AND + whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("userId")); + whereBuilder.Append("="); + whereBuilder.Append(userId); - //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... - whereBuilder.Append("("); - for (var index = 0; index < entityIds.Length; index++) - { - var entityId = entityIds[index]; - whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("nodeId")); - whereBuilder.Append("="); - whereBuilder.Append(entityId); - if (index < entityIds.Length - 1) - { - whereBuilder.Append(" OR "); - } - } - whereBuilder.Append(")"); + if (entityIds.Any()) + { + whereBuilder.Append(" AND "); - var sql = new Sql(); - sql.Select("*") - .From() - .Where(whereBuilder.ToString()); + //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... + whereBuilder.Append("("); + for (var index = 0; index < entityIds.Length; index++) + { + var entityId = entityIds[index]; + whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("nodeId")); + whereBuilder.Append("="); + whereBuilder.Append(entityId); + if (index < entityIds.Length - 1) + { + whereBuilder.Append(" OR "); + } + } + whereBuilder.Append(")"); + } + + var sql = new Sql(); + sql.Select("*") + .From() + .Where(whereBuilder.ToString()); + + //ToArray() to ensure it's all fetched from the db once. + var result = _unitOfWork.Database.Fetch(sql).ToArray(); + return ConvertToPermissionList(result); + + }, + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, + // then it will refresh from the database. + new TimeSpan(0, 20, 0), + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average + priority: CacheItemPriority.BelowNormal); - //ToArray() to ensure it's all fetched from the db once. - var result = _unitOfWork.Database.Fetch(sql).ToArray(); - return ConvertToPermissionList(result); } /// @@ -72,7 +98,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - internal IEnumerable GetPermissionsForEntity(int entityId) + public IEnumerable GetPermissionsForEntity(int entityId) { var sql = new Sql(); sql.Select("*") @@ -85,7 +111,118 @@ namespace Umbraco.Core.Persistence.Repositories return ConvertToPermissionList(result); } - private IEnumerable ConvertToPermissionList(IEnumerable result) + /// + /// Assigns the same permission set for a single user to any number of entities + /// + /// + /// + /// + /// + /// This will first clear the permissions for this user and entities and recreate them + /// + public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + { + var db = _unitOfWork.Database; + using (var trans = db.GetTransaction()) + { + db.Execute("DELETE FROM umbracoUser2NodePermission WHERE userId=@userId AND nodeId in (@nodeIds)", + new {userId = userId, nodeIds = entityIds}); + + var toInsert = new List(); + foreach (var p in permissions) + { + foreach (var e in entityIds) + { + toInsert.Add(new User2NodePermissionDto + { + NodeId = e, + Permission = p.ToString(CultureInfo.InvariantCulture), + UserId = userId + }); + } + } + + _unitOfWork.Database.BulkInsertRecords(toInsert, trans); + + trans.Complete(); + + //Raise the event + AssignedPermissions.RaiseEvent( + new SaveEventArgs(ConvertToPermissionList(toInsert), false), this); + } + } + + /// + /// Assigns one permission to an entity for multiple users + /// + /// + /// + /// + public void AssignEntityPermission(TEntity entity, char permission, IEnumerable userIds) + { + var db = _unitOfWork.Database; + using (var trans = db.GetTransaction()) + { + db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId AND permission=@permission AND userId in (@userIds)", + new + { + nodeId = entity.Id, + permission = permission.ToString(CultureInfo.InvariantCulture), + userIds = userIds + }); + + var actions = userIds.Select(id => new User2NodePermissionDto + { + NodeId = entity.Id, + Permission = permission.ToString(CultureInfo.InvariantCulture), + UserId = id + }).ToArray(); + + _unitOfWork.Database.BulkInsertRecords(actions, trans); + + trans.Complete(); + + //Raise the event + AssignedPermissions.RaiseEvent( + new SaveEventArgs(ConvertToPermissionList(actions), false), this); + } + } + + /// + /// Assigns permissions to an entity for multiple users/permission entries + /// + /// + /// + /// A key/value pair list containing a userId and a permission to assign + /// + /// + /// This will first clear the permissions for this entity then re-create them + /// + public void ReplaceEntityPermissions(TEntity entity, IEnumerable> userPermissions) + { + var db = _unitOfWork.Database; + using (var trans = db.GetTransaction()) + { + db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = entity.Id }); + + var actions = userPermissions.Select(p => new User2NodePermissionDto + { + NodeId = entity.Id, + Permission = p.Item2, + UserId = p.Item1 + }).ToArray(); + + _unitOfWork.Database.BulkInsertRecords(actions, trans); + + trans.Complete(); + + //Raise the event + AssignedPermissions.RaiseEvent( + new SaveEventArgs(ConvertToPermissionList(actions), false), this); + } + } + + private static IEnumerable ConvertToPermissionList(IEnumerable result) { var permissions = new List(); var nodePermissions = result.GroupBy(x => x.NodeId); @@ -99,117 +236,8 @@ namespace Umbraco.Core.Persistence.Repositories } } return permissions; - } - - /// - /// Assigns one permission to an entity for multiple users - /// - /// - /// - /// - internal void AssignEntityPermissions(TEntity entity, char permission, IEnumerable userIds) - { - var actions = userIds.Select(id => new User2NodePermissionDto - { - NodeId = entity.Id, - Permission = permission.ToString(CultureInfo.InvariantCulture), - UserId = (int)id - }); - - _unitOfWork.Database.BulkInsertRecords(actions); } - /// - /// Assigns permissions to an entity for multiple users/permission entries - /// - /// - /// - /// A key/value pair list containing a userId and a permission to assign - /// - internal void AssignEntityPermissions(TEntity entity, IEnumerable> userPermissions) - { - var actions = userPermissions.Select(p => new User2NodePermissionDto - { - NodeId = entity.Id, - Permission = p.Item2, - UserId = (int)p.Item1 - }); - - _unitOfWork.Database.BulkInsertRecords(actions); - } - - /// - /// Replace permissions for an entity for multiple users - /// - /// - /// - /// - internal void ReplaceEntityPermissions(TEntity entity, string permissions, IEnumerable userIds) - { - _unitOfWork.Database.Update( - GenerateReplaceEntityPermissionsSql(entity.Id, permissions, userIds.ToArray())); - } - - /// - /// An overload to replace entity permissions and all replace all descendant permissions - /// - /// - /// - /// - /// A callback to get the descendant Ids of the current entity - /// - /// - internal void ReplaceEntityPermissions(TEntity entity, string permissions, Func> getDescendantIds, IEnumerable userIds) - { - _unitOfWork.Database.Update( - GenerateReplaceEntityPermissionsSql( - new[] {entity.Id}.Concat(getDescendantIds(entity)).ToArray(), - permissions, - userIds.ToArray())); - } - - internal static string GenerateReplaceEntityPermissionsSql(int entityId, string permissions, object[] userIds) - { - return GenerateReplaceEntityPermissionsSql(new[] {entityId}, permissions, userIds); - } - - internal static string GenerateReplaceEntityPermissionsSql(int[] entityIds, string permissions, object[] userIds) - { - //create the "SET" clause of the update statement - var sqlSet = string.Format("SET {0}={1}", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("permission"), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(permissions)); - - //build the nodeIds part of the where clause - var sqlNodeWhere = BuildOrClause(entityIds, "nodeId"); - - //build up the userIds part of the where clause - var userWhereBuilder = BuildOrClause(userIds, "userId"); - - var sqlWhere = new Sql(); - sqlWhere.Where(string.Format("{0} AND {1}", sqlNodeWhere, userWhereBuilder)); - - return string.Format("{0} {1}", sqlSet, sqlWhere.SQL); - } - - private static string BuildOrClause(IEnumerable ids, string colName) - { - var asArray = ids.ToArray(); - var userWhereBuilder = new StringBuilder(); - userWhereBuilder.Append("("); - for (var index = 0; index < asArray.Length; index++) - { - var userId = asArray[index]; - userWhereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(colName)); - userWhereBuilder.Append("="); - userWhereBuilder.Append(userId); - if (index < asArray.Length - 1) - { - userWhereBuilder.Append(" OR "); - } - } - userWhereBuilder.Append(")"); - return userWhereBuilder.ToString(); - } + public static event TypedEventHandler, SaveEventArgs> AssignedPermissions; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index b93de7e585..9858b921f7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -18,13 +18,13 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IRelationTypeRepository _relationTypeRepository; - public RelationRepository(IDatabaseUnitOfWork work, IRelationTypeRepository relationTypeRepository) - : base(work) + public RelationRepository(IDatabaseUnitOfWork work, IRelationTypeRepository relationTypeRepository) + : base(work) { _relationTypeRepository = relationTypeRepository; } - public RelationRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IRelationTypeRepository relationTypeRepository) + public RelationRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IRelationTypeRepository relationTypeRepository) : base(work, cache) { _relationTypeRepository = relationTypeRepository; @@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Repositories return null; var relationType = _relationTypeRepository.Get(dto.RelationType); - if(relationType == null) + if (relationType == null) throw new Exception(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType)); var factory = new RelationFactory(relationType); diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index 79fba9ea8d..d4249b605e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -16,12 +16,12 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class RelationTypeRepository : PetaPocoRepositoryBase, IRelationTypeRepository { - public RelationTypeRepository(IDatabaseUnitOfWork work) - : base(work) + public RelationTypeRepository(IDatabaseUnitOfWork work) + : base(work) { } - public RelationTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + public RelationTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index b97227d783..0932cc89b2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -58,7 +58,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public void AddOrUpdate(TEntity entity) { - if (!entity.HasIdentity) + if (entity.HasIdentity == false) { _work.RegisterAdded(entity, this); } @@ -104,7 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories { //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - TracksChangesEntityBase asEntity = entity as TracksChangesEntityBase; + var asEntity = entity as TracksChangesEntityBase; if (asEntity != null) { asEntity.ResetDirtyProperties(false); @@ -135,13 +135,16 @@ namespace Umbraco.Core.Persistence.Repositories { if (ids.Any()) { - var entities = _cache.GetByIds(typeof(TEntity), ids.Select(id => id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString())).ToList()); + var entities = _cache.GetByIds( + typeof (TEntity), ids.Select(id => id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString())).ToList()) + .ToArray(); + if (ids.Count().Equals(entities.Count()) && entities.Any(x => x == null) == false) return entities.Select(x => (TEntity)x); } else { - var allEntities = _cache.GetAllByType(typeof(TEntity)); + var allEntities = _cache.GetAllByType(typeof (TEntity)).ToArray(); if (allEntities.Any()) { @@ -154,7 +157,10 @@ namespace Umbraco.Core.Persistence.Repositories } } - var entityCollection = PerformGetAll(ids); + var entityCollection = PerformGetAll(ids) + //ensure we don't include any null refs in the returned collection! + .WhereNotNull() + .ToArray(); foreach (var entity in entityCollection) { @@ -175,7 +181,9 @@ namespace Umbraco.Core.Persistence.Repositories /// public IEnumerable GetByQuery(IQuery query) { - return PerformGetByQuery(query); + return PerformGetByQuery(query) + //ensure we don't include any null refs in the returned collection! + .WhereNotNull(); } protected abstract bool PerformExists(TId id); @@ -215,8 +223,19 @@ namespace Umbraco.Core.Persistence.Repositories /// public virtual void PersistNewItem(IEntity entity) { - PersistNewItem((TEntity)entity); - _cache.Save(typeof(TEntity), entity); + try + { + PersistNewItem((TEntity)entity); + _cache.Save(typeof(TEntity), entity); + } + catch (Exception) + { + //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way + // that we cache entities: http://issues.umbraco.org/issue/U4-4259 + _cache.Delete(typeof (TEntity), entity); + throw; + } + } /// @@ -225,8 +244,19 @@ namespace Umbraco.Core.Persistence.Repositories /// public virtual void PersistUpdatedItem(IEntity entity) { - PersistUpdatedItem((TEntity)entity); - _cache.Save(typeof(TEntity), entity); + try + { + PersistUpdatedItem((TEntity)entity); + _cache.Save(typeof(TEntity), entity); + } + catch (Exception) + { + //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way + // that we cache entities: http://issues.umbraco.org/issue/U4-4259 + _cache.Delete(typeof (TEntity), entity); + throw; + } + } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index de7dd50223..f6af195b94 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -28,9 +28,10 @@ namespace Umbraco.Core.Persistence.Repositories public override Script Get(string id) { - if(!FileSystem.FileExists(id)) + if(FileSystem.FileExists(id) == false) { - throw new Exception(string.Format("The file {0} was not found", id)); + return null; + //throw new Exception(string.Format("The file {0} was not found", id)); } string content = string.Empty; diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index 893fce373c..b4a62abe5f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -83,6 +83,27 @@ namespace Umbraco.Core.Persistence.Repositories return nodeDto == null ? 0 : nodeDto.NodeId; } + //This should be used later to do GetAll properly without individual selections + private IEnumerable> GetStylesheetIds(string[] paths) + { + var sql = new Sql() + .Select("*") + .From() + .Where("nodeObjectType = @NodeObjectType AND umbracoNode.text in (@aliases)", + new + { + NodeObjectType = UmbracoObjectTypes.Stylesheet.GetGuid(), + aliases = paths.Select(x => x.TrimEnd(".css").Replace("\\", "/")).ToArray() + }); + var dtos = _dbwork.Database.Fetch(sql); + + return dtos.Select(x => new Tuple( + //the id + x.NodeId, + //the original path requested for the id + paths.First(p => p.TrimEnd(".css").Replace("\\", "/") == x.Text))); + } + public override IEnumerable GetAll(params string[] ids) { if (ids.Any()) diff --git a/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs index 476c65706f..41b7bd6f1a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs @@ -373,7 +373,7 @@ namespace Umbraco.Core.Persistence.Repositories /// private static string GetTagSet(IEnumerable tagsToInsert) { - var array = tagsToInsert.Select(tag => string.Format("select '{0}' as Tag, '{1}' as [Group]", tag.Text, tag.Group)).ToArray(); + var array = tagsToInsert.Select(tag => string.Format("select '{0}' as Tag, '{1}' as [Group]", tag.Text.Replace("'", "''"), tag.Group)).ToArray(); return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet"; } diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index aa754c765d..0c3e64162f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Relators; using Umbraco.Core.Persistence.SqlSyntax; @@ -20,17 +22,20 @@ namespace Umbraco.Core.Persistence.Repositories internal class UserRepository : PetaPocoRepositoryBase, IUserRepository { private readonly IUserTypeRepository _userTypeRepository; + private readonly CacheHelper _cacheHelper; - public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository) + public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository, CacheHelper cacheHelper) : base(work) { _userTypeRepository = userTypeRepository; + _cacheHelper = cacheHelper; } - public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository) + public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository, CacheHelper cacheHelper) : base(work, cache) { _userTypeRepository = userTypeRepository; + _cacheHelper = cacheHelper; } #region Overrides of RepositoryBase @@ -126,6 +131,8 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { + "DELETE FROM cmsTask WHERE userId = @Id", + "DELETE FROM cmsTask WHERE parentUserId = @Id", "DELETE FROM umbracoUser2NodePermission WHERE userId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id", "DELETE FROM umbracoUserLogins WHERE userId = @Id", @@ -200,7 +207,30 @@ namespace Umbraco.Core.Persistence.Repositories #endregion #region Implementation of IUserRepository - + + public int GetCountByQuery(IQuery query) + { + var sqlClause = GetBaseQuery("umbracoUser.id"); + var translator = new SqlTranslator(sqlClause, query); + var subquery = translator.Translate(); + //get the COUNT base query + var sql = GetBaseQuery(true) + .Append(new Sql("WHERE umbracoUser.id IN (" + subquery.SQL + ")", subquery.Arguments)); + + return Database.ExecuteScalar(sql); + } + + public bool Exists(string username) + { + var sql = new Sql(); + var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username); + sql.Select("COUNT(*)") + .From() + .Where(x => x.UserName == escapedUserName); + + return Database.ExecuteScalar(sql) > 0; + } + public IEnumerable GetUsersAssignedToSection(string sectionAlias) { //Here we're building up a query that looks like this, a sub query is required because the resulting structure @@ -224,73 +254,88 @@ namespace Umbraco.Core.Persistence.Repositories return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); } + /// + /// Gets paged user results + /// + /// + /// The where clause, if this is null all records are queried + /// + /// + /// + /// + /// + /// + /// + /// The query supplied will ONLY work with data specifically on the umbracoUser table because we are using PetaPoco paging (SQL paging) + /// + public IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy) + { + if (orderBy == null) throw new ArgumentNullException("orderBy"); + + var sql = new Sql(); + sql.Select("*").From(); + + Sql resultQuery; + if (query != null) + { + var translator = new SqlTranslator(sql, query); + resultQuery = translator.Translate(); + } + else + { + resultQuery = sql; + } + + //get the referenced column name + var expressionMember = ExpressionHelper.GetMemberInfo(orderBy); + //now find the mapped column name + var mapper = MappingResolver.Current.ResolveMapperByType(typeof(IUser)); + var mappedField = mapper.Map(expressionMember.Name); + if (mappedField.IsNullOrWhiteSpace()) + { + throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); + } + //need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177 + resultQuery.OrderBy(string.Format("({0})", mappedField)); + + var pagedResult = Database.Page(pageIndex + 1, pageSize, resultQuery); + + totalRecords = Convert.ToInt32(pagedResult.TotalItems); + + //now that we have the user dto's we need to construct true members from the list. + if (totalRecords == 0) + { + return Enumerable.Empty(); + } + + var result = GetAll(pagedResult.Items.Select(x => x.Id).ToArray()); + + //now we need to ensure this result is also ordered by the same order by clause + return result.OrderBy(orderBy.Compile()); + } + /// /// Returns permissions for a given user for any number of nodes /// /// /// /// - public IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds) + public IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) { - var repo = new PermissionRepository(UnitOfWork); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); return repo.GetUserPermissionsForEntities(userId, entityIds); } - public IProfile GetProfileByUserName(string username) + /// + /// Replaces the same permission set for a single user to any number of entities + /// + /// + /// + /// + public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) { - var sql = GetBaseQuery(false); - sql.Where("umbracoUser.userLogin = @Username", new { Username = username }); - - var dto = Database.FirstOrDefault(sql); - - if (dto == null) - return null; - - return new Profile(dto.Id, dto.UserName); - } - - public IUser GetUserByUserName(string username) - { - var sql = GetBaseQuery(false); - sql.Where("umbracoUser.userLogin = @Username", new { Username = username }); - - var dto = Database.FirstOrDefault(sql); - - if (dto == null) - return null; - - return new User(_userTypeRepository.Get(dto.Type)) - { - Id = dto.Id, - Email = dto.Email, - Language = dto.UserLanguage, - Name = dto.UserName, - NoConsole = dto.NoConsole, - IsLockedOut = dto.Disabled - }; - - } - - public IUser GetUserById(int id) - { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - - var dto = Database.FirstOrDefault(sql); - - if (dto == null) - return null; - - return new User(_userTypeRepository.Get(dto.Type)) - { - Id = dto.Id, - Email = dto.Email, - Language = dto.UserLanguage, - Name = dto.UserName, - NoConsole = dto.NoConsole, - IsLockedOut = dto.Disabled - }; - + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); + repo.ReplaceUserPermissions(userId, permissions, entityIds); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index e8257880cf..175ac87218 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -120,5 +121,42 @@ namespace Umbraco.Core.Persistence.Repositories } } } + + /// + /// This is a fix for U4-1407 - when property types are added to a content type - the property of the entity are not actually created + /// and we get YSODs + /// + /// + /// + /// + /// + /// + /// + protected PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentTypeComposition contentType, DateTime createDate, DateTime updateDate) + { + var sql = new Sql(); + sql.Select("*") + .From() + .InnerJoin() + .On(left => left.PropertyTypeId, right => right.Id) + .Where(x => x.NodeId == id) + .Where(x => x.VersionId == versionId); + + var propertyDataDtos = Database.Fetch(sql); + var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate); + var properties = propertyFactory.BuildEntity(propertyDataDtos).ToArray(); + + var newProperties = properties.Where(x => x.HasIdentity == false && x.PropertyType.HasIdentity); + foreach (var property in newProperties) + { + var propertyDataDto = new PropertyDataDto { NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = versionId }; + int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto)); + + property.Version = versionId; + property.Id = primaryKey; + } + + return new PropertyCollection(properties); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 7e0b506424..a381443561 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -1,4 +1,5 @@ using Umbraco.Core.Configuration; +using System; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Repositories; @@ -12,12 +13,28 @@ namespace Umbraco.Core.Persistence public class RepositoryFactory { private readonly bool _disableAllCache; + private readonly CacheHelper _cacheHelper; private readonly IUmbracoSettingsSection _settings; + #region Ctors public RepositoryFactory() : this(false, UmbracoConfig.For.UmbracoSettings()) { - + } + + public RepositoryFactory(CacheHelper cacheHelper) + : this(false, UmbracoConfig.For.UmbracoSettings()) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _disableAllCache = false; + _cacheHelper = cacheHelper; + } + + public RepositoryFactory(bool disableAllCache, CacheHelper cacheHelper) + : this(disableAllCache, UmbracoConfig.For.UmbracoSettings()) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _cacheHelper = cacheHelper; } public RepositoryFactory(bool disableAllCache) @@ -30,9 +47,17 @@ namespace Umbraco.Core.Persistence { _disableAllCache = disableAllCache; _settings = settings; - + _cacheHelper = _disableAllCache ? CacheHelper.CreateDisabledCacheHelper() : ApplicationContext.Current.ApplicationCache; } + internal RepositoryFactory(bool disableAllCache, IUmbracoSettingsSection settings, CacheHelper cacheHelper) + { + _disableAllCache = disableAllCache; + _settings = settings; + _cacheHelper = cacheHelper; + } + #endregion + public virtual ITagsRepository CreateTagsRepository(IDatabaseUnitOfWork uow) { return new TagsRepository( @@ -43,18 +68,19 @@ namespace Umbraco.Core.Persistence public virtual IContentRepository CreateContentRepository(IDatabaseUnitOfWork uow) { return new ContentRepository( - uow, + uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, CreateContentTypeRepository(uow), CreateTemplateRepository(uow), - CreateTagsRepository(uow)) { EnsureUniqueNaming = _settings.Content.EnsureUniqueNaming }; + CreateTagsRepository(uow), + _cacheHelper) { EnsureUniqueNaming = _settings.Content.EnsureUniqueNaming }; } public virtual IContentTypeRepository CreateContentTypeRepository(IDatabaseUnitOfWork uow) { return new ContentTypeRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, new TemplateRepository(uow, NullCacheProvider.Current)); } @@ -69,7 +95,7 @@ namespace Umbraco.Core.Persistence { return new DictionaryRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, CreateLanguageRepository(uow)); } @@ -77,7 +103,7 @@ namespace Umbraco.Core.Persistence { return new LanguageRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } public virtual IMediaRepository CreateMediaRepository(IDatabaseUnitOfWork uow) @@ -93,7 +119,7 @@ namespace Umbraco.Core.Persistence { return new MediaTypeRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } public virtual IRelationRepository CreateRelationRepository(IDatabaseUnitOfWork uow) @@ -133,19 +159,22 @@ namespace Umbraco.Core.Persistence NullCacheProvider.Current); } - internal virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow) + public virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow) { return new UserTypeRepository( uow, - NullCacheProvider.Current); + //There's not many user types but we query on users all the time so the result needs to be cached + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } - internal virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow) - { + public virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow) + { return new UserRepository( uow, - NullCacheProvider.Current, - CreateUserTypeRepository(uow)); + //Need to cache users - we look up user information more than anything in the back office! + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, + CreateUserTypeRepository(uow), + _cacheHelper); } internal virtual IMacroRepository CreateMacroRepository(IDatabaseUnitOfWork uow) @@ -153,20 +182,27 @@ namespace Umbraco.Core.Persistence return new MacroRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } - internal virtual IMemberRepository CreateMemberRepository(IDatabaseUnitOfWork uow) + public virtual IMemberRepository CreateMemberRepository(IDatabaseUnitOfWork uow) { - return new MemberRepository(uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, + return new MemberRepository( + uow, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, CreateMemberTypeRepository(uow), + CreateMemberGroupRepository(uow), CreateTagsRepository(uow)); } - internal virtual IMemberTypeRepository CreateMemberTypeRepository(IDatabaseUnitOfWork uow) + public virtual IMemberTypeRepository CreateMemberTypeRepository(IDatabaseUnitOfWork uow) { - return new MemberTypeRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + return new MemberTypeRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } - internal virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow) + public virtual IMemberGroupRepository CreateMemberGroupRepository(IDatabaseUnitOfWork uow) + { + return new MemberGroupRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, _cacheHelper); + } + + public virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow) { return new EntityRepository(uow); } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 1182487376..b4c0020305 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -2,29 +2,23 @@ using System; using System.Collections.Generic; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { - internal static class SqlSyntaxProviderExtensions - { - /// - /// Returns the quotes tableName.columnName combo - /// - /// - /// - /// - /// - public static string GetQuotedColumn(this ISqlSyntaxProvider sql, string tableName, string columnName) - { - return sql.GetQuotedTableName(tableName) + "." + sql.GetQuotedColumnName(columnName); - } - } - /// /// Defines an SqlSyntaxProvider /// public interface ISqlSyntaxProvider { + string EscapeString(string val); + + string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType); + string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType); + string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType); + string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType); + string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType); + string GetQuotedTableName(string tableName); string GetQuotedColumnName(string columnName); string GetQuotedName(string name); @@ -65,9 +59,12 @@ namespace Umbraco.Core.Persistence.SqlSyntax bool SupportsClustered(); bool SupportsIdentityInsert(); bool? SupportsCaseInsensitiveQueries(Database db); + IEnumerable GetTablesInSchema(Database db); IEnumerable GetColumnsInSchema(Database db); IEnumerable> GetConstraintsPerTable(Database db); IEnumerable> GetConstraintsPerColumn(Database db); + + IEnumerable> GetDefinedIndexes(Database db); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntax.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntax.cs new file mode 100644 index 0000000000..00b07ae15a --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntax.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Persistence.SqlSyntax +{ + /// + /// Static class that provides simple access to the MySql SqlSyntax Provider + /// + internal static class MySqlSyntax + { + public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index 8f466e3829..ba6f4dcc7e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -6,14 +6,6 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Persistence.SqlSyntax { - /// - /// Static class that provides simple access to the MySql SqlSyntax Provider - /// - internal static class MySqlSyntax - { - public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } } - } - /// /// Represents an SqlSyntaxProvider for MySql /// @@ -44,7 +36,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax List list; try { + //needs to be open to read the schema name db.OpenSharedConnection(); + var items = db.Fetch( "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @TableSchema", @@ -63,7 +57,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax List list; try { + //needs to be open to read the schema name db.OpenSharedConnection(); + var items = db.Fetch( "SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @TableSchema", @@ -86,6 +82,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax List> list; try { + //needs to be open to read the schema name + db.OpenSharedConnection(); + //Does not include indexes and constraints are named differently var items = db.Fetch( @@ -105,6 +104,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax List> list; try { + //needs to be open to read the schema name + db.OpenSharedConnection(); + //Does not include indexes and constraints are named differently var items = db.Fetch( @@ -123,12 +125,43 @@ namespace Umbraco.Core.Persistence.SqlSyntax return list; } + public override IEnumerable> GetDefinedIndexes(Database db) + { + List> list; + try + { + //needs to be open to read the schema name + db.OpenSharedConnection(); + + var indexes = + db.Fetch(@"SELECT DISTINCT + TABLE_NAME, INDEX_NAME, COLUMN_NAME, CASE NON_UNIQUE WHEN 1 THEN 0 ELSE 1 END AS `UNIQUE` +FROM INFORMATION_SCHEMA.STATISTICS +WHERE TABLE_SCHEMA = @TableSchema +AND INDEX_NAME <> COLUMN_NAME AND INDEX_NAME <> 'PRIMARY' +ORDER BY TABLE_NAME, INDEX_NAME", + new { TableSchema = db.Connection.Database }); + list = + indexes.Select( + item => + new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE == 1)) + .ToList(); + } + finally + { + db.CloseSharedConnection(); + } + return list; + } + public override bool DoesTableExist(Database db, string tableName) { long result; try { + //needs to be open to read the schema name db.OpenSharedConnection(); + result = db.ExecuteScalar("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES " + "WHERE TABLE_NAME = @TableName AND " + @@ -185,7 +218,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax { string primaryKey = string.Empty; var columnDefinition = table.Columns.FirstOrDefault(x => x.IsPrimaryKey); - if (columnDefinition != null && columnDefinition.PrimaryKeyColumns.Contains(",") == false) + if (columnDefinition != null) { string columns = string.IsNullOrEmpty(columnDefinition.PrimaryKeyColumns) ? GetQuotedColumnName(columnDefinition.Name) @@ -335,5 +368,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax // add message to check with their hosting provider return supportsCaseInsensitiveQueries; } + + public override string EscapeString(string val) + { + return PetaPocoExtensions.EscapeAtSymbols(MySql.Data.MySqlClient.MySqlHelper.EscapeString(val)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntax.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntax.cs new file mode 100644 index 0000000000..29660355fe --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntax.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Persistence.SqlSyntax +{ + /// + /// Static class that provides simple access to the Sql CE SqlSyntax Provider + /// + internal static class SqlCeSyntax + { + public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 106b45ffed..170b10cb5e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -3,17 +3,10 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { - /// - /// Static class that provides simple access to the Sql CE SqlSyntax Provider - /// - internal static class SqlCeSyntax - { - public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } } - } - /// /// Represents an SqlSyntaxProvider for Sql Ce /// @@ -67,6 +60,76 @@ namespace Umbraco.Core.Persistence.SqlSyntax return indexType; } + public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEqualComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for = comparison with NText columns but allows this syntax + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnStartsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEndsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + public override string GetQuotedTableName(string tableName) { return string.Format("[{0}]", tableName); @@ -163,6 +226,18 @@ namespace Umbraco.Core.Persistence.SqlSyntax indexItem.INDEX_NAME))).ToList(); } + public override IEnumerable> GetDefinedIndexes(Database db) + { + var items = + db.Fetch( + @"SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, [UNIQUE] FROM INFORMATION_SCHEMA.INDEXES +WHERE INDEX_NAME NOT LIKE 'PK_%' +ORDER BY TABLE_NAME, INDEX_NAME"); + return + items.Select( + item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE)); + } + public override bool DoesTableExist(Database db, string tableName) { var result = diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntax.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntax.cs new file mode 100644 index 0000000000..a2f000e4b8 --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntax.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Persistence.SqlSyntax +{ + /// + /// Static class that provides simple access to the Sql Server SqlSyntax Provider + /// + internal static class SqlServerSyntax + { + public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 3e37674ddf..ca9e88fb1e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -2,31 +2,10 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { - /// - /// Static class that provides simple access to the Sql Server SqlSyntax Provider - /// - internal static class SqlServerSyntax - { - public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } } - } - - /// - /// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...) - /// - internal enum SqlServerVersionName - { - Invalid = -1, - V7 = 0, - V2000 = 1, - V2005 = 2, - V2008 = 3, - V2012 = 4, - Other = 5 - } - /// /// Represents an SqlSyntaxProvider for Sql Server /// @@ -55,6 +34,76 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// internal Lazy VersionName { get; set; } + public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEqualComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for = comparison with NText columns but allows this syntax + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnStartsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEndsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + public override string GetQuotedTableName(string tableName) { return string.Format("[{0}]", tableName); @@ -102,6 +151,22 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)).ToList(); } + public override IEnumerable> GetDefinedIndexes(Database db) + { + var items = + db.Fetch( + @"select T.name as TABLE_NAME, I.name as INDEX_NAME, AC.Name as COLUMN_NAME, +CASE WHEN I.is_unique_constraint = 1 OR I.is_unique = 1 THEN 1 ELSE 0 END AS [UNIQUE] +from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id] + inner join sys.index_columns as IC on IC.[object_id] = I.[object_id] and IC.[index_id] = I.[index_id] + inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id] +WHERE I.name NOT LIKE 'PK_%' +order by T.name, I.name"); + return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, + item.UNIQUE == 1)).ToList(); + + } + public override bool DoesTableExist(Database db, string tableName) { var result = diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerVersionName.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerVersionName.cs new file mode 100644 index 0000000000..37870a9536 --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerVersionName.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Core.Persistence.SqlSyntax +{ + /// + /// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...) + /// + internal enum SqlServerVersionName + { + Invalid = -1, + V7 = 0, + V2000 = 1, + V2005 = 2, + V2008 = 3, + V2012 = 4, + Other = 5 + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index e86222ac50..1ceeddafb7 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -102,6 +103,41 @@ namespace Umbraco.Core.Persistence.SqlSyntax DbTypeMap.Set(DbType.Binary, BlobColumnDefinition); } + public virtual string EscapeString(string val) + { + return PetaPocoExtensions.EscapeAtSymbols(val.Replace("'", "''")); + } + + public virtual string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) = '{1}'", column, value.ToUpper()); + } + + public virtual string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like '{1}%'", column, value.ToUpper()); + } + + public virtual string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like '%{1}'", column, value.ToUpper()); + } + + public virtual string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like '%{1}%'", column, value.ToUpper()); + } + + public virtual string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like '{1}'", column, value.ToUpper()); + } + public virtual string GetQuotedTableName(string tableName) { return string.Format("\"{0}\"", tableName); @@ -176,6 +212,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax return new List>(); } + public abstract IEnumerable> GetDefinedIndexes(Database db); + public virtual bool DoesTableExist(Database db, string tableName) { return false; diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs new file mode 100644 index 0000000000..1fa235720f --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs @@ -0,0 +1,33 @@ +namespace Umbraco.Core.Persistence.SqlSyntax +{ + internal static class SqlSyntaxProviderExtensions + { + /// + /// Returns the quotes tableName.columnName combo + /// + /// + /// + /// + /// + public static string GetQuotedColumn(this ISqlSyntaxProvider sql, string tableName, string columnName) + { + return sql.GetQuotedTableName(tableName) + "." + sql.GetQuotedColumnName(columnName); + } + + /// + /// This is used to generate a delete query that uses a sub-query to select the data, it is required because there's a very particular syntax that + /// needs to be used to work for all servers: MySql, SQLCE and MSSQL + /// + /// + /// + /// See: http://issues.umbraco.org/issue/U4-3876 + /// + public static string GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery) + { + return string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)", + sqlProvider.GetQuotedTableName(tableName), + sqlProvider.GetQuotedColumnName(columnName), + subQuery.SQL); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index d3305af031..2f0f7fcec6 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core /// it will use the cached resolved plugins that it has already found which means that no assembly scanning is necessary. This leads /// to much faster startup times. /// - internal class PluginManager + public class PluginManager { private readonly ApplicationContext _appContext; private const string CacheKey = "umbraco-plugins.list"; @@ -107,7 +107,7 @@ namespace Umbraco.Core /// /// The setter is generally only used for unit tests /// - internal static PluginManager Current + public static PluginManager Current { get { @@ -507,15 +507,6 @@ namespace Umbraco.Core return ResolveTypes(); } - /// - /// Returns all available PropertyValueConverter - /// - /// - internal IEnumerable ResolvePropertyValueConverters() - { - return ResolveTypes(); - } - /// /// Returns all available IDataType in application /// @@ -560,16 +551,7 @@ namespace Umbraco.Core { return ResolveTypesWithAttribute(); } - - /// - /// Returns all available IMigrations in application - /// - /// - internal IEnumerable ResolveMigrationTypes() - { - return ResolveTypes(); - } - + /// /// Returns all SqlSyntaxProviders with the SqlSyntaxProviderAttribute /// @@ -783,12 +765,13 @@ namespace Umbraco.Core UpdateCachedPluginsFile(typeList.GetTypes(), resolutionKind); } + #region Public Methods /// /// Generic method to find the specified type and cache the result /// /// /// - internal IEnumerable ResolveTypes(bool cacheResult = true, IEnumerable specificAssemblies = null) + public IEnumerable ResolveTypes(bool cacheResult = true, IEnumerable specificAssemblies = null) { return ResolveTypes( () => TypeFinder.FindClassesOfType(specificAssemblies ?? AssembliesToScan), @@ -802,11 +785,11 @@ namespace Umbraco.Core /// /// /// - internal IEnumerable ResolveTypesWithAttribute(bool cacheResult = true) + public IEnumerable ResolveTypesWithAttribute(bool cacheResult = true, IEnumerable specificAssemblies = null) where TAttribute : Attribute { return ResolveTypes( - () => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan), + () => TypeFinder.FindClassesOfTypeWithAttribute(specificAssemblies ?? AssembliesToScan), TypeResolutionKind.FindTypesWithAttribute, cacheResult); } @@ -816,14 +799,15 @@ namespace Umbraco.Core /// /// /// - internal IEnumerable ResolveAttributedTypes(bool cacheResult = true) + public IEnumerable ResolveAttributedTypes(bool cacheResult = true, IEnumerable specificAssemblies = null) where TAttribute : Attribute { return ResolveTypes( - () => TypeFinder.FindClassesWithAttribute(AssembliesToScan), + () => TypeFinder.FindClassesWithAttribute(specificAssemblies ?? AssembliesToScan), TypeResolutionKind.FindAttributedTypes, cacheResult); - } + } + #endregion /// /// Used for unit tests diff --git a/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs b/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs index 641e060d50..70bb014bb3 100644 --- a/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs +++ b/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs @@ -4,12 +4,12 @@ namespace Umbraco.Core.Profiling { public class ProfilingViewEngine: IViewEngine { - private readonly IViewEngine _inner; + internal readonly IViewEngine Inner; private readonly string _name; public ProfilingViewEngine(IViewEngine inner) { - _inner = inner; + Inner = inner; _name = inner.GetType().Name; } @@ -17,7 +17,7 @@ namespace Umbraco.Core.Profiling { using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindPartialView, {1}, {2}", _name, partialViewName, useCache))) { - return WrapResult(_inner.FindPartialView(controllerContext, partialViewName, useCache)); + return WrapResult(Inner.FindPartialView(controllerContext, partialViewName, useCache)); } } @@ -25,7 +25,7 @@ namespace Umbraco.Core.Profiling { using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindView, {1}, {2}, {3}", _name, viewName, masterName, useCache))) { - return WrapResult(_inner.FindView(controllerContext, viewName, masterName, useCache)); + return WrapResult(Inner.FindView(controllerContext, viewName, masterName, useCache)); } } @@ -41,7 +41,7 @@ namespace Umbraco.Core.Profiling { using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.ReleaseView, {1}", _name, view.GetType().Name))) { - _inner.ReleaseView(controllerContext, view); + Inner.ReleaseView(controllerContext, view); } } } diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs index 4857e2bb07..10bdd053a8 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.PropertyEditors { @@ -8,5 +10,27 @@ namespace Umbraco.Core.PropertyEditors [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] internal class DefaultPropertyValueConverterAttribute : Attribute { + public DefaultPropertyValueConverterAttribute() + { + DefaultConvertersToShadow = Enumerable.Empty(); + } + + public DefaultPropertyValueConverterAttribute(params Type[] convertersToShadow) + { + DefaultConvertersToShadow = convertersToShadow; + } + + /// + /// A DefaultPropertyValueConverter can 'shadow' other default property value converters so that + /// a DefaultPropertyValueConverter can be more specific than another one. + /// + /// + /// An example where this is useful is that both the RelatedLiksEditorValueConverter and the JsonValueConverter + /// will be returned as value converters for the Related Links Property editor, however the JsonValueConverter + /// is a very generic converter and the RelatedLiksEditorValueConverter is more specific than it, so the RelatedLiksEditorValueConverter + /// can specify that it 'shadows' the JsonValueConverter. + /// + public IEnumerable DefaultConvertersToShadow { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs index cc89e7671b..9e2fb5d10a 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs @@ -97,10 +97,7 @@ namespace Umbraco.Core.PropertyEditors CreateMap("mediaCurrent", Constants.PropertyEditors.MediaPickerAlias); - CreateMap("number", Constants.PropertyEditors.IntegerAlias); - - - + CreateMap("number", Constants.PropertyEditors.IntegerAlias); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs index e5eac179df..441d7fde2c 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.PropertyEditors /// /// This resolver will contain any property editors defined in manifests as well! /// - internal class PropertyEditorResolver : LazyManyObjectsResolverBase + public class PropertyEditorResolver : LazyManyObjectsResolverBase { public PropertyEditorResolver(Func> typeListProducerList) : base(typeListProducerList, ObjectLifetimeScope.Application) diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs index 53fecdac9d..295fdb4b09 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Threading; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.PropertyEditors @@ -36,5 +38,41 @@ namespace Umbraco.Core.PropertyEditors { get { return Values; } } + + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private Tuple[] _defaults = null; + + /// + /// Caches and gets the default converters with their metadata + /// + internal Tuple[] DefaultConverters + { + get + { + using (var locker = new UpgradeableReadLock(_lock)) + { + if (_defaults == null) + { + locker.UpgradeToWriteLock(); + + var defaultConvertersWithAttributes = Converters + .Select(x => new + { + attribute = x.GetType().GetCustomAttribute(false), + converter = x + }) + .Where(x => x.attribute != null) + .ToArray(); + + _defaults = defaultConvertersWithAttributes + .Select( + x => new Tuple(x.converter, x.attribute)) + .ToArray(); + } + + return _defaults; + } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs index 48ede3d2b8..306ca4dbb0 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Xml.Linq; using Newtonsoft.Json; @@ -127,7 +128,7 @@ namespace Umbraco.Core.PropertyEditors /// public DataTypeDatabaseType GetDatabaseType() { - switch (ValueType.ToUpper()) + switch (ValueType.ToUpper(CultureInfo.InvariantCulture)) { case "INT": case "INTEGER": diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs index 435a9833a6..769f3928a7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs @@ -26,13 +26,14 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters if (source == null) return DateTime.MinValue; // in XML a DateTime is: string - format "yyyy-MM-ddTHH:mm:ss" + // Actually, not always sometimes it is formatted in UTC style with 'Z' suffixed on the end but that is due to this bug: + // http://issues.umbraco.org/issue/U4-4145, http://issues.umbraco.org/issue/U4-3894 + // We should just be using TryConvertTo instead. var sourceString = source as string; if (sourceString != null) { - DateTime value; - return DateTime.TryParseExact(sourceString, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out value) - ? value - : DateTime.MinValue; + var attempt = sourceString.TryConvertTo(); + return attempt.Success == false ? DateTime.MinValue : attempt.Result; } // in the database a DateTime is: DateTime @@ -47,7 +48,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) { // source should come from ConvertSource and be a DateTime already - return XmlConvert.ToString((DateTime) source, "yyyy-MM-ddTHH:mm:ss"); + return XmlConvert.ToString((DateTime)source, XmlDateTimeSerializationMode.Unspecified); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs index 17ecbaeed1..6725257888 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs @@ -31,6 +31,10 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters var sourceString = source.ToString(); if (string.IsNullOrWhiteSpace(sourceString)) return Enumerable.Empty(); + //SD: I have no idea why this logic is here, I'm pretty sure we've never saved the multiple txt string + // as xml in the database, it's always been new line delimited. Will ask Stephen about this. + // In the meantime, we'll do this xml check, see if it parses and if not just continue with + // splitting by newline var values = new List(); var pos = sourceString.IndexOf("", StringComparison.Ordinal); while (pos >= 0) @@ -41,6 +45,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters values.Add(value); pos = sourceString.IndexOf("", pos, StringComparison.Ordinal); } + + // Fall back on normal behaviour + if (values.Any() == false) + { + return sourceString.Split(Environment.NewLine.ToCharArray()); + } + return values.ToArray(); } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs new file mode 100644 index 0000000000..45563d11e8 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + /// + /// Ensures that no matter what is selected in (editor), the value results in a string. + /// + /// + /// For more details see issues http://issues.umbraco.org/issue/U4-3776 (MNTP) + /// and http://issues.umbraco.org/issue/U4-4160 (media picker). + /// The cache level is set to .Content because the string is supposed to depend + /// on the source value only, and not on any other content. It is NOT appropriate + /// to use that converter for values whose .ToString() would depend on other content. + /// + [DefaultPropertyValueConverter] + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class MustBeStringValueConverter : PropertyValueConverterBase + { + private static readonly string[] Aliases = + { + Constants.PropertyEditors.MultiNodeTreePickerAlias, + Constants.PropertyEditors.MultipleMediaPickerAlias + }; + + public override bool IsConverter(PublishedPropertyType propertyType) + { + return Aliases.Contains(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) return null; + return source.ToString(); + } + } +} diff --git a/src/Umbraco.Core/Publishing/PublishStatus.cs b/src/Umbraco.Core/Publishing/PublishStatus.cs index 865f00c58c..519046f5f9 100644 --- a/src/Umbraco.Core/Publishing/PublishStatus.cs +++ b/src/Umbraco.Core/Publishing/PublishStatus.cs @@ -26,9 +26,9 @@ namespace Umbraco.Core.Publishing /// public PublishStatus(IContent content) : this(content, PublishStatusType.Success) - { + { } - + public IContent ContentItem { get; private set; } public PublishStatusType StatusType { get; internal set; } diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index b642796932..45c290daba 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -137,12 +137,19 @@ namespace Umbraco.Core.Security { if (response == null) throw new ArgumentNullException("response"); //remove the cookie - var cookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") + var authCookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") { Expires = DateTime.Now.AddYears(-1), Path = "/" }; - response.Headers.AddCookies(new[] { cookie }); + //remove the preview cookie too + var prevCookie = new CookieHeaderValue(Constants.Web.PreviewCookieName, "") + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }; + + response.Headers.AddCookies(new[] { authCookie, prevCookie }); } /// @@ -259,21 +266,27 @@ namespace Umbraco.Core.Security private static void Logout(this HttpContextBase http, string cookieName) { if (http == null) throw new ArgumentNullException("http"); - //remove from the request - http.Request.Cookies.Remove(cookieName); + //clear the preview cookie too + var cookies = new[] { cookieName, Constants.Web.PreviewCookieName }; + foreach (var c in cookies) + { + //remove from the request + http.Request.Cookies.Remove(c); + + //expire from the response + var formsCookie = http.Response.Cookies[c]; + if (formsCookie != null) + { + //this will expire immediately and be removed from the browser + formsCookie.Expires = DateTime.Now.AddYears(-1); + } + else + { + //ensure there's def an expired cookie + http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) }); + } + } - //expire from the response - var formsCookie = http.Response.Cookies[cookieName]; - if (formsCookie != null) - { - //this will expire immediately and be removed from the browser - formsCookie.Expires = DateTime.Now.AddYears(-1); - } - else - { - //ensure there's def an expired cookie - http.Response.Cookies.Add(new HttpCookie(cookieName) { Expires = DateTime.Now.AddYears(-1) }); - } } private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName) diff --git a/src/Umbraco.Core/Security/IUsersMembershipProvider.cs b/src/Umbraco.Core/Security/IUsersMembershipProvider.cs new file mode 100644 index 0000000000..8f2dbf8b00 --- /dev/null +++ b/src/Umbraco.Core/Security/IUsersMembershipProvider.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Security +{ + /// + /// A marker interface used internally to identify Umbraco built-in Users membership providers + /// + internal interface IUsersMembershipProvider + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index d1129e097d..f5ba315177 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Web.Configuration; using System.Web.Hosting; using System.Web.Security; +using Umbraco.Core.Logging; namespace Umbraco.Core.Security { @@ -18,7 +19,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is 7 /// - protected virtual int DefaultMinPasswordLength + public virtual int DefaultMinPasswordLength { get { return 7; } } @@ -26,7 +27,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is 1 /// - protected virtual int DefaultMinNonAlphanumericChars + public virtual int DefaultMinNonAlphanumericChars { get { return 1; } } @@ -34,7 +35,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is false to use better security /// - protected virtual bool DefaultUseLegacyEncoding + public virtual bool DefaultUseLegacyEncoding { get { return false; } } @@ -44,7 +45,7 @@ namespace Umbraco.Core.Security /// authenticate the username + password when ChangePassword is called. This property exists purely for /// backwards compatibility. /// - internal virtual bool AllowManuallyChangingPassword + public virtual bool AllowManuallyChangingPassword { get { return false; } } @@ -60,7 +61,8 @@ namespace Umbraco.Core.Security private string _passwordStrengthRegularExpression; private bool _requiresQuestionAndAnswer; private bool _requiresUniqueEmail; - private bool _useLegacyEncoding; + private string _customHashAlgorithmType ; + internal bool UseLegacyEncoding; #region Properties @@ -208,7 +210,7 @@ namespace Umbraco.Core.Security _enablePasswordRetrieval = config.GetValue("enablePasswordRetrieval", false); _enablePasswordReset = config.GetValue("enablePasswordReset", false); _requiresQuestionAndAnswer = config.GetValue("requiresQuestionAndAnswer", false); - _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", false); + _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", true); _maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); _passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0); _minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80); @@ -220,10 +222,10 @@ namespace Umbraco.Core.Security _applicationName = GetDefaultAppName(); //by default we will continue using the legacy encoding. - _useLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); + UseLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); - // make sure password format is clear by default. - string str = config["passwordFormat"] ?? "Clear"; + // make sure password format is Hashed by default. + string str = config["passwordFormat"] ?? "Hashed"; switch (str.ToLower()) { @@ -244,8 +246,13 @@ namespace Umbraco.Core.Security } if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) - throw new ProviderException("Provider can not retrieve hashed password"); - + { + var ex = new ProviderException("Provider can not retrieve a hashed password"); + LogHelper.Error("Cannot specify a Hashed password format with the enabledPasswordRetrieval option set to true", ex); + throw ex; + } + + _customHashAlgorithmType = config.GetValue("hashAlgorithmType", string.Empty); } /// @@ -271,14 +278,19 @@ namespace Umbraco.Core.Security AlphanumericChars, Strength } - + /// - /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to + /// Processes a request to update the password for a membership user. /// - /// - /// - /// - /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// + /// + /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to + /// public sealed override bool ChangePassword(string username, string oldPassword, string newPassword) { if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false) @@ -287,11 +299,227 @@ namespace Umbraco.Core.Security throw new NotSupportedException("This provider does not support manually changing the password"); } + var args = new ValidatePasswordEventArgs(username, newPassword, false); + OnValidatingPassword(args); + + if (args.Cancel) + { + if (args.FailureInformation != null) + throw args.FailureInformation; + throw new MembershipPasswordException("Change password canceled due to password validation failure."); + } + + if (AllowManuallyChangingPassword == false) + { + if (ValidateUser(username, oldPassword) == false) return false; + } + return PerformChangePassword(username, oldPassword, newPassword); } + /// + /// Processes a request to update the password for a membership user. + /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// protected abstract bool PerformChangePassword(string username, string oldPassword, string newPassword); + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + /// + /// Performs the basic validation before passing off to PerformChangePasswordQuestionAndAnswer + /// + public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + if (RequiresQuestionAndAnswer == false) + { + throw new NotSupportedException("Updating the password Question and Answer is not available if requiresQuestionAndAnswer is not set in web.config"); + } + + if (AllowManuallyChangingPassword == false) + { + if (ValidateUser(username, password) == false) + { + return false; + } + } + + return PerformChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); + } + + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + protected abstract bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer); + + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + /// + /// Ensures the ValidatingPassword event is executed before executing PerformCreateUser and performs basic membership provider validation of values. + /// + public sealed override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + var valStatus = ValidateNewUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey); + if (valStatus != MembershipCreateStatus.Success) + { + status = valStatus; + return null; + } + + return PerformCreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Performs the validation of the information for creating a new user + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected MembershipCreateStatus ValidateNewUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey) + { + var args = new ValidatePasswordEventArgs(username, password, true); + OnValidatingPassword(args); + if (args.Cancel) + { + return MembershipCreateStatus.InvalidPassword; + } + + // Validate password + var passwordValidAttempt = IsPasswordValid(password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength); + if (passwordValidAttempt.Success == false) + { + return MembershipCreateStatus.InvalidPassword; + } + + // Validate email + if (IsEmailValid(email) == false) + { + return MembershipCreateStatus.InvalidEmail; + } + + // Make sure username isn't all whitespace + if (string.IsNullOrWhiteSpace(username.Trim())) + { + return MembershipCreateStatus.InvalidUserName; + } + + // Check password question + if (string.IsNullOrWhiteSpace(passwordQuestion) && RequiresQuestionAndAnswer) + { + return MembershipCreateStatus.InvalidQuestion; + } + + // Check password answer + if (string.IsNullOrWhiteSpace(passwordAnswer) && RequiresQuestionAndAnswer) + { + return MembershipCreateStatus.InvalidAnswer; + } + + return MembershipCreateStatus.Success; + } + + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected abstract MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); + + /// + /// Gets the members password if password retreival is enabled + /// + /// + /// + /// + public sealed override string GetPassword(string username, string answer) + { + if (EnablePasswordRetrieval == false) + throw new ProviderException("Password Retrieval Not Enabled."); + + if (PasswordFormat == MembershipPasswordFormat.Hashed) + throw new ProviderException("Cannot retrieve Hashed passwords."); + + return PerformGetPassword(username, answer); + } + + /// + /// Gets the members password if password retreival is enabled + /// + /// + /// + /// + protected abstract string PerformGetPassword(string username, string answer); + + public sealed override string ResetPassword(string username, string answer) + { + if (EnablePasswordReset == false) + { + throw new NotSupportedException("Password reset is not supported"); + } + + var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); + + var args = new ValidatePasswordEventArgs(username, newPassword, true); + OnValidatingPassword(args); + if (args.Cancel) + { + if (args.FailureInformation != null) + { + throw args.FailureInformation; + } + throw new MembershipPasswordException("Reset password canceled due to password validation failure."); + } + + return PerformResetPassword(username, answer, newPassword); + } + + protected abstract string PerformResetPassword(string username, string answer, string generatedPassword); + protected internal static Attempt IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength) { if (minRequiredNonAlphanumericChars > 0) @@ -319,7 +547,7 @@ namespace Umbraco.Core.Security return Attempt.Succeed(PasswordValidityError.Ok); } - + /// /// Gets the name of the default app. /// @@ -372,34 +600,55 @@ namespace Umbraco.Core.Security return num; } - protected string FormatPasswordForStorage(string pass, string salt) + /// + /// If the password format is a hashed keyed algorithm then we will pre-pend the salt used to hash the password + /// to the hashed password itself. + /// + /// + /// + /// + protected internal string FormatPasswordForStorage(string pass, string salt) { - if (_useLegacyEncoding) + if (UseLegacyEncoding) { return pass; } - - //the better way, we use salt per member - return salt + pass; + + if (PasswordFormat == MembershipPasswordFormat.Hashed) + { + //the better way, we use salt per member + return salt + pass; + } + return pass; } - protected string EncryptOrHashPassword(string pass, string salt) + internal static bool IsEmailValid(string email) + { + const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" + + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? + /// Checks the password. + /// + /// The password. + /// The dbPassword. + /// + protected internal bool CheckPassword(string password, string dbPassword) + { + switch (PasswordFormat) + { + case MembershipPasswordFormat.Encrypted: + var decrypted = DecryptPassword(dbPassword); + return decrypted == password; + case MembershipPasswordFormat.Hashed: + string salt; + var storedHashedPass = StoredPassword(dbPassword, out salt); + var hashed = EncryptOrHashPassword(password, salt); + return storedHashedPass == hashed; + case MembershipPasswordFormat.Clear: + return password == dbPassword; + default: + throw new ArgumentOutOfRangeException(); + } + } + /// /// Encrypt/hash a new password with a new salt /// /// /// /// - protected string EncryptOrHashNewPassword(string newPassword, out string salt) + protected internal string EncryptOrHashNewPassword(string newPassword, out string salt) { salt = GenerateSalt(); return EncryptOrHashPassword(newPassword, salt); } - /// - /// Gets the encrypted or hashed string of an existing password for an existing user - /// - /// The stored string for the password - /// - protected string EncryptOrHashExistingPassword(string storedPassword) - { - if (_useLegacyEncoding) - { - return EncryptOrHashPassword(storedPassword, storedPassword); - } - else - { - string salt; - var pass = StoredPassword(storedPassword, PasswordFormat, out salt); - return EncryptOrHashPassword(pass, salt); - } - } - - protected string DecodePassword(string pass) + protected internal string DecryptPassword(string pass) { //if we are doing it the old way - if (_useLegacyEncoding) + if (UseLegacyEncoding) { return LegacyUnEncodePassword(pass); } @@ -493,7 +750,7 @@ namespace Umbraco.Core.Security case 0: return pass; case 1: - throw new ProviderException("Provider can not decode hashed password"); + throw new ProviderException("Provider can not decrypt hashed password"); default: var bytes = DecryptPassword(Convert.FromBase64String(pass)); return bytes == null ? null : Encoding.Unicode.GetString(bytes, 16, bytes.Length - 16); @@ -504,17 +761,22 @@ namespace Umbraco.Core.Security /// Returns the hashed password without the salt if it is hashed /// /// - /// /// returns the salt /// - internal static string StoredPassword(string storedString, MembershipPasswordFormat format, out string salt) + internal string StoredPassword(string storedString, out string salt) { - switch (format) + if (UseLegacyEncoding) + { + salt = string.Empty; + return storedString; + } + + switch (PasswordFormat) { case MembershipPasswordFormat.Hashed: var saltLen = GenerateSalt(); salt = storedString.Substring(0, saltLen.Length); - return storedString.Substring(saltLen.Length); + return storedString.Substring(saltLen.Length); case MembershipPasswordFormat.Clear: case MembershipPasswordFormat.Encrypted: default: @@ -531,9 +793,9 @@ namespace Umbraco.Core.Security return Convert.ToBase64String(numArray); } - protected HashAlgorithm GetHashAlgorithm(string password) + protected internal HashAlgorithm GetHashAlgorithm(string password) { - if (_useLegacyEncoding) + if (UseLegacyEncoding) { //before we were never checking for an algorithm type so we were always using HMACSHA1 // for any SHA specified algorithm :( so we'll need to keep doing that for backwards compat support. @@ -546,10 +808,21 @@ namespace Umbraco.Core.Security }; } } - + //get the algorithm by name - return HashAlgorithm.Create(Membership.HashAlgorithmType); + if (_customHashAlgorithmType.IsNullOrWhiteSpace()) + { + _customHashAlgorithmType = Membership.HashAlgorithmType; + } + + var alg = HashAlgorithm.Create(_customHashAlgorithmType); + if (alg == null) + { + throw new InvalidOperationException("The hash algorithm specified " + Membership.HashAlgorithmType + " cannot be resolved"); + } + + return alg; } /// @@ -601,5 +874,24 @@ namespace Umbraco.Core.Security return password; } + public override string ToString() + { + var result = base.ToString(); + var sb = new StringBuilder(result); + sb.AppendLine("Name =" + Name); + sb.AppendLine("_applicationName =" + _applicationName); + sb.AppendLine("_enablePasswordReset=" + _enablePasswordReset); + sb.AppendLine("_enablePasswordRetrieval=" + _enablePasswordRetrieval); + sb.AppendLine("_maxInvalidPasswordAttempts=" + _maxInvalidPasswordAttempts); + sb.AppendLine("_minRequiredNonAlphanumericCharacters=" + _minRequiredNonAlphanumericCharacters); + sb.AppendLine("_minRequiredPasswordLength=" + _minRequiredPasswordLength); + sb.AppendLine("_passwordAttemptWindow=" + _passwordAttemptWindow); + sb.AppendLine("_passwordFormat=" + _passwordFormat); + sb.AppendLine("_passwordStrengthRegularExpression=" + _passwordStrengthRegularExpression); + sb.AppendLine("_requiresQuestionAndAnswer=" + _requiresQuestionAndAnswer); + sb.AppendLine("_requiresUniqueEmail=" + _requiresUniqueEmail); + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs new file mode 100644 index 0000000000..4d4207fc67 --- /dev/null +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Hosting; +using System.Web.Security; +using Umbraco.Core.Configuration; +using Umbraco.Core.Security; + +namespace Umbraco.Core.Security +{ + internal static class MembershipProviderExtensions + { + public static MembershipUserCollection FindUsersByName(this MembershipProvider provider, string usernameToMatch) + { + int totalRecords = 0; + return provider.FindUsersByName(usernameToMatch, 0, int.MaxValue, out totalRecords); + } + + public static MembershipUserCollection FindUsersByEmail(this MembershipProvider provider, string emailToMatch) + { + int totalRecords = 0; + return provider.FindUsersByEmail(emailToMatch, 0, int.MaxValue, out totalRecords); + } + + public static MembershipUser CreateUser(this MembershipProvider provider, string username, string password, string email) + { + MembershipCreateStatus status; + var user = provider.CreateUser(username, password, email, null, null, true, null, out status); + if (user == null) + throw new MembershipCreateUserException(status); + return user; + } + + /// + /// Method to get the Umbraco Members membership provider based on it's alias + /// + /// + public static MembershipProvider GetMembersMembershipProvider() + { + if (Membership.Providers[Constants.Conventions.Member.UmbracoMemberProviderName] == null) + { + throw new InvalidOperationException("No membership provider found with name " + Constants.Conventions.Member.UmbracoMemberProviderName); + } + return Membership.Providers[Constants.Conventions.Member.UmbracoMemberProviderName]; + } + + /// + /// Method to get the Umbraco Users membership provider based on it's alias + /// + /// + public static MembershipProvider GetUsersMembershipProvider() + { + if (Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider] == null) + { + throw new InvalidOperationException("No membership provider found with name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); + } + return Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; + } + + public static MembershipUser GetCurrentUser(this MembershipProvider membershipProvider) + { + var username = membershipProvider.GetCurrentUserName(); + return membershipProvider.GetUser(username, true); + } + + /// + /// Just returns the current user's login name (just a wrapper). + /// + /// + /// + public static string GetCurrentUserName(this MembershipProvider membershipProvider) + { + if (HostingEnvironment.IsHosted) + { + HttpContext current = HttpContext.Current; + if (current != null) + return current.User.Identity.Name; + } + IPrincipal currentPrincipal = Thread.CurrentPrincipal; + if (currentPrincipal == null || currentPrincipal.Identity == null) + return string.Empty; + else + return currentPrincipal.Identity.Name; + } + + /// + /// Returns true if the provider specified is a built-in Umbraco users provider + /// + /// + /// + public static bool IsUmbracoUsersProvider(this MembershipProvider membershipProvider) + { + return (membershipProvider is IUsersMembershipProvider); + } + + /// + /// Returns true if the provider specified is a built-in Umbraco membership provider + /// + /// + /// + public static bool IsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (membershipProvider is UmbracoMembershipProviderBase); + } + + //TODO: Add role provider checks too + + public static UmbracoMembershipProviderBase AsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (UmbracoMembershipProviderBase)membershipProvider; + } + + } +} diff --git a/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs b/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs new file mode 100644 index 0000000000..fc07abc896 --- /dev/null +++ b/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs @@ -0,0 +1,23 @@ +using System.Collections.Specialized; + +namespace Umbraco.Core.Security +{ + /// + /// An interface for exposing the content type properties for storing membership data in when + /// a membership provider's data is backed by an Umbraco content type. + /// + public interface IUmbracoMemberTypeMembershipProvider + { + + string LockPropertyTypeAlias { get; } + string LastLockedOutPropertyTypeAlias { get; } + string FailedPasswordAttemptsPropertyTypeAlias { get; } + string ApprovedPropertyTypeAlias { get; } + string CommentPropertyTypeAlias { get; } + string LastLoginPropertyTypeAlias { get; } + string LastPasswordChangedPropertyTypeAlias { get; } + string PasswordRetrievalQuestionPropertyTypeAlias { get; } + string PasswordRetrievalAnswerPropertyTypeAlias { get; } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs b/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs new file mode 100644 index 0000000000..12ca2b4b4a --- /dev/null +++ b/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs @@ -0,0 +1,85 @@ +using System.Text; +using System.Web.Security; + +namespace Umbraco.Core.Security +{ + /// + /// A base membership provider class for umbraco providers + /// + public abstract class UmbracoMembershipProviderBase : MembershipProviderBase + { + public abstract string DefaultMemberTypeAlias { get; } + + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected sealed override MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + return PerformCreateUser(DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Adds a new membership user to the data source. + /// + /// The member type alias to use when creating the member + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + //do the base validation first + var valStatus = ValidateNewUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey); + if (valStatus != MembershipCreateStatus.Success) + { + status = valStatus; + return null; + } + + return PerformCreateUser(memberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Adds a new membership user to the data source. + /// + /// The member type alias to use when creating the member + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected abstract MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); + + public override string ToString() + { + var result = base.ToString(); + var sb = new StringBuilder(result); + sb.AppendLine("DefaultMemberTypeAlias=" + DefaultMemberTypeAlias); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 5c1df82861..d0cbc086d6 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; @@ -50,22 +51,40 @@ namespace Umbraco.Core.Services public ContentService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IPublishingStrategy publishingStrategy) { - if (provider == null) throw new ArgumentNullException("provider"); - if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); - if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy"); - _uowProvider = provider; - _publishingStrategy = publishingStrategy; + if (provider == null) throw new ArgumentNullException("provider"); + if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); + if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy"); + _uowProvider = provider; + _publishingStrategy = publishingStrategy; _repositoryFactory = repositoryFactory; } - //TODO: There are various ways to expose permission setting on this service, we just need to list out the different ways we'll need to - // be able to acheive this for the core, for now this is here so I can run a unit test. - internal void AssignContentPermissions(IContent entity, char permission, IEnumerable userIds) - { + /// + /// Assigns a single permission to the current content item for the specified user ids + /// + /// + /// + /// + public void AssignContentPermission(IContent entity, char permission, IEnumerable userIds) + { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { - repository.AssignEntityPermissions(entity, permission, userIds); + repository.AssignEntityPermission(entity, permission, userIds); + } + } + + /// + /// Gets the list of permissions for the content item + /// + /// + /// + public IEnumerable GetPermissionsForEntity(IContent content) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateContentRepository(uow)) + { + return repository.GetPermissionsForEntity(content.Id); } } @@ -157,12 +176,20 @@ namespace Umbraco.Core.Services var contentType = FindContentTypeByAlias(contentTypeAlias); var content = new Content(name, parentId, contentType); + //NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found + // out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now. if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) { content.WasCancelled = true; return content; } + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + { + content.WasCancelled = true; + return content; + } + var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { @@ -172,6 +199,8 @@ namespace Umbraco.Core.Services uow.Commit(); } + Saved.RaiseEvent(new SaveEventArgs(content, false), this); + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); @@ -197,12 +226,20 @@ namespace Umbraco.Core.Services var contentType = FindContentTypeByAlias(contentTypeAlias); var content = new Content(name, parent, contentType); + //NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found + // out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now. if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parent), this)) { content.WasCancelled = true; return content; } + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + { + content.WasCancelled = true; + return content; + } + var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { @@ -212,6 +249,8 @@ namespace Umbraco.Core.Services uow.Commit(); } + Saved.RaiseEvent(new SaveEventArgs(content, false), this); + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); Audit.Add(AuditTypes.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); @@ -346,9 +385,9 @@ namespace Umbraco.Core.Services /// /// to retrieve ancestors for /// An Enumerable list of objects - public IEnumerable GetAncestors(IContent content) - { - var ids = content.Path.Split(',').Where(x => x != "-1" && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray(); + public IEnumerable GetAncestors(IContent content) + { + var ids = content.Path.Split(',').Where(x => x != "-1" && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray(); if (ids.Any() == false) return new List(); @@ -356,9 +395,9 @@ namespace Umbraco.Core.Services { return repository.GetAll(ids); } - } + } - /// + /// /// Gets a collection of objects by Parent Id /// /// Id of the Parent to retrieve Children from @@ -442,7 +481,7 @@ namespace Umbraco.Core.Services return GetById(content.ParentId); } - /// + /// /// Gets the published version of an item /// /// Id of the to retrieve version from @@ -599,7 +638,7 @@ namespace Umbraco.Core.Services { LogHelper.Error("An error occurred executing RePublishAll", ex); return false; - } + } } /// @@ -618,7 +657,7 @@ namespace Umbraco.Core.Services catch (Exception ex) { LogHelper.Error("An error occurred executing RePublishAll", ex); - } + } } /// @@ -627,10 +666,10 @@ namespace Umbraco.Core.Services /// The to publish /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - [Obsolete("Use PublishWithStatus instead, that method will provide more detailed information on the outcome")] public bool Publish(IContent content, int userId = 0) { var result = SaveAndPublishDo(content, userId); + LogHelper.Info("Call was made to ContentService.Publish, use PublishWithStatus instead since that method will provide more detailed information on the outcome"); return result.Success; } @@ -1055,7 +1094,7 @@ namespace Umbraco.Core.Services } //Ensure that Path and Level is updated on children - var children = GetChildren(content.Id); + var children = GetChildren(content.Id).ToArray(); if (children.Any()) { foreach (var child in children) @@ -1219,6 +1258,7 @@ namespace Umbraco.Core.Services } } + /// /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. /// @@ -1357,7 +1397,7 @@ namespace Umbraco.Core.Services if (raiseEvents) Saved.RaiseEvent(new SaveEventArgs(items, false), this); - if(shouldBePublished.Any()) + if (shouldBePublished.Any()) _publishingStrategy.PublishingFinalized(shouldBePublished, false); Audit.Add(AuditTypes.Sort, "Sorting content performed by user", userId, 0); @@ -1367,30 +1407,6 @@ namespace Umbraco.Core.Services #region Internal Methods - ///// - ///// Internal method that Publishes a single object for legacy purposes. - ///// - ///// The to publish - ///// Optional Id of the User issueing the publishing - ///// True if publishing succeeded, otherwise False - //internal Attempt PublishInternal(IContent content, int userId = 0) - //{ - // return SaveAndPublishDo(content, userId); - //} - - ///// - ///// Internal method that Publishes a object and all its children for legacy purposes. - ///// - ///// The to publish along with its children - ///// Optional Id of the User issueing the publishing - ///// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published - ///// True if publishing succeeded, otherwise False - //internal IEnumerable> PublishWithChildrenInternal( - // IContent content, int userId = 0, bool includeUnpublished = false) - //{ - // return PublishWithChildrenDo(content, userId, includeUnpublished); - //} - /// /// Gets a collection of descendants by the first Parent. /// @@ -1450,49 +1466,63 @@ namespace Umbraco.Core.Services { if (contentTypeIds.Any() == false) { - //Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!) - uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN - (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml - INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)"); + //Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!) (based on inner join of cmsDocument) + var subQuery = new Sql() + .Select("DISTINCT cmsContentXml.nodeId") + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId); + + var deleteSql = SqlSyntaxContext.SqlSyntaxProvider.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery); + uow.Database.Execute(deleteSql); } else { foreach (var id in contentTypeIds) { //first we'll clear out the data from the cmsContentXml table for this type - uow.Database.Execute(@"delete from cmsContentXml where nodeId in -(select cmsDocument.nodeId from cmsDocument - inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId - where published = 1 and contentType = @contentTypeId)", new { contentTypeId = id }); + var id1 = id; + var subQuery = new Sql() + .Select("cmsDocument.nodeId") + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .Where(dto => dto.Published) + .Where(dto => dto.ContentTypeId == id1); + + var deleteSql = SqlSyntaxContext.SqlSyntaxProvider.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery); + uow.Database.Execute(deleteSql); } } //bulk insert it into the database uow.Database.BulkInsertRecords(xmlItems, tr); + + tr.Complete(); } Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1); - } + } } - /// - /// Publishes a object and all its children - /// - /// The to publish along with its children - /// Optional Id of the User issueing the publishing - /// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published - /// - /// A list of publish statues. If the parent document is not valid or cannot be published because it's parent(s) is not published - /// then the list will only contain one status item, otherwise it will contain status items for it and all of it's descendants that - /// are to be published. - /// + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional Id of the User issueing the publishing + /// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published + /// + /// A list of publish statues. If the parent document is not valid or cannot be published because it's parent(s) is not published + /// then the list will only contain one status item, otherwise it will contain status items for it and all of it's descendants that + /// are to be published. + /// private IEnumerable> PublishWithChildrenDo( IContent content, int userId = 0, bool includeUnpublished = false) { - if (content == null) throw new ArgumentNullException("content"); + if (content == null) throw new ArgumentNullException("content"); - using (new WriteLock(Locker)) - { + using (new WriteLock(Locker)) + { var result = new List>(); //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published @@ -1516,7 +1546,7 @@ namespace Umbraco.Core.Services Attempt.Fail( new PublishStatus(content, PublishStatusType.FailedContentInvalid) { - InvalidProperties = ((ContentBase) content).LastInvalidProperties + InvalidProperties = ((ContentBase)content).LastInvalidProperties })); return result; } @@ -1567,7 +1597,7 @@ namespace Umbraco.Core.Services return publishedOutcome; - } + } } /// @@ -1613,7 +1643,7 @@ namespace Umbraco.Core.Services /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. /// True if publishing succeeded, otherwise False - private Attempt SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true) + private Attempt SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true) { if (raiseEvents) { @@ -1697,7 +1727,7 @@ namespace Umbraco.Core.Services Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId, content.Id); return Attempt.If(publishStatus.StatusType == PublishStatusType.Success, publishStatus); - } + } } /// @@ -1940,7 +1970,7 @@ namespace Umbraco.Core.Services /// /// Occurs before Create /// - [Obsolete("This event should not be used, it was originally created for backwards compatibility for the legacy API. To modify a new document before it is saved use the Saving event")] + [Obsolete("Use the Created event instead, the Creating and Created events both offer the same functionality, Creating event has been deprecated.")] public static event TypedEventHandler> Creating; /// @@ -1950,7 +1980,6 @@ namespace Umbraco.Core.Services /// Please note that the Content object has been created, but might not have been saved /// so it does not have an identity yet (meaning no Id has been set). /// - [Obsolete("This event should not be used, it was originally created for backwards compatibility for the legacy API. To modify a new document before it is saved use the Saving event")] public static event TypedEventHandler> Created; /// diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index a2f27f58a2..8dbd3a2d98 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; @@ -21,7 +20,7 @@ namespace Umbraco.Core.Services /// /// Represents the ContentType Service, which is an easy access to operations involving /// - public class ContentTypeService : IContentTypeService + public class ContentTypeService : ContentTypeServiceBase, IContentTypeService { private readonly RepositoryFactory _repositoryFactory; private readonly IContentService _contentService; @@ -147,48 +146,7 @@ namespace Umbraco.Core.Services private void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes) { - var toUpdate = new List(); - - foreach (var contentType in contentTypes) - { - //we need to determine if we need to refresh the xml content in the database. This is to be done when: - // - the item is not new (already existed in the db) AND - // - a content type changes it's alias OR - // - if a content type has it's property removed OR - // - if a content type has a property whose alias has changed - //here we need to check if the alias of the content type changed or if one of the properties was removed. - var dirty = contentType as IRememberBeingDirty; - if (dirty == null) continue; - - //check if any property types have changed their aliases (and not new property types) - var hasAnyPropertiesChangedAlias = contentType.PropertyTypes.Any(propType => - { - var dirtyProperty = propType as IRememberBeingDirty; - if (dirtyProperty == null) return false; - return dirtyProperty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new' - && dirtyProperty.WasPropertyDirty("Alias"); //alias has changed - }); - - if (dirty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new' - && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") || hasAnyPropertiesChangedAlias)) - { - //If the alias was changed then we only need to update the xml structures for content of the current content type. - //If a property was deleted or a property alias was changed then we need to update the xml structures for any - // content of the current content type and any of the content type's child content types. - if (dirty.WasPropertyDirty("Alias") - && dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") == false && hasAnyPropertiesChangedAlias == false) - { - //if only the alias changed then only update the current content type - toUpdate.Add(contentType); - } - else - { - //if a property was deleted or alias changed, then update all content of the current content type - // and all of it's desscendant doc types. - toUpdate.AddRange(contentType.DescendantsAndSelf()); - } - } - } + var toUpdate = GetContentTypesForXmlUpdates(contentTypes).ToArray(); if (toUpdate.Any()) { diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs new file mode 100644 index 0000000000..b5115d61c2 --- /dev/null +++ b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Services +{ + public class ContentTypeServiceBase + { + /// + /// This is called after an content type is saved and is used to update the content xml structures in the database + /// if they are required to be updated. + /// + /// + internal IEnumerable GetContentTypesForXmlUpdates(params IContentTypeBase[] contentTypes) + { + + var toUpdate = new List(); + + foreach (var contentType in contentTypes) + { + //we need to determine if we need to refresh the xml content in the database. This is to be done when: + // - the item is not new (already existed in the db) AND + // - a content type changes it's alias OR + // - if a content type has it's property removed OR + // - if a content type has a property whose alias has changed + //here we need to check if the alias of the content type changed or if one of the properties was removed. + var dirty = contentType as IRememberBeingDirty; + if (dirty == null) continue; + + //check if any property types have changed their aliases (and not new property types) + var hasAnyPropertiesChangedAlias = contentType.PropertyTypes.Any(propType => + { + var dirtyProperty = propType as IRememberBeingDirty; + if (dirtyProperty == null) return false; + return dirtyProperty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new' + && dirtyProperty.WasPropertyDirty("Alias"); //alias has changed + }); + + if (dirty.WasPropertyDirty("HasIdentity") == false //ensure it's not 'new' + && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") || hasAnyPropertiesChangedAlias)) + { + //If the alias was changed then we only need to update the xml structures for content of the current content type. + //If a property was deleted or a property alias was changed then we need to update the xml structures for any + // content of the current content type and any of the content type's child content types. + if (dirty.WasPropertyDirty("Alias") + && dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") == false && hasAnyPropertiesChangedAlias == false) + { + //if only the alias changed then only update the current content type + toUpdate.Add(contentType); + } + else + { + //if a property was deleted or alias changed, then update all content of the current content type + // and all of it's desscendant doc types. + toUpdate.AddRange(contentType.DescendantsAndSelf()); + } + } + } + + return toUpdate; + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 0ea5250ea8..5eb516aa4b 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -138,7 +138,7 @@ namespace Umbraco.Core.Services using (var uow = _uowProvider.GetUnitOfWork()) { var dtos = uow.Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = id }); - var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList(); + var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList(); return PreValueConverter.ConvertToPreValuesCollection(list); } @@ -226,17 +226,17 @@ namespace Umbraco.Core.Services { using (var uow = _uowProvider.GetUnitOfWork()) { - var sortOrderObj = - uow.Database.ExecuteScalar( - "SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id }); - int sortOrder; - if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false) - { - sortOrder = 1; - } - using (var transaction = uow.Database.GetTransaction()) { + var sortOrderObj = + uow.Database.ExecuteScalar( + "SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id }); + int sortOrder; + if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false) + { + sortOrder = 1; + } + foreach (var value in values) { var dto = new DataTypePreValueDto { DataTypeNodeId = id, Value = value, SortOrder = sortOrder }; diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index af50d81d2b..3837603d88 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -302,12 +302,12 @@ namespace Umbraco.Core.Services /// An enumerable list of objects public virtual IEnumerable GetAll() where T : IUmbracoEntity { - var typeFullName = typeof (T).FullName; + var typeFullName = typeof(T).FullName; Mandate.That(_supportedObjectTypes.ContainsKey(typeFullName), () => - { - throw new NotSupportedException - ("The passed in type is not supported"); - }); + { + throw new NotSupportedException + ("The passed in type is not supported"); + }); var objectType = _supportedObjectTypes[typeFullName].Item1; return GetAll(objectType); @@ -430,7 +430,7 @@ namespace Umbraco.Core.Services if (attribute == null) throw new NullReferenceException("The passed in UmbracoObjectType does not contain an UmbracoObjectTypeAttribute, which is used to retrieve the Type."); - if(attribute.ModelType == null) + if (attribute.ModelType == null) throw new NullReferenceException("The passed in UmbracoObjectType does not contain a Type definition"); return attribute.ModelType; diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index e2f946ffee..848097f6f0 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Auditing; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services @@ -13,24 +14,24 @@ namespace Umbraco.Core.Services /// public class FileService : IFileService { - private readonly RepositoryFactory _repositoryFactory; + private readonly RepositoryFactory _repositoryFactory; private readonly IUnitOfWorkProvider _fileUowProvider; private readonly IDatabaseUnitOfWorkProvider _dataUowProvider; public FileService() : this(new RepositoryFactory()) - {} + { } public FileService(RepositoryFactory repositoryFactory) - : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory) + : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory) { } - public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory) + public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory) { - _repositoryFactory = repositoryFactory; - _fileUowProvider = fileProvider; - _dataUowProvider = dataProvider; + _repositoryFactory = repositoryFactory; + _fileUowProvider = fileProvider; + _dataUowProvider = dataProvider; } /// @@ -65,19 +66,19 @@ namespace Umbraco.Core.Services /// public void SaveStylesheet(Stylesheet stylesheet, int userId = 0) { - if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) - return; - - var uow = _fileUowProvider.GetUnitOfWork(); + if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) + return; + + var uow = _fileUowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateStylesheetRepository(uow, _dataUowProvider.GetUnitOfWork())) - { - repository.AddOrUpdate(stylesheet); - uow.Commit(); + { + repository.AddOrUpdate(stylesheet); + uow.Commit(); - SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); - } + SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + } - Audit.Add(AuditTypes.Save, string.Format("Save Stylesheet performed by user"), userId, -1); + Audit.Add(AuditTypes.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } /// @@ -92,15 +93,15 @@ namespace Umbraco.Core.Services { var stylesheet = repository.Get(name); - if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) - return; + if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) + return; - repository.Delete(stylesheet); - uow.Commit(); + repository.Delete(stylesheet); + uow.Commit(); - DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); + DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); - Audit.Add(AuditTypes.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1); + Audit.Add(AuditTypes.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1); } } @@ -146,19 +147,19 @@ namespace Umbraco.Core.Services /// public void SaveScript(Script script, int userId = 0) { - if (SavingScript.IsRaisedEventCancelled(new SaveEventArgs + + + diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js new file mode 100644 index 0000000000..640abec1b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -0,0 +1,337 @@ +angular.module("umbraco.install").factory('installerService', function($rootScope, $q, $timeout, $http, $location, $log){ + + var _status = { + index: 0, + current: undefined, + steps: undefined, + loading: true, + progress: "100%" + }; + + var factTimer = undefined; + var _installerModel = { + installId: undefined, + instructions: { + } + }; + + //add to umbraco installer facts here + var facts = ['Umbraco helped millions of people watch a man jump from the edge of space', + 'Over 250.000 websites are currently powered by Umbraco', + 'On an average day, more than 1.000 people download Umbraco', + 'umbraco.tv is the premier source of Umbraco video tutorials to get you started', + 'You can find the world\'s friendliest CMS community at our.umbraco.org', + 'You can become a certified Umbraco developer by attending one of the official courses', + 'Umbraco works really well on tablets', + 'You have 100% control over your markup and design when crafting a website in Umbraco', + 'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company', + "There's a pretty big chance, you've visited a website powered by Umbraco today", + "'Umbraco-spotting' is the game of spotting big brands running Umbraco", + "At least 2 people have the Umbraco logo tattooed on them", + "'Umbraco' is the danish name for an allen key", + "Umbraco has been around since 2005, that's a looong time in IT", + "More than 400 people from all over the world meet each year in Denmark in June for our annual conference CodeGarden", + "While you install Umbraco someone else on the other side of the planet probably does it too", + "You can extend Umbraco without modifying the source code and using either JavaScript or C#" + ]; + + /** + Returns the description for the step at a given index based on the order of the serverOrder of steps + Since they don't execute on the server in the order that they are displayed in the UI. + */ + function getDescriptionForStepAtIndex(steps, index) { + var sorted = _.sortBy(steps, "serverOrder"); + if (sorted[index]) { + return sorted[index].description; + } + return null; + } + /* Returns the description for the given step name */ + function getDescriptionForStepName(steps, name) { + var found = _.find(steps, function(i) { + return i.name == name; + }); + return (found) ? found.description : null; + } + + //calculates the offset of the progressbar on the installaer + function calculateProgress(steps, next) { + var sorted = _.sortBy(steps, "serverOrder"); + + var pct = "100%"; + for (var i = sorted.length - 1; i >= 0; i--) { + if(sorted[i].name == next){ + pct = Math.floor((i+1) / steps.length * 100) + "%"; + break; + } + } + return pct; + } + + //helpful defaults for the view loading + function resolveView(view){ + + if(view.indexOf(".html") < 0){ + view = view + ".html"; + } + if(view.indexOf("/") < 0){ + view = "views/install/" + view; + } + + return view; + } + + /** Have put this here because we are not referencing our other modules */ + function safeApply (scope, fn) { + if (scope.$$phase || scope.$root.$$phase) { + if (angular.isFunction(fn)) { + fn(); + } + } + else { + if (angular.isFunction(fn)) { + scope.$apply(fn); + } + else { + scope.$apply(); + } + } + } + + var service = { + + status : _status, + //loads the needed steps and sets the intial state + init : function(){ + service.status.loading = true; + if(!_status.all){ + service.getSteps().then(function(response){ + service.status.steps = response.data.steps; + service.status.index = 0; + _installerModel.installId = response.data.installId; + service.findNextStep(); + + $timeout(function(){ + service.status.loading = false; + service.status.configuring = true; + }, 2000); + }); + } + }, + + //loads available packages from our.umbraco.org + getPackages : function(){ + return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetPackages"); + }, + + getSteps : function(){ + return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup"); + }, + + gotoStep : function(index){ + var step = service.status.steps[index]; + step.view = resolveView(step.view); + + if(!step.model){ + step.model = {}; + } + + service.status.index = index; + service.status.current = step; + service.retrieveCurrentStep(); + }, + + gotoNamedStep : function(stepName){ + var step = _.find(service.status.steps, function(s, index){ + if (s.view && s.name === stepName) { + service.status.index = index; + return true; + } + return false; + }); + + step.view = resolveView(step.view); + if(!step.model){ + step.model = {}; + } + service.retrieveCurrentStep(); + service.status.current = step; + }, + + /** + Finds the next step containing a view. If one is found it stores it as the current step + and retreives the step information and returns it, otherwise returns null . + */ + findNextStep : function(){ + var step = _.find(service.status.steps, function(s, index){ + if(s.view && index >= service.status.index){ + service.status.index = index; + return true; + } + return false; + }); + + if (step) { + if (step.view.indexOf(".html") < 0) { + step.view = step.view + ".html"; + } + + if (step.view.indexOf("/") < 0) { + step.view = "views/install/" + step.view; + } + + if (!step.model) { + step.model = {}; + } + + service.status.current = step; + service.retrieveCurrentStep(); + + //returns the next found step + return step; + } + else { + //there are no more steps found containing a view so return null + return null; + } + }, + + storeCurrentStep : function(){ + _installerModel.instructions[service.status.current.name] = service.status.current.model; + }, + + retrieveCurrentStep : function(){ + if(_installerModel.instructions[service.status.current.name]){ + service.status.current.model = _installerModel.instructions[service.status.current.name]; + } + }, + + /** Moves the installer forward to the next view, if there are not more views than the installation will commence */ + forward : function(){ + service.storeCurrentStep(); + service.status.index++; + var found = service.findNextStep(); + if (!found) { + //no more steps were found so start the installation process + service.install(); + } + }, + + backwards : function(){ + service.storeCurrentStep(); + service.gotoStep(service.status.index--); + }, + + install : function(){ + service.storeCurrentStep(); + service.switchToFeedback(); + + service.status.feedback = getDescriptionForStepAtIndex(service.status.steps, 0); + service.status.progress = 0; + + function processInstallStep() { + + $http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostPerformInstall", _installerModel) + .success(function(data, status, headers, config) { + if (!data.complete) { + + //progress feedback + service.status.progress = calculateProgress(service.status.steps, data.nextStep); + + if (data.view) { + //set the current view and model to whatever the process returns, the view is responsible for retriggering install(); + var v = resolveView(data.view); + service.status.current = { view: v, model: data.model }; + + //turn off loading bar and feedback + service.switchToConfiguration(); + } + else { + var desc = getDescriptionForStepName(service.status.steps, data.nextStep); + if (desc) { + service.status.feedback = desc; + } + processInstallStep(); + } + } + else { + service.complete(); + } + }).error(function(data, status, headers, config) { + //need to handle 500's separately, this will happen if something goes wrong outside + // of the installer (like app startup events or something) and these will get returned as text/html + // not as json. If this happens we can't actually load in external views since they will YSOD as well! + // so we need to display this in our own internal way + + if (status >= 500 && status < 600) { + service.status.current = { view: "ysod", model: null }; + var ysod = data; + //we need to manually write the html to the iframe - the html contains full html markup + $timeout(function () { + document.getElementById('ysod').contentDocument.write(ysod); + }, 500); + } + else { + //this is where we handle installer error + var v = data.view ? resolveView(data.view) : resolveView("error"); + var model = data.model ? data.model : data; + service.status.current = { view: v, model: model }; + } + + service.switchToConfiguration(); + + }); + } + processInstallStep(); + }, + + randomFact: function () { + safeApply($rootScope, function() { + service.status.fact = facts[_.random(facts.length - 1)]; + }); + }, + + switchToFeedback : function(){ + service.status.current = undefined; + service.status.loading = true; + service.status.configuring = false; + + //initial fact + service.randomFact(); + + //timed facts + factTimer = window.setInterval(function(){ + service.randomFact(); + },6000); + }, + + switchToConfiguration : function(){ + service.status.loading = false; + service.status.configuring = true; + service.status.feedback = undefined; + service.status.fact = undefined; + + if(factTimer){ + clearInterval(factTimer); + } + }, + + complete : function(){ + + service.status.progress = "100%"; + service.status.done = true; + service.status.feedback = "Redirecting you to Umbraco, please wait"; + service.status.loading = false; + + if(factTimer){ + clearInterval(factTimer); + } + + $timeout(function(){ + window.location.href = Umbraco.Sys.ServerVariables.umbracoBaseUrl; + }, 1500); + } + }; + + return service; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/continueinstall.html b/src/Umbraco.Web.UI.Client/src/installer/steps/continueinstall.html new file mode 100644 index 0000000000..28fa1d3fff --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/continueinstall.html @@ -0,0 +1,13 @@ +
+

Continue Umbraco Installation

+

+ You see this screen because your Umbraco installation did not complete correctly. +

+

+ Simply click continue below to be guided through the rest of the installation process. +

+ +

+ +

+
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/database.controller.js b/src/Umbraco.Web.UI.Client/src/installer/steps/database.controller.js new file mode 100644 index 0000000000..5ac118e1f4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.controller.js @@ -0,0 +1,35 @@ +angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseController", function($scope, $http, installerService){ + + $scope.checking = false; + $scope.dbs = [ + {name: 'Microsoft SQL Server Compact (SQL CE)', id: 0}, + {name: 'Microsoft SQL Server', id: 1}, + {name: 'MySQL', id: 2}, + {name: 'Custom connection string', id: -1}]; + + if(installerService.status.current.model.dbType === undefined){ + installerService.status.current.model.dbType = 0; + } + + $scope.validateAndForward = function(){ + if(!$scope.checking && this.myForm.$valid){ + $scope.checking = true; + var model = installerService.status.current.model; + + $http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostValidateDatabaseConnection", + model).then(function(response){ + + if(response.data === "true"){ + installerService.forward(); + }else{ + $scope.invalidDbDns = true; + } + + $scope.checking = false; + }, function(){ + $scope.invalidDbDns = true; + $scope.checking = false; + }); + } + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html new file mode 100644 index 0000000000..e78754ff22 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html @@ -0,0 +1,121 @@ +
+ +

Configure your database

+

+ Enter connection and authentication details for the database you want to install Umbraco on +

+ +
+
+ What type of database do you use? + +
+ +
+
+ +
+

Great!, no need to configure anything then, you simply click the continue button below to continue to the next step

+
+ + + +
+ + What is the exact connectionstring we should use? +
+ +
+ + + Enter a valid database connection string. +
+
+
+ +
+
+ Where do we find your database? +
+
+ +
+ + Enter server domain or IP +
+
+
+ +
+
+ +
+ + Enter the name of the database +
+
+
+
+ + +
+ What credentials are used to access the database? +
+
+ +
+ + Enter the database user name +
+
+
+ +
+
+ +
+ + Enter the database password +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ + + + Validating your database connection... + + + + Could not connect to database + +
+
+
+ + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/error.html b/src/Umbraco.Web.UI.Client/src/installer/steps/error.html new file mode 100644 index 0000000000..4fb5823afa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/error.html @@ -0,0 +1,9 @@ +
+

Error during installation

+ +

{{installer.current.model.message}}

+ +

See the log for full details (logs can typically be found in the App_Data\Logs folder).

+ + +
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html new file mode 100644 index 0000000000..3ced6ed678 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html @@ -0,0 +1,26 @@ +
+

Your permission settings are not ready for umbraco

+

+ In order to run umbraco, you'll need to update your permission settings. + Detailed information about the correct file & folder permissions for Umbraco can be found + here. +

+

+ The following report list the permissions that are currently failing. Once the permissions are fixed press the 'Go back' button to restart the installation. +

+ +
    +
  • +

    {{category}}

    +
      +
    • + {{item}} +
    • +
    +
  • +
+ +

+ +

+
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.controller.js b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.controller.js new file mode 100644 index 0000000000..81958ddd55 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.controller.js @@ -0,0 +1,12 @@ +angular.module("umbraco.install").controller("Umbraco.Installer.PackagesController", function ($scope, installerService) { + + installerService.getPackages().then(function (response) { + $scope.packages = response.data; + }); + + $scope.setPackageAndContinue = function (pckId) { + installerService.status.current.model = pckId; + installerService.forward(); + }; + +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html new file mode 100644 index 0000000000..7347023c9d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/starterkit.html @@ -0,0 +1,20 @@ +
+

Install a starter website

+ +

+ Installing a starter website helps you learn how Umbraco works, and gives you a solid + and simple foundation to build on top of. +

+ + + + + No thanks, I do not want to install a starter website + +
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/upgrade.html b/src/Umbraco.Web.UI.Client/src/installer/steps/upgrade.html new file mode 100644 index 0000000000..374772451b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/upgrade.html @@ -0,0 +1,13 @@ +
+

Upgrading Umbraco

+

+ Welcome to the Umbraco installer. You see this screen because your Umbraco installation needs a quick upgrade of its database and files, which will ensure your website is kept as fast, secure and up to date as possible. +

+

+ Simply click continue below to be guided through the rest of the upgrade +

+ +

+ +

+
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js new file mode 100644 index 0000000000..297db6ac4a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js @@ -0,0 +1,26 @@ +angular.module("umbraco.install").controller("Umbraco.Install.UserController", function($scope, installerService) { + + $scope.passwordPattern = /.*/; + $scope.installer.current.model.subscribeToNewsLetter = true; + + if ($scope.installer.current.model.minNonAlphaNumericLength > 0) { + var exp = ""; + for (var i = 0; i < $scope.installer.current.model.minNonAlphaNumericLength; i++) { + exp += ".*[\\W].*"; + } + //replace duplicates + exp = exp.replace(".*.*", ".*"); + $scope.passwordPattern = new RegExp(exp); + } + + $scope.validateAndInstall = function(){ + installerService.install(); + }; + + $scope.validateAndForward = function(){ + if(this.myForm.$valid){ + installerService.forward(); + } + }; + +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html new file mode 100644 index 0000000000..a4f6c32c13 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html @@ -0,0 +1,69 @@ +
+

Install Umbraco 7

+ +

Enter your name, email and password to install Umbraco 7 with its default settings, alternatively you can customize your installation

+ +
+ +
+
+
+
+ +
+ +
+
+ +
+ +
+ + Your email will be used as your login +
+
+ +
+ +
+ + + At least {{installer.current.model.minCharLength}} characters long + + + At least {{installer.current.model.minNonAlphaNumericLength}} symbol{{installer.current.model.minNonAlphaNumericLength > 1 ? 's' : ''}} + +
+
+ + +
+
+ +
+
+ +
+
+ + + + Customize +
+
+ +
+
+
+
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html b/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html new file mode 100644 index 0000000000..df1e58d737 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html @@ -0,0 +1,22 @@ +
+

Major version upgrade from {{installer.current.model.currentVersion}} to {{installer.current.model.newVersion}}

+

There were {{installer.current.model.errors.length}} issues detected

+

+ The following compatibility issues were found. If you continue all non-compatible property editors will be converted to a Readonly/Label. + You will be able to change the property editor to a compatible type manually by editing the data type after installation. +

+

+ Otherwise if you choose not to proceed you will need to fix the errors listed below. + Refer to v{{installer.current.model.newVersion}} upgrade instructions for full details. +

+ +
    +
  • + {{item}} +
  • +
+ +

+ +

+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/alerts.less b/src/Umbraco.Web.UI.Client/src/less/alerts.less index 51961aefdc..98fb3842a4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/alerts.less +++ b/src/Umbraco.Web.UI.Client/src/less/alerts.less @@ -62,6 +62,16 @@ color: @infoText; } +.alert-form { + background-color: @grayLighter; + border: 1px solid @gray; + color: @gray; +} + +.alert-form h4 { + color: @gray; +} + // Block alerts // ------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/less/animations.less b/src/Umbraco.Web.UI.Client/src/less/animations.less index 73592f74d0..20cb52b109 100644 --- a/src/Umbraco.Web.UI.Client/src/less/animations.less +++ b/src/Umbraco.Web.UI.Client/src/less/animations.less @@ -1,21 +1,23 @@ // Animations // ------------------------- -.fade-hide, .fade-show { +.fade-hide, .fade-show , .fade-leave, .fade-enter { -webkit-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; -moz-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; -o-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; } -.fade-hide { +.fade-enter { opacity: 1; } + .fade-hide.fade-hide-active { opacity: 0; } -.fade-show { +.fade-leave { opacity: 0; } + .fade-show.fade-show-active { opacity: 1; } @@ -63,6 +65,7 @@ display: block; position: relative; } + @-webkit-keyframes leave { to { opacity: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 30a89fe63d..acb14f6a91 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -48,6 +48,7 @@ @import "../../lib/bootstrap/less/modals.less"; @import "../../lib/bootstrap/less/tooltip.less"; @import "../../lib/bootstrap/less/popovers.less"; +@import "tipmenu.less"; // Components: Misc @import "../../lib/bootstrap/less/thumbnails.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index 90189fc9db..d175541e17 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -72,6 +72,7 @@ } + // Button Sizes // -------------------------------------------------- @@ -134,6 +135,23 @@ input[type="button"] { } } +// Round button +.btn-round{ + font-size: 24px; + color: @gray; + background: @white; + + line-height: 32px; + text-align: center; + -moz-border-radius: 15px; + border-radius: 15px; + height: 32px; + width: 32px; + overflow: hidden; + text-decoration: none; + display: inline-block; + z-index: 6666; +} // Alternate buttons diff --git a/src/Umbraco.Web.UI.Client/src/less/grid.less b/src/Umbraco.Web.UI.Client/src/less/grid.less index c5dcdd2826..8a0022a558 100644 --- a/src/Umbraco.Web.UI.Client/src/less/grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/grid.less @@ -156,7 +156,7 @@ body { #speechbubble { z-index: 1000; position: absolute; - bottom: 20px; + bottom: 40px; left: 0; right: 0; border-bottom: none; diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less new file mode 100644 index 0000000000..298f4f4728 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -0,0 +1,291 @@ +// Core variables and mixins +@import "fonts.less"; // Loading fonts +@import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "mixins.less"; +@import "buttons.less"; +@import "forms.less"; + +// Grid system and page structure +@import "../../lib/bootstrap/less/scaffolding.less"; +@import "../../lib/bootstrap/less/grid.less"; +@import "../../lib/bootstrap/less/layouts.less"; + +@import "../../lib/bootstrap/less/thumbnails.less"; +@import "../../lib/bootstrap/less/media.less"; + + +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + display: none !important; +} + + +html { + background: url('../img/installer.jpg') no-repeat center center fixed; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + + font-family: @baseFontFamily; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @textColor; + + vertical-align: center; + text-align: center; +} + +#logo{ + position: absolute; + top: 20px; + left: 20px; + opacity: 0.8; + z-index: 777; +} + +#installer{ + margin: auto; + background: white; + width: 750px; + height: 600px; + text-align: left; + padding: 30px; + overflow:hidden; + z-index: 667; +} + +#overlay{ + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + background: @blackLight; + z-index: 666; +} + +.loading #overlay{ + opacity: 0.5; + display: block !important; +} + + +#fact{ + color: #fff; + text-shadow: 0px 0px 4px black; + font-size: 25px; + text-align: left; + line-height: 35px; + z-index: 667; + height: 600px; + width: 750px; +} + +#fact h2{ + font-size: 35px; + border-bottom: 1px solid white; + padding-bottom: 10px; + margin-bottom: 20px; + color: white; +} + +#fact a{color: white;} + +#feedback{ + color: #fff; + text-shadow: 0px 0px 4px black; + font-size: 14px; + text-align: center; + line-height: 20px; + z-index: 667; + bottom: 20px; + right: 0; + left: 0; + height: 25px; + position: absolute; +} + + +h1{ + border-bottom: 1px solid @grayLighter; + padding-bottom: 10px; + color: @gray; +} + +.error h1, .error .message, span.error{ color: @red;} + +legend{font-size: 14px; font-weight: bold} + +input.ng-dirty.ng-invalid{border-color: #b94a48; color: #b94a48;} +.disabled{ + opacity: 0.6; +} + +.controls{ + text-align: left +} + +.controls small{display: block; color: @gray;} + +.absolute-center { + margin: auto; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; +} + +.fade-hide, .fade-show { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; +} +.fade-hide { + opacity:1; +} +.fade-hide.fade-hide-active { + opacity:0; +} +.fade-show { + opacity:0; +} +.fade-show.fade-show-active { + opacity:1; +} + + +.umb-loader{ +background-color: white; +margin-top:0; +margin-left:-100%; +-moz-animation-name:bounce_loadingProgressG; +-moz-animation-duration:1s; +-moz-animation-iteration-count:infinite; +-moz-animation-timing-function:linear; +-webkit-animation-name:bounce_loadingProgressG; +-webkit-animation-duration:1s; +-webkit-animation-iteration-count:infinite; +-webkit-animation-timing-function:linear; +-ms-animation-name:bounce_loadingProgressG; +-ms-animation-duration:1s; +-ms-animation-iteration-count:infinite; +-ms-animation-timing-function:linear; +-o-animation-name:bounce_loadingProgressG; +-o-animation-duration:1s; +-o-animation-iteration-count:infinite; +-o-animationtiming-function:linear; +animation-name:bounce_loadingProgressG; +animation-duration:1s; +animation-iteration-count:infinite; +animation-timing-function:linear; +width:100%; +height: 5px; +} + + + @-moz-keyframes bounce_loadingProgressG{ + 0%{ + margin-left:-100%; + } + + 100%{ + margin-left:100%; + } + + } + + @-webkit-keyframes bounce_loadingProgressG{ + 0%{ + margin-left:-100%; + } + + 100%{ + margin-left:100%; + } + + } + + @-ms-keyframes bounce_loadingProgressG{ + 0%{ + margin-left:-100%; + } + + 100%{ + margin-left:100%; + } + + } + + @-o-keyframes bounce_loadingProgressG{ + 0%{ + margin-left:-100%; + } + + 100%{ + margin-left:100%; + } + + } + + @keyframes bounce_loadingProgressG{ + 0%{ + margin-left:-100%; + } + 100%{ + margin-left:100%; + } +} + +//loader defaults +.umb-loader-container, .umb-loader-done{ + height: 3px; + position: absolute; + bottom: 0; + left: 0; + overflow: hidden; + width: 0%; + z-index: 777; +} + +.umb-loader-done{ + right: 0%; + background: white; +} + + +.permissions-report { + overflow:auto; + height:320px; + margin:0; + display:block; + padding:0; +} +.permissions-report > li { + list-style:none; +} +.permissions-report h4 { + margin:7px; +} + +.upgrade-report { + overflow:auto; + height:280px; + display:block; +} + +select { + width:320px; +} + +#ysod { + height:500px; + width:100%; + overflow:auto; + border:none; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/listview.less b/src/Umbraco.Web.UI.Client/src/less/listview.less index 22535fc4d3..ce1a73fe6d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/listview.less +++ b/src/Umbraco.Web.UI.Client/src/less/listview.less @@ -87,7 +87,8 @@ } .umb-listview .selected i.icon, .umb-listview tr:hover i.icon{display: none} -.umb-listview .selected input[type="checkbox"], .umb-listview tr:hover input[type="checkbox"]{display: inline-block;} +.umb-listview .selected input[type="checkbox"], +.umb-listview tr:hover input[type="checkbox"]{display: inline-block !important;} .umb-listview .inactive{color: @grayLight;} .umb-listview .selected td{font-weight: bold;} @@ -120,7 +121,7 @@ font-weight: 400 } -.table-striped tbody > tr:nth-child(even) > td, .table-striped tbody > tr:nth-child(even) > th { +.umb-listview .table-striped tbody > tr:nth-child(even) > td, .umb-listview .table-striped tbody > tr:nth-child(even) > th { background-color: @grayLighter } @@ -167,21 +168,23 @@ /* TEMP */ -.table-striped tbody td { +.umb-listview .table-striped tbody td { position: relative } -.table-striped tbody input[type="checkbox"] { - display: block; - position: absolute; - top: 10px; - left:10px; + +.umb-listview .table-striped thead input[type="checkbox"] { + margin-left: 7px; +} + +.umb-listview .table-striped tbody input[type="checkbox"] { + display: none; + margin-left: 7px; z-index: 5; } -.table-striped tbody i { +.umb-listview .table-striped tbody i { display: block; - position: absolute; top: 10px; left:6px; padding: 0 0 0 4px; @@ -191,7 +194,7 @@ height: 20px; } -.table-striped tbody > tr:nth-child(even) > td i, .table-striped tbody > tr:nth-child(even) > th i{ +.umb-listview .table-striped tbody > tr:nth-child(even) > td i, .umb-listview .table-striped tbody > tr:nth-child(even) > th i{ background-color: @grayLighter } diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 2fea609f67..b6887ee257 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -163,6 +163,10 @@ h5{ background: @black } +.umb-version { + color: #bbbbbb; position: absolute; bottom: 5px; right: 20px; +} + /* DASHBOARD */ .dashboardHideLink { display: none; diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index a502001975..18eefc402a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -33,6 +33,10 @@ bottom: 90px; } +.umb-panel.editor-breadcrumb .umb-panel-body, .umb-panel.editor-breadcrumb .umb-bottom-bar,{ + bottom: 40px !Important; +} + .umb-panel-header .umb-headline, .umb-panel-header h1 { font-size: 18px; border: none; @@ -155,17 +159,74 @@ bottom: 0px; left: 100px; right: 20px; - }; +}; @media (min-width: 1101px) { .umb-bottom-bar {left: 460px;} } .umb-tab-buttons{padding-left: 240px;} -.umb-tab-pane.with-buttons{padding-bottom: 80px} +.umb-tab-pane{padding-bottom: 90px} .tab-content{overflow: visible; } +.umb-panel-footer-nav{ + position: absolute; + bottom: 0px; + height: 30px; + left: 0px; + right: 0px; + background: @grayLighter; + border-top: @grayLight 1px solid; + display: block; + margin: 0; +} + +.umb-panel-footer-nav li a { + border-radius: 0; + display: block; + float: left; + height: 30px; + background: @grayLighter; + text-align: center; + padding: 8px 0px 8px 30px; + position: relative; + margin: 0 1px 0 0; + text-decoration: none; + color: @gray; + font-size: 11px; +} + +.umb-panel-footer-nav li a:after { + content: ""; + border-top: 16px solid transparent; + border-bottom: 16px solid transparent; + border-left: 16px solid @grayLighter; + position: absolute; right: -16px; top: 0; + z-index: 1; +} + +.umb-panel-footer-nav li a:before { + content: ""; + border-top: 16px solid transparent; + border-bottom: 16px solid transparent; + border-left: 16px solid @grayLight; + position: absolute; left: 0; top: 0; +} + +.umb-panel-footer-nav li:first-child a{ + padding-left: 20px; +} + +.umb-panel-footer-nav li:first-child a:before { + display: none; +} + +.umb-panel-footer-nav li:last-child a:after { + display: none; +} + + // Utility classes diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 6ffde599a6..036dafec08 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -93,6 +93,19 @@ ul.color-picker li a { border:none; } +/* pre-value editor */ + +.control-group.color-picker-preval pre { + display: inline; + margin-right: 20px; + margin-left: 10px; +} + +.control-group.color-picker-preval label { + border:solid white 1px; + padding:6px; +} + // // Media picker @@ -130,6 +143,8 @@ ul.color-picker li a { text-decoration: none; } + + .umb-thumbnails{ position: relative; } @@ -158,7 +173,8 @@ ul.color-picker li a { background: white; margin: 5px; position: relative; - text-align: center + text-align: center; + vertical-align: top; } .umb-sortable-thumbnails li:hover a{ @@ -171,6 +187,12 @@ ul.color-picker li a { max-height:100%; margin:auto; display:block; + background-image: url(../img/checkered-background.png); +} + +.umb-sortable-thumbnails li img.noScale{ + max-width: none !important; + max-height: none !important; } .umb-sortable-thumbnails .icon-holder .icon{ @@ -184,6 +206,92 @@ ul.color-picker li a { } +// +// Cropper +// ------------------------------------------------- + +.umb-cropper{ + position: relative; + padding: 10px; +} + +.umb-cropper img, .umb-cropper-gravity img{ + position: absolute; + top: 0; + left: 0; + } + + .umb-cropper .overlay, .umb-cropper-gravity .overlay { + top: 0; + left: 0; + cursor: move; + z-index: 6001; +} + +.umb-cropper .viewport{ + overflow: hidden; + position: relative; + margin: auto; + } + +.umb-cropper-gravity .viewport{ + overflow: hidden; + position: relative; + width: 400px; + height: 300px +} + + +.umb-cropper .viewport:after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 5999; + -moz-opacity: .75; + opacity: .75; + filter: alpha(opacity=7); + -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); +} + +.umb-cropper-gravity .overlay{ + width: 14px; + height: 14px; + text-align: center; + border-radius: 20px; + background: @blue; + border: 3px solid white; + opacity: 0.8; +} + +.umb-cropper-gravity .overlay i{ + font-size: 26px; + line-height: 26px; + opacity: 0.8 !important; +} + +.umb-cropper .crop-container{ + text-align: center; +} + +.umb-cropper .crop-slider{ + vertical-align: middle; + padding: 10px 50px 10px 50px; + border-top: 1px solid @grayLighter; + margin-top: 10px; +} + +.umb-cropper .crop-slider i{color: @gray;} +.umb-cropper .crop-slider input{ + margin-top: 7px; + width: 320px; +} + + // // folder-browser // -------------------------------------------------- @@ -196,6 +304,10 @@ ul.color-picker li a { line-height: 120px } +.umb-folderbrowser .selector-overlay{ + display: none !important; +} + .umb-upload-drop-zone .info, .umb-upload-button-big{ display: block; padding: 20px; @@ -221,6 +333,7 @@ ul.color-picker li a { left: 0; bottom: 0; height: 100%; + width: 100%; } @@ -234,6 +347,8 @@ ul.color-picker li a { position: relative; } + + .umb-photo-folder .picrow div, .umb-photo-preview{ margin: 0px; padding: 0px; @@ -262,7 +377,7 @@ ul.color-picker li a { vertical-align: center; font-size: 12px; background: @grayLighter; - color: @grayLight; + color: @black; text-decoration: none; } @@ -283,11 +398,11 @@ ul.color-picker li a { .umb-photo-folder .umb-non-thumbnail i{ color: @grayLight; - font-size: 70px; - line-height: 80px; + font-size: 50px; + line-height: 60px; display: block; margin: auto; - padding-top: 25%; + padding-top: 20px; } .umb-photo-folder .selected{ diff --git a/src/Umbraco.Web.UI.Client/src/less/tipmenu.less b/src/Umbraco.Web.UI.Client/src/less/tipmenu.less new file mode 100644 index 0000000000..e92b1019e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/tipmenu.less @@ -0,0 +1,20 @@ +.tipmenu .tooltip{ + position:absolute; + left:0; + right:0; + margin-left:auto; + margin-right:auto; +} + +.tipmenu:hover .tooltip{ + opacity: 1; +} + +.tipmenu:hover .tooltip a{ + color: white; +} + +.tipmenu .tooltip-inner{ + margin-left:auto; + margin-right:auto; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less index cb7f031b27..e843499d2e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/tree.less @@ -61,7 +61,7 @@ margin: 0px; min-width: 100%; width: 100%; - display: table + //display: table } .umb-tree ul.collapsed { @@ -82,6 +82,7 @@ vertical-align: middle; padding: 5px 0 5px 0; position: relative; + overflow: hidden; } .umb-tree a.noSpr { @@ -123,7 +124,7 @@ .umb-tree .icon { vertical-align: middle; margin: 1px 13px 1px 0px; - color: #414141; + color: #21201C; font-size: 15px; } .umb-tree i.noSpr { diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index f43e5065f3..f5155e9939 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -31,9 +31,19 @@ @pink: #c3325f; @purple: #7a43b6; + .red{color: @red;} .blue{color: @blue;} + +//icon colors for tree icons +.color-red, .color-red i{color: #d90416 !important;} +.color-blue, .color-blue i{color: #04bfbf !important;} +.color-orange, .color-orange i{color: #d9631e !important;} +.color-green, .color-green i{color: #04BF67 !important;} +.color-yellow, .color-yellow i{color: #f28729 !important;} + + // Scaffolding // ------------------------- @bodyBackground: @white; diff --git a/src/Umbraco.Web.UI.Client/src/loader.js b/src/Umbraco.Web.UI.Client/src/loader.js index ffb61665a5..9695ae0b59 100644 --- a/src/Umbraco.Web.UI.Client/src/loader.js +++ b/src/Umbraco.Web.UI.Client/src/loader.js @@ -1,16 +1,24 @@ +//global no-cache filter, this is enabled when we're in debug mode +//in live mode we use client dependency and don't turn this thing on +yepnope.addFilter(function (resourceObj) { + var url = resourceObj.url; + if(url.indexOf("lib/") === 0 || url.indexOf("js/umbraco.") === 0){ + return resourceObj; + } + + resourceObj.url = resourceObj.url + "?umb__rnd=" + (new Date).getTime(); + return resourceObj; +}); + + yepnope({ load: [ 'lib/jquery/jquery-2.0.3.min.js', /* the jquery ui elements we need */ - /* NOTE: I've opted not to use the full lib, just the parts we need to save on DL*/ - 'lib/jquery/jquery.ui.core.min.js', - 'lib/jquery/jquery.ui.widget.min.js', - - 'lib/jquery/jquery.ui.mouse.min.js', - 'lib/jquery/jquery.ui.sortable.min.js', - + 'lib/jquery/jquery-ui-1.10.3.custom.min.js', + /* 1.1.5 */ 'lib/angular/1.1.5/angular.min.js', 'lib/angular/1.1.5/angular-cookies.min.js', diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.controller.js new file mode 100644 index 0000000000..57f1edd8ac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.controller.js @@ -0,0 +1,15 @@ +function ContentEditDialogController($scope) { + //setup scope vars + $scope.model = {}; + $scope.model.defaultButton = null; + $scope.model.subButtons = []; + + var dialogOptions = $scope.$parent.dialogOptions; + if(dialogOptions.entity){ + $scope.model.entity = dialogOptions.entity; + $scope.loaded = true; + } +} + +angular.module("umbraco") + .controller("Umbraco.Dialogs.Content.EditController", ContentEditDialogController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html new file mode 100644 index 0000000000..fb35372353 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html @@ -0,0 +1,58 @@ +
+ +
+
+

{{model.entity.name}}

+
+ + + + + +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html index ca8a1e133f..ef759c8b06 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html @@ -1,7 +1,8 @@
-
-

Help

-
+
+

Help

+ Umbraco version {{version}} +
@@ -11,7 +12,7 @@
  • - + {{topic.title}} {{topic.description}} @@ -21,7 +22,7 @@
  • - {{version}} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js index 51e3bd228a..de71977ebe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.controller.js @@ -7,5 +7,13 @@ angular.module("umbraco") $scope.icons = icons; }); + $scope.submitClass = function(icon){ + if($scope.color) + { + $scope.submit(icon + " " + $scope.color); + }else{ + $scope.submit(icon); + } + }; } ); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html index f02a8923be..bcf7fe3247 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html @@ -1,25 +1,48 @@
    -
    - -
    -
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html index e0954ad64c..54d911fb6a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html @@ -20,7 +20,7 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js new file mode 100644 index 0000000000..abcdb0852f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -0,0 +1,137 @@ +//this controller simply tells the dialogs service to open a mediaPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') + .controller("Umbraco.PropertyEditors.ImageCropperController", + function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager) { + + var config = angular.copy($scope.model.config); + + //move previously saved value to the editor + if ($scope.model.value) { + //backwards compat with the old file upload (incase some-one swaps them..) + if (angular.isString($scope.model.value)) { + config.src = $scope.model.value; + $scope.model.value = config; + } else if ($scope.model.value.crops) { + //sync any config changes with the editor and drop outdated crops + _.each($scope.model.value.crops, function (saved) { + var configured = _.find(config.crops, function (item) { return item.alias === saved.alias }); + + if (configured && configured.height === saved.height && configured.width === saved.width) { + configured.coordinates = saved.coordinates; + } + }); + $scope.model.value.crops = config.crops; + + //restore focalpoint if missing + if (!$scope.model.value.focalPoint) { + $scope.model.value.focalPoint = { left: 0.5, top: 0.5 }; + } + } + + $scope.imageSrc = $scope.model.value.src; + } + + + //crop a specific crop + $scope.crop = function (crop) { + $scope.currentCrop = crop; + $scope.currentPoint = undefined; + }; + + //done cropping + $scope.done = function () { + $scope.currentCrop = undefined; + $scope.currentPoint = undefined; + }; + + //crop a specific crop + $scope.clear = function (crop) { + //clear current uploaded files + fileManager.setFiles($scope.model.alias, []); + + //clear the ui + $scope.imageSrc = undefined; + if ($scope.model.value) { + delete $scope.model.value; + } + }; + + //show previews + $scope.togglePreviews = function () { + if ($scope.showPreviews) { + $scope.showPreviews = false; + $scope.tempShowPreviews = false; + } else { + $scope.showPreviews = true; + } + }; + + //on image selected, update the cropper + $scope.$on("filesSelected", function (ev, args) { + $scope.model.value = config; + + if (args.files && args.files[0]) { + + fileManager.setFiles($scope.model.alias, args.files); + + var reader = new FileReader(); + reader.onload = function (e) { + + $scope.$apply(function () { + $scope.imageSrc = e.target.result; + }); + + }; + + reader.readAsDataURL(args.files[0]); + } + }); + + var unsubscribe = $scope.$on("formSubmitting", function () { + $scope.done(); + }); + + $scope.$on('$destroy', function () { + unsubscribe(); + }); + }) + .run(function (mediaHelper, umbRequestHelper) { + if (mediaHelper && mediaHelper.registerFileResolver) { + mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) { + if (property.value.src) { + + if (thumbnail === true) { + return property.value.src + "?width=600&mode=max"; + } + else { + return property.value.src; + } + + //this is a fallback in case the cropper has been asssigned a upload field + } + else if (angular.isString(property.value)) { + if (thumbnail) { + + if (mediaHelper.detectIfImageByExtension(property.value)) { + var thumbnailUrl = umbRequestHelper.getApiUrl( + "imagesApiBaseUrl", + "GetBigThumbnail", + [{ originalImagePath: property.value }]); + + return thumbnailUrl; + } + else { + return null; + } + + } + else { + return property.value; + } + } + + return null; + }); + } + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html new file mode 100644 index 0000000000..90a6f0981d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -0,0 +1,63 @@ +
    + + + + +

    Click to upload

    + +
    + + +
    +
    +
    + + + +
    + +
    + + + Reset + +
    +
    + +
    +
    + +
    + +
    + + + + Remove file +
    +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.controller.js new file mode 100644 index 0000000000..3a99c90a50 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.controller.js @@ -0,0 +1,44 @@ +angular.module("umbraco").controller("Umbraco.PrevalueEditors.CropSizesController", + function ($scope, $timeout) { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + $scope.remove = function (item, evt) { + evt.preventDefault(); + $scope.model.value = _.reject($scope.model.value, function (x) { + return x.alias === item.alias; + }); + }; + + $scope.edit = function (item, evt) { + evt.preventDefault(); + $scope.newItem = item; + }; + + $scope.cancel = function (evt) { + evt.preventDefault(); + $scope.newItem = null; + }; + + $scope.add = function (evt) { + evt.preventDefault(); + + if ($scope.newItem && $scope.newItem.alias && + angular.isNumber($scope.newItem.width) && angular.isNumber($scope.newItem.height) && + $scope.newItem.width > 0 && $scope.newItem.height > 0) { + + var exists = _.find($scope.model.value, function (item) { return $scope.newItem.alias === item.alias; }); + if (!exists) { + $scope.model.value.push($scope.newItem); + $scope.newItem = {}; + $scope.hasError = false; + return; + } + } + + //there was an error, do the highlight (will be set back by the directive) + $scope.hasError = true; + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html new file mode 100644 index 0000000000..69d54244e8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html @@ -0,0 +1,49 @@ +
    + + +
    +
      +
    • + + + + {{node.alias}} +
      {{node.width}}px × {{node.height}}px +
    • +
    +
    + +
    +

    Define crop

    +

    + Give the crop an alias and it's default width and height. +

    + +
    + + +
    + +
    + + + × + +
    + +
    + + Cancel +
    +
    + +
    + +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 786ec65279..01ed21d42f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -44,7 +44,7 @@ - - -
    + Name diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/macrocontainer/macrocontainer.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/macrocontainer/macrocontainer.controller.js index 2198c02478..06b4c52f35 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/macrocontainer/macrocontainer.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/macrocontainer/macrocontainer.controller.js @@ -27,8 +27,8 @@ angular.module('umbraco') function collectDetails(macro){ macro.details = ""; - if(macro.marcoParamsDictionary){ - angular.forEach((macro.marcoParamsDictionary), function(value, key){ + if(macro.macroParamsDictionary){ + angular.forEach((macro.macroParamsDictionary), function(value, key){ macro.details += key + ": " + value + " "; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js index 041cc7ffca..b65d3373b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js @@ -26,13 +26,9 @@ function ($scope, assetsService, dialogService, $log, imageHelper) { //subscribe to the image dialog clicks editor2.hooks.set("insertImageDialog", function (callback) { - dialogService.mediaPicker({ callback: function (data) { - $(data.selection).each(function (i, item) { - var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: item, scope: $scope }); - callback(imagePropVal); - }); - } + callback(data.url); + } }); return true; // tell the editor that we'll take care of getting the image url diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 23a0a4aefc..05951232f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -1,25 +1,40 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController", - function($rootScope, $scope, dialogService, mediaResource, imageHelper, $timeout) { + function($rootScope, $scope, dialogService, mediaResource, mediaHelper, $timeout) { //check the pre-values for multi-picker var multiPicker = $scope.model.config.multiPicker !== '0' ? true : false; + if (!$scope.model.config.startNodeId) + $scope.model.config.startNodeId = -1; + + function setupViewModel() { $scope.images = []; $scope.ids = []; if ($scope.model.value) { - $scope.ids = $scope.model.value.split(','); + var ids = $scope.model.value.split(','); - mediaResource.getByIds($scope.ids).then(function (medias) { + mediaResource.getByIds(ids).then(function (medias) { //img.media = media; _.each(medias, function (media, i) { - media.src = imageHelper.getImagePropertyValue({ imageModel: media }); - media.thumbnail = imageHelper.getThumbnailFromPath(media.src); - $scope.images.push(media); + + //only show non-trashed items + if(media.parentId >= -1){ + if(!media.thumbnail){ + media.thumbnail = mediaHelper.resolveFile(media, true); + } + + //media.src = mediaHelper.getImagePropertyValue({ imageModel: media }); + //media.thumbnail = mediaHelper.getThumbnailFromPath(media.src); + $scope.images.push(media); + $scope.ids.push(media.id); + } }); + + $scope.sync(); }); } } @@ -34,6 +49,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.add = function() { dialogService.mediaPicker({ + startNodeId: $scope.model.config.startNodeId, multiPicker: multiPicker, callback: function(data) { @@ -43,8 +59,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl } _.each(data, function(media, i) { - media.src = imageHelper.getImagePropertyValue({ imageModel: media }); - media.thumbnail = imageHelper.getThumbnailFromPath(media.src); + + if(!media.thumbnail){ + media.thumbnail = mediaHelper.resolveFile(media, true); + } $scope.images.push(media); $scope.ids.push(media.id); @@ -57,7 +75,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.sortableOptions = { update: function(e, ui) { - var r = []; + var r = []; + //TODO: Instead of doing this with a half second delay would be better to use a watch like we do in the + // content picker. THen we don't have to worry about setting ids, render models, models, we just set one and let the + // watch do all the rest. $timeout(function(){ angular.forEach($scope.images, function(value, key){ r.push(value.id); @@ -66,7 +87,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.ids = r; $scope.sync(); }, 500, false); - } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index 91ab18dd07..3857d073ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -2,9 +2,9 @@
    • - + meh? - + {{image.name}} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index afbb248e5d..21afc87c2b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -33,52 +33,45 @@ angular.module('umbraco') $scope.remove =function(index){ $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); }; $scope.add =function(item){ if($scope.ids.indexOf(item.id) < 0){ item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item.id); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - $scope.model.value = trim($scope.ids.join(), ","); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); } }; $scope.clear = function() { - $scope.model.value = ""; $scope.renderModel = []; - $scope.ids = []; }; - - $scope.sortableOptions = { - update: function(e, ui) { - var r = []; - angular.forEach($scope.renderModel, function(value, key){ - r.push(value.id); - }); - - $scope.ids = r; - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - + //We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required + // because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable + // occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs. + // In their source code there is no event so we need to just subscribe to our model changes here. + //This also makes it easier to manage models, we update one and the rest will just work. + $scope.$watch(function () { + //return the joined Ids as a string to watch + return _.map($scope.renderModel, function (i) { + return i.id; + }).join(); + }, function (newVal) { + $scope.ids = _.map($scope.renderModel, function (i) { + return i.id; + }); + $scope.model.value = trim($scope.ids.join(), ","); + }); $scope.$on("formSubmitting", function (ev, args) { $scope.model.value = trim($scope.ids.join(), ","); }); - - function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); return str.replace(rgxtrim, ''); } - function populate(data){ if(angular.isArray(data)){ _.each(data, function (item, i) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index 473c5694e8..cde28b9e7c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -1,8 +1,7 @@
      -
      • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js new file mode 100644 index 0000000000..5fe8a87d56 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js @@ -0,0 +1,22 @@ +angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController", + function($scope) { + + if (angular.isObject($scope.model.config.items)) { + + //now we need to format the items in the dictionary because we always want to have an array + var newItems = []; + var vals = _.values($scope.model.config.items); + var keys = _.keys($scope.model.config.items); + for (var i = 0; i < vals.length; i++) { + newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value }); + } + + //ensure the items are sorted by the provided sort order + newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); }); + + //re-assign + $scope.model.config.items = newItems; + + } + + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html index c526f13b09..cdb5a37ac8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html @@ -1,12 +1,12 @@ -
        +
          -
        • +
        • -
        +
      \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js index d15284628c..0c75afc4d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js @@ -39,13 +39,11 @@ $scope.model.value[idx].edit = false; }; - $scope.delete = function (idx) { - - $scope.model.value.splice(idx, 1); - + $scope.delete = function (idx) { + $scope.model.value.splice(idx, 1); }; - $scope.add = function () { + $scope.add = function ($event) { if ($scope.newCaption == "") { $scope.hasError = true; } else { @@ -68,7 +66,7 @@ this.internal = $scope.newInternal; this.edit = false; this.isInternal = true; - this.iternalName = $scope.newInternalName; + this.internalName = $scope.newInternalName; this.type = "internal"; this.title = $scope.newCaption; }; @@ -81,6 +79,7 @@ $scope.newInternalName = ''; } + $event.preventDefault(); }; $scope.switch = function ($event) { @@ -88,8 +87,11 @@ $event.preventDefault(); }; - $scope.switchLinkType = function ($event,link) { - link.isInternal = !link.isInternal; + $scope.switchLinkType = function ($event, link) { + link.isInternal = !link.isInternal; + link.type = link.isInternal ? "internal" : "external"; + if (!link.isInternal) + link.link = $scope.newLink; $event.preventDefault(); }; @@ -138,10 +140,10 @@ if ($scope.currentEditLink != null) { $scope.currentEditLink.internal = data.id; $scope.currentEditLink.internalName = data.name; + $scope.currentEditLink.link = data.id; } else { $scope.newInternal = data.id; $scope.newInternalName = data.name; } } - }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html index 4320a2f1b8..43ee95b5cd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html @@ -19,7 +19,7 @@
    @@ -55,7 +55,6 @@
    @@ -73,10 +72,9 @@
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 1754584a9d..daf058f313 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -34,7 +34,7 @@ angular.module("umbraco") //queue rules loading angular.forEach(editorConfig.stylesheets, function(val, key){ - stylesheets.push("/css/" + val + ".css?" + new Date().getTime()); + stylesheets.push("../css/" + val + ".css?" + new Date().getTime()); await.push(stylesheetResource.getRulesByName(val).then(function(rules) { angular.forEach(rules, function(rule) { @@ -64,6 +64,118 @@ angular.module("umbraco") //wait for queue to end $q.all(await).then(function () { + + //create a baseline Config to exten upon + var baseLineConfigObj = { + mode: "exact", + skin: "umbraco", + plugins: plugins, + valid_elements: validElements, + invalid_elements: invalidElements, + extended_valid_elements: extendedValidElements, + menubar: false, + statusbar: false, + height: editorConfig.dimensions.height, + width: editorConfig.dimensions.width, + toolbar: toolbar, + content_css: stylesheets.join(','), + relative_urls: false, + style_formats: styleFormats + }; + + + if(tinyMceConfig.customConfig){ + angular.extend(baseLineConfigObj, tinyMceConfig.customConfig); + } + + //set all the things that user configs should not be able to override + baseLineConfigObj.elements = $scope.model.alias + "_rte"; + baseLineConfigObj.setup = function (editor) { + + //set the reference + tinyMceEditor = editor; + + //enable browser based spell checking + editor.on('init', function(e) { + editor.getBody().setAttribute('spellcheck', true); + }); + + //We need to listen on multiple things here because of the nature of tinymce, it doesn't + //fire events when you think! + //The change event doesn't fire when content changes, only when cursor points are changed and undo points + //are created. the blur event doesn't fire if you insert content into the editor with a button and then + //press save. + //We have a couple of options, one is to do a set timeout and check for isDirty on the editor, or we can + //listen to both change and blur and also on our own 'saving' event. I think this will be best because a + //timer might end up using unwanted cpu and we'd still have to listen to our saving event in case they clicked + //save before the timeout elapsed. + + //this doesnt actually work... + //editor.on('change', function (e) { + // angularHelper.safeApply($scope, function () { + // $scope.model.value = editor.getContent(); + // }); + //}); + + //when we leave the editor (maybe) + editor.on('blur', function (e) { + editor.save(); + angularHelper.safeApply($scope, function () { + $scope.model.value = editor.getContent(); + }); + }); + + //when buttons modify content + editor.on('ExecCommand', function (e) { + editor.save(); + angularHelper.safeApply($scope, function () { + $scope.model.value = editor.getContent(); + }); + }); + + // Update model on keypress + editor.on('KeyUp', function (e) { + editor.save(); + angularHelper.safeApply($scope, function () { + $scope.model.value = editor.getContent(); + }); + }); + + // Update model on change, i.e. copy/pasted text, plugins altering content + editor.on('SetContent', function (e) { + if(!e.initial){ + editor.save(); + angularHelper.safeApply($scope, function () { + $scope.model.value = editor.getContent(); + }); + } + }); + + + editor.on('ObjectResized', function(e) { + var qs = "?width=" + e.width + "px&height=" + e.height + "px"; + var srcAttr = $(e.target).attr("src"); + var path = srcAttr.split("?")[0]; + $(e.target).attr("data-mce-src", path + qs); + }); + + + //Create the insert media plugin + tinyMceService.createMediaPicker(editor, $scope); + + //Create the embedded plugin + tinyMceService.createInsertEmbeddedMedia(editor, $scope); + + //Create the insert link plugin + tinyMceService.createLinkPicker(editor, $scope); + + //Create the insert macro plugin + tinyMceService.createInsertMacro(editor, $scope); + }; + + + + /** Loads in the editor */ function loadTinyMce() { @@ -71,63 +183,13 @@ angular.module("umbraco") //the elements needed $timeout(function () { tinymce.DOM.events.domLoaded = true; - tinymce.init({ - mode: "exact", - elements: $scope.model.alias + "_rte", - skin: "umbraco", - plugins: plugins, - valid_elements: validElements, - invalid_elements: invalidElements, - extended_valid_elements: extendedValidElements, - menubar: false, - statusbar: false, - height: editorConfig.dimensions.height, - width: editorConfig.dimensions.width, - toolbar: toolbar, - content_css: stylesheets.join(','), - relative_urls: false, - style_formats: styleFormats, - setup: function (editor) { - - //set the reference - tinyMceEditor = editor; - - //We need to listen on multiple things here because of the nature of tinymce, it doesn't - //fire events when you think! - //The change event doesn't fire when content changes, only when cursor points are changed and undo points - //are created. the blur event doesn't fire if you insert content into the editor with a button and then - //press save. - //We have a couple of options, one is to do a set timeout and check for isDirty on the editor, or we can - //listen to both change and blur and also on our own 'saving' event. I think this will be best because a - //timer might end up using unwanted cpu and we'd still have to listen to our saving event in case they clicked - //save before the timeout elapsed. - editor.on('change', function (e) { - angularHelper.safeApply($scope, function () { - $scope.model.value = editor.getContent(); - }); - }); - editor.on('blur', function (e) { - angularHelper.safeApply($scope, function () { - $scope.model.value = editor.getContent(); - }); - }); - - //Create the insert media plugin - tinyMceService.createMediaPicker(editor, $scope); - - //Create the embedded plugin - tinyMceService.createInsertEmbeddedMedia(editor, $scope); - - //Create the insert link plugin - tinyMceService.createLinkPicker(editor, $scope); - - //Create the insert macro plugin - tinyMceService.createInsertMacro(editor, $scope); - } - }); - }, 200); + tinymce.init(baseLineConfigObj); + }, 200, false); } + + + loadTinyMce(); //here we declare a special method which will be called whenever the value has changed from the server @@ -142,7 +204,6 @@ angular.module("umbraco") //listen for formSubmitting event (the result is callback used to remove the event subscription) var unsubscribe = $scope.$on("formSubmitting", function () { - //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer // we do parse it out on the server side but would be nice to do that on the client side before as well. $scope.model.value = tinyMceEditor.getContent(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js index 097907871e..212a350ced 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js @@ -2,7 +2,6 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", function ($scope, $timeout, $log, tinyMceService, stylesheetResource) { var cfg = tinyMceService.defaultPrevalues(); - if($scope.model.value){ if(angular.isString($scope.model.value)){ $scope.model.value = cfg; @@ -11,18 +10,28 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", $scope.model.value = cfg; } + if (!$scope.model.value.stylesheets) { + $scope.model.value.stylesheets = []; + } + if (!$scope.model.value.toolbar) { + $scope.model.value.toolbar = []; + } + tinyMceService.configuration().then(function(config){ $scope.tinyMceConfig = config; }); - + stylesheetResource.getAll().then(function(stylesheets){ $scope.stylesheets = stylesheets; }); $scope.selected = function(cmd, alias, lookup){ - cmd.selected = lookup.indexOf(alias) >= 0; - return cmd.selected; + if (lookup && angular.isArray(lookup)) { + cmd.selected = lookup.indexOf(alias) >= 0; + return cmd.selected; + } + return false; }; $scope.selectCommand = function(command){ @@ -35,7 +44,8 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", } }; - $scope.selectStylesheet = function(css){ + $scope.selectStylesheet = function (css) { + var index = $scope.model.value.stylesheets.indexOf(css.name); if(css.selected && index === -1){ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html index a990641150..8989bea69b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -8,7 +8,7 @@ ng-change="selectCommand(cmd)" /> - + {{cmd.frontEndCommand}} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index aacfd75895..48a7dbd611 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -90,7 +90,7 @@ //tell the assetsService to load the bootstrap slider //libs from the plugin folder assetsService - .loadJs("lib/slider/bootstrap-slider.js") + .loadJs("lib/slider/js/bootstrap-slider.js") .then(function () { createSlider(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html index 4319f375f0..7df7e9fded 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html @@ -2,7 +2,7 @@ + ng-required="model.validation.mandatory" /> Required diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/content/edit-content-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/content/edit-content-controller.spec.js index 5fe4354222..e30a92bbe9 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/content/edit-content-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/content/edit-content-controller.spec.js @@ -5,7 +5,7 @@ describe('edit content controller tests', function () { beforeEach(module('umbraco')); //inject the contentMocks service - beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, contentMocks, mocksUtils) { + beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, contentMocks, entityMocks, mocksUtils) { //for these tests we don't want any authorization to occur mocksUtils.disableAuth(); @@ -16,6 +16,7 @@ describe('edit content controller tests', function () { //have the contentMocks register its expect urls on the httpbackend //see /mocks/content.mocks.js for how its setup contentMocks.register(); + entityMocks.register(); //this controller requires an angular form controller applied to it scope.contentForm = angularHelper.getNullForm("contentForm"); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js index 3b453a7930..0d2b1a7bd9 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js @@ -5,8 +5,7 @@ describe('edit media controller tests', function () { beforeEach(module('umbraco')); //inject the contentMocks service - beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, mediaMocks, mocksUtils) { - + beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, mediaMocks, entityMocks, mocksUtils) { //for these tests we don't want any authorization to occur mocksUtils.disableAuth(); @@ -16,6 +15,7 @@ describe('edit media controller tests', function () { //have the contentMocks register its expect urls on the httpbackend //see /mocks/content.mocks.js for how its setup mediaMocks.register(); + entityMocks.register(); //this controller requires an angular form controller applied to it scope.contentForm = angularHelper.getNullForm("contentForm"); @@ -47,7 +47,7 @@ describe('edit media controller tests', function () { }); it('it should have a tabs collection', function () { - expect(scope.content.tabs.length).toBe(5); + expect(scope.content.tabs.length).toBe(1); }); it('it should have a properties collection on each tab', function () { diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index 2ffd204d24..0e030b67ba 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -16,9 +16,15 @@ describe('Content picker controller tests', function () { alias: "property", value:"1233,1231,23121", label: "My content picker", - description: "desc" + description: "desc", + config: {} }; + //this controller requires an angular form controller applied to it + scope.contentPickerForm = angularHelper.getNullForm("contentPickerForm"); + scope.contentPickerForm.minCount = angularHelper.getNullForm("minCount"); + scope.contentPickerForm.maxCount = angularHelper.getNullForm("maxCount"); + //have the contentMocks register its expect urls on the httpbackend //see /mocks/content.mocks.js for how its setup entityMocks.register(); @@ -60,7 +66,7 @@ describe('Content picker controller tests', function () { it("Removing an item should update renderModel, ids and model.value", function(){ scope.remove(1); - + scope.$apply(); expect(scope.renderModel.length).toBe(2); expect(scope.ids.length).toBe(2); expect(scope.model.value).toBe("1233,23121"); @@ -69,7 +75,7 @@ describe('Content picker controller tests', function () { it("Adding an item should update renderModel, ids and model.value", function(){ scope.add({name: "meh", id: 666, icon: "woop"}); - + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); @@ -77,12 +83,14 @@ describe('Content picker controller tests', function () { it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ - scope.add({name: "meh", id: 666, icon: "woop"}); + scope.add({ name: "meh", id: 666, icon: "woop" }); + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); - scope.add({name: "meh 2", id: 666, icon: "woop 2"}); + scope.add({ name: "meh 2", id: 666, icon: "woop 2" }); + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js index f0c82f6b6b..4055e2d181 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js @@ -23,7 +23,7 @@ describe('Drop down controller tests', function () { expect(scope.model.config.items).toBeDefined(); expect(scope.model.config.multiple).toBeDefined(); }); - + it("should convert simple array to dictionary", function () { scope.model = { @@ -37,12 +37,13 @@ describe('Drop down controller tests', function () { $routeParams: routeParams }); - expect(scope.model.config.items["value0"]).toBe("value0"); - expect(scope.model.config.items["value1"]).toBe("value1"); - expect(scope.model.config.items["value2"]).toBe("value2"); - + //this should be the expected format based on the changes made to the sortable prevalues + expect(scope.model.config.items[0].value).toBe("value0"); + expect(scope.model.config.items[1].value).toBe("value1"); + expect(scope.model.config.items[2].value).toBe("value2"); }); + it("should allow an existing valid dictionary", function () { scope.model = { diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js index 470b7b59f8..535d134b4b 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js @@ -11,17 +11,34 @@ describe('macro service tests', function () { it('can parse syntax for macros', function () { - var result = macroService.parseMacroSyntax(""); + var result = macroService.parseMacroSyntax(""); + + console.log(result.macroParamsDictionary.test); expect(result).not.toBeNull(); expect(result.macroAlias).toBe("Map"); - expect(result.marcoParamsDictionary.test1).not.toBeUndefined(); - expect(result.marcoParamsDictionary.test1).toBe("asdf"); - expect(result.marcoParamsDictionary.test2).not.toBeUndefined(); - expect(result.marcoParamsDictionary.test2).toBe("hello"); + expect(result.macroParamsDictionary).not.toBeUndefined(); + + expect(result.macroParamsDictionary.test).not.toBeUndefined(); + expect(result.macroParamsDictionary.test).toBe("asdf"); + + expect(result.macroParamsDictionary.test2).not.toBeUndefined(); + expect(result.macroParamsDictionary.test2).toBe("hello"); }); + + it('can parse syntax for macros with aliases containing dots', function () { + + var result = macroService.parseMacroSyntax(""); + + expect(result).not.toBeNull(); + expect(result.macroAlias).toBe("Map.Test"); + expect(result.macroParamsDictionary.test).not.toBeUndefined(); + expect(result.macroParamsDictionary.test).toBe("asdf"); + expect(result.macroParamsDictionary.test2).not.toBeUndefined(); + expect(result.macroParamsDictionary.test2).toBe("hello"); + }); it('can parse syntax for macros with body', function () { @@ -29,10 +46,10 @@ describe('macro service tests', function () { expect(result).not.toBeNull(); expect(result.macroAlias).toBe("Map"); - expect(result.marcoParamsDictionary.test1).not.toBeUndefined(); - expect(result.marcoParamsDictionary.test1).toBe("asdf"); - expect(result.marcoParamsDictionary.test2).not.toBeUndefined(); - expect(result.marcoParamsDictionary.test2).toBe("hello"); + expect(result.macroParamsDictionary.test1).not.toBeUndefined(); + expect(result.macroParamsDictionary.test1).toBe("asdf"); + expect(result.macroParamsDictionary.test2).not.toBeUndefined(); + expect(result.macroParamsDictionary.test2).toBe("hello"); }); @@ -45,7 +62,7 @@ describe('macro service tests', function () { var syntax = macroService.generateMacroSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: { + macroParamsDictionary: { param1: "value1", param2: "value2", param3: "value3" @@ -61,7 +78,7 @@ describe('macro service tests', function () { var syntax = macroService.generateMacroSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: {} + macroParamsDictionary: {} }); expect(syntax). @@ -73,7 +90,7 @@ describe('macro service tests', function () { var syntax = macroService.generateWebFormsSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: { + macroParamsDictionary: { param1: "value1", param2: "value2", param3: "value3" @@ -89,7 +106,7 @@ describe('macro service tests', function () { var syntax = macroService.generateWebFormsSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: {} + macroParamsDictionary: {} }); expect(syntax). @@ -101,7 +118,7 @@ describe('macro service tests', function () { var syntax = macroService.generateMvcSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: { + macroParamsDictionary: { param1: "value1", param2: "value2", param3: "value3" @@ -118,7 +135,7 @@ describe('macro service tests', function () { var syntax = macroService.generateMvcSyntax({ macroAlias: "myMacro", - marcoParamsDictionary: {} + macroParamsDictionary: {} }); expect(syntax). diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/notifications.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/notifications.spec.js index d2ac8f914b..c26b40f0b1 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/notifications.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/notifications.spec.js @@ -22,8 +22,8 @@ describe('notification tests', function () { notifications.remove(0); expect(notifications.getCurrent().length).toEqual(3); - expect(notifications.getCurrent()[0].headline).toBe("error:"); - expect(notifications.getCurrent()[1].headline).toBe("warning:"); + expect(notifications.getCurrent()[0].headline).toBe("error: "); + expect(notifications.getCurrent()[1].headline).toBe("warning: "); expect(notifications.getCurrent()[2].headline).toBe("warning"); notifications.removeAll(); diff --git a/src/Umbraco.Web.UI/Properties/Settings.Designer.cs b/src/Umbraco.Web.UI/Properties/Settings.Designer.cs index 5fd974b641..962354c575 100644 --- a/src/Umbraco.Web.UI/Properties/Settings.Designer.cs +++ b/src/Umbraco.Web.UI/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34003 +// Runtime Version:4.0.30319.18408 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -22,5 +22,14 @@ namespace Umbraco.Web.UI.Properties { return defaultInstance; } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://our.umbraco.org/umbraco/webservices/api/repository.asmx")] + public string Umbraco_Web_UI_org_umbraco_our_Repository { + get { + return ((string)(this["Umbraco_Web_UI_org_umbraco_our_Repository"])); + } + } } } diff --git a/src/Umbraco.Web.UI/Properties/Settings.settings b/src/Umbraco.Web.UI/Properties/Settings.settings index 2bd17f050c..5df40e9fea 100644 --- a/src/Umbraco.Web.UI/Properties/Settings.settings +++ b/src/Umbraco.Web.UI/Properties/Settings.settings @@ -1,5 +1,9 @@  - + - + + + http://our.umbraco.org/umbraco/webservices/api/repository.asmx + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 69dd00ce1a..903542f7dd 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -115,23 +115,37 @@ False ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll - + False - ..\packages\ClientDependency.1.7.0.4\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll - + False ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll + + + False + ..\packages\ImageProcessor.1.8.7.0\lib\ImageProcessor.dll + + + False + ..\packages\ImageProcessor.Web.3.2.1.0\lib\net45\ImageProcessor.Web.dll False ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll + + ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.Extensions.dll + ..\packages\Microsoft.Web.Helpers.1.0.0\lib\Microsoft.Web.Helpers.dll @@ -147,13 +161,13 @@ False ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll - + False ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll - + False - ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll System @@ -174,6 +188,7 @@ + True @@ -293,10 +308,13 @@ Properties\SolutionInfo.cs - - default.aspx + + loadStarterKits.ascx ASPXCodeBehind + + loadStarterKits.ascx + noNodes.aspx ASPXCodeBehind @@ -305,103 +323,6 @@ noNodes.aspx ASPXCodeBehind - - default.aspx - - - InstallerRestService.aspx - ASPXCodeBehind - - - InstallerRestService.aspx - - - database.ascx - ASPXCodeBehind - - - database.ascx - - - defaultUser.ascx - ASPXCodeBehind - - - defaultUser.ascx - - - license.ascx - ASPXCodeBehind - - - license.ascx - - - renaming.ascx - ASPXCodeBehind - - - renaming.ascx - - - RenderingEngine.ascx - ASPXCodeBehind - - - RenderingEngine.ascx - - - loadStarterKits.ascx - ASPXCodeBehind - - - loadStarterKits.ascx - - - StarterKits.ascx - ASPXCodeBehind - - - StarterKits.ascx - - - ASPXCodeBehind - - - theend.ascx - ASPXCodeBehind - - - theend.ascx - - - UpgradeReport.ascx - ASPXCodeBehind - - - UpgradeReport.ascx - - - validatePermissions.ascx - ASPXCodeBehind - - - validatePermissions.ascx - - - welcome.ascx - ASPXCodeBehind - - - welcome.ascx - - - Title.ascx - ASPXCodeBehind - - - Title.ascx - True @@ -461,9 +382,16 @@ xslt.ascx ASPXCodeBehind + + User.ascx + ASPXCodeBehind + xslt.ascx + + User.ascx + ExamineManagement.ascx ASPXCodeBehind @@ -609,6 +537,8 @@ treeInit.aspx + + @@ -629,9 +559,9 @@ - + @@ -644,6 +574,7 @@ + 404handlers.config @@ -703,6 +634,7 @@ Designer + Designer @@ -714,7 +646,6 @@ UI.xml - @@ -754,6 +685,7 @@ + @@ -1371,10 +1303,19 @@ + + + + + + + + + @@ -1716,12 +1657,6 @@ - - - - - - @@ -1859,8 +1794,6 @@ - - @@ -2051,18 +1984,6 @@ - - UserControl - - - UserControl - - - UserControl - - - UserControl - Designer @@ -2117,13 +2038,12 @@ - - + - + Code @@ -2142,21 +2062,6 @@ - - - - - - - - - - - - - - - @@ -2599,6 +2504,7 @@ + @@ -2624,9 +2530,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" True True - 7000 + 7110 / - http://localhost:7000 + http://localhost:7110 False False @@ -2649,11 +2555,17 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" - + + + + + + + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index 735291e38c..eef8f5a911 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -2,31 +2,38 @@ @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc -@using umbraco.cms.businesslogic.member @using Umbraco.Web -@using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ - var profileModel = new ProfileModel(); + var profileModel = Members.GetCurrentMemberProfileModel(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); Html.RequiresJs("/umbraco_client/ui/jquery.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + + var success = TempData["ProfileUpdateSuccess"] != null; } -@if (Member.IsLoggedOn()) -{ - @Html.RenderJsHere() +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ +@Html.RenderJsHere() +@if (Members.IsLoggedIn() && profileModel != null) +{ + if (success) + { + @* This message will show if RedirectOnSucces is set to false (default) *@ +

    Profile updated

    + } + using (Html.BeginUmbracoForm("HandleUpdateProfile")) {
    Edit profile - @Html.ValidationSummary(true) + @Html.ValidationSummary("profileModel", true) @Html.LabelFor(m => profileModel.Name) @Html.TextBoxFor(m => profileModel.Name) @@ -41,14 +48,19 @@ @for (var i = 0; i < profileModel.MemberProperties.Count; i++) { @Html.LabelFor(m => profileModel.MemberProperties[i].Value, profileModel.MemberProperties[i].Name) + @* + By default this will render a textbox but if you want to change the editor template for this property you can + easily change it. For example, if you wanted to render a custom editor for this field called "MyEditor" you would + create a file at ~/Views/Shared/EditorTemplates/MyEditor.cshtml", then you will change the next line of code to + render your specific editor template like: + @Html.EditorFor(m => profileModel.MemberProperties[i].Value, "MyEditor") + *@ @Html.EditorFor(m => profileModel.MemberProperties[i].Value) @Html.HiddenFor(m => profileModel.MemberProperties[i].Alias)
    } - - @Html.HiddenFor(m => profileModel.MemberTypeAlias)
    - } + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml index 4ce0d13985..3dc013f2e8 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml @@ -6,7 +6,7 @@ and use it to point the macro at a specific media folder to display it's content a simple list. Macro Parameters To Create, for this macro to work: -Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent +Alias:mediaId Name:Media Folder ID Type:Single Media Picker *@ @if (Model.MacroParameters["mediaId"] != null) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml index d1fe543412..cbb190dde9 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml @@ -1,7 +1,7 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage @* === Macro Parameters To Create === - Show:True Alias:nodeId Name:Node ID Type:Content Picker + Alias:nodeId Name:Node ID Type:Content Picker *@ @if (Model.MacroParameters["startNodeID"] != null) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml index c9e79f7892..3a75914e09 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml @@ -1,7 +1,7 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage
      - @*OrderBy() takes the property to sort by and optionally order desc/asc *@ + @* OrderBy() takes the property to sort by and optionally order desc/asc *@ @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("CreateDate desc")) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml index 10ae17b364..3ea4460c38 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml @@ -1,6 +1,6 @@ @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
        - @*OrderBy() takes the property to sort by*@ + @* OrderBy() takes the property to sort by *@ @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("Name")) {
      • @page.Name
      • diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml index 6295e8b339..f224a782d1 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml @@ -1,18 +1,15 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage @* Macro parameter to be set on the macro - Show:True Alias:propertyAlias Name:Property Alias Type:Textstring + Alias:propertyAlias Name:Property Alias Type:Textbox *@ - @{ - @* Get the property alias we want to filter on from the macro parameter *@ var propertyAlias = Model.MacroParameters["propertyAlias"]; var selection = CurrentPage.Children.Where("Visible").OrderBy(propertyAlias); } -
          @foreach (var page in selection) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesWithDoctype.cshtml index eb0ccdb481..d5445c54e7 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesWithDoctype.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesWithDoctype.cshtml @@ -7,7 +7,6 @@ Settings section). *@ - @{ @*Build a query and return the visible items *@ var selection= CurrentPage.Textpages.Where("Visible"); @@ -18,7 +17,6 @@ *@ } - @*Determine if there are any nodes in the selection, then render list *@ @if(selection.Any()){ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml index 061cc3ae30..9048c2927d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml @@ -1,8 +1,8 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage @* -Macro Parameters To Create, for this macro to work: -Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent + Macro Parameters To Create, for this macro to work: + Alias:mediaId Name:Media Folder ID Type:Single Media Picker *@ @if (Model.MacroParameters["mediaId"] != null) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml index 73b891d448..6687efa474 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml @@ -16,6 +16,7 @@ Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); } +@* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ @Html.RenderJsHere() @using (Html.BeginUmbracoForm("HandleLogin")) @@ -23,7 +24,7 @@
          Login - @Html.ValidationSummary(true) + @Html.ValidationSummary("loginModel", true) @Html.LabelFor(m => loginModel.Username) @Html.TextBoxFor(m => loginModel.Username) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 7da6b8e8a3..b80c74347e 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -1,19 +1,30 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage +@using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web @using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ - var loginStatusModel = new LoginStatusModel(); + var loginStatusModel = Members.GetCurrentLoginStatus(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); Html.RequiresJs("/umbraco_client/ui/jquery.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + + var logoutModel = new PostRedirectModel(); + + @* + Here you can specify a redirect URL for after logging out, by default umbraco will simply + redirect to the current page. Example to redirect to the home page: + + logoutModel.RedirectUrl = "/"; + *@ } +@* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ @Html.RenderJsHere() @if (loginStatusModel.IsLoggedIn) @@ -26,5 +37,7 @@ Logout
          + + @Html.HiddenFor(m => logoutModel.RedirectUrl) } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml index f9e913d9c4..062e5c7171 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml @@ -10,10 +10,10 @@ @* Lists each selected value from the picker as a link *@
            - @foreach(var id in CurrentPage.PropertyWithPicker){ + @foreach(var id in CurrentPage.PropertyWithPicker.Split(',')){ @*For each link, get the node, and display its name and url*@ - var content = Umbraco.Content(id.InnerText); + var content = Umbraco.Content(id);
          • @content.Name
          • } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml index 6920d4bc06..d3312a1ac0 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml @@ -6,9 +6,8 @@ the css class "current". *@ - @{ - @*Get the root of the website *@ + @* Get the root of the website *@ var root = CurrentPage.AncestorOrSelf(1); } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index 014b33ac2e..988641d324 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -3,27 +3,30 @@ @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web -@using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ - var registerModel = new RegisterModel(); + @* + You can specify a custom member type alias in the constructor, the default is 'Member' + for example, to use 'Custom Member' you'd use this syntax: + + var registerModel = Members.CreateRegistrationModel("Custom Member"); + *@ + + var registerModel = Members.CreateRegistrationModel(); @* - Configurable here: - - registerModel.MemberTypeAlias - the default is "Member" - - registerModel.RedirectOnSucces - the default is false - - registerModel.RedirectUrl - what page do we go to if the registration was successful? - the default is "/" unless RedirectOnSuccess is set to false - + Configurable here: + + registerModel.RedirectUrl - Optional. What path to redirect to if registration is successful. + By default the member will be redirected to the current umbraco page + unless this is specified. + registerModel.UsernameIsEmail - the default is true if you want the username to be different from the email address, set this to true and add a new Username field in the form below - + @Html.LabelFor(m => registerModel.Username) @Html.TextBoxFor(m => registerModel.Username) @Html.ValidationMessageFor(m => registerModel.Username) @@ -38,19 +41,22 @@ var success = TempData["FormSuccess"] != null; } +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ +@Html.RenderJsHere() + @if (success) { - // This message will show if RedirectOnSucces is set to false (default) + @* This message will show if RedirectOnSucces is set to false (default) *@

            Registration succeeeded.

            -} -else -{ +} +else +{ using (Html.BeginUmbracoForm("HandleRegisterMember")) {
            Register Member - @Html.ValidationSummary(true) + @Html.ValidationSummary("registerModel", true) @Html.LabelFor(m => registerModel.Name) @Html.TextBoxFor(m => registerModel.Name) @@ -66,11 +72,19 @@ else @Html.PasswordFor(m => registerModel.Password) @Html.ValidationMessageFor(m => registerModel.Password)
            - - @if (registerModel.MemberProperties != null) { + + @if (registerModel.MemberProperties != null) + { for (var i = 0; i < registerModel.MemberProperties.Count; i++) { @Html.LabelFor(m => registerModel.MemberProperties[i].Value, registerModel.MemberProperties[i].Name) + @* + By default this will render a textbox but if you want to change the editor template for this property you can + easily change it. For example, if you wanted to render a custom editor for this field called "MyEditor" you would + create a file at ~/Views/Shared/EditorTemplates/MyEditor.cshtml", then you will change the next line of code to + render your specific editor template like: + @Html.EditorFor(m => profileModel.MemberProperties[i].Value, "MyEditor") + *@ @Html.EditorFor(m => registerModel.MemberProperties[i].Value) @Html.HiddenFor(m => registerModel.MemberProperties[i].Alias)
            @@ -78,13 +92,10 @@ else } @Html.HiddenFor(m => registerModel.MemberTypeAlias) - @Html.HiddenFor(m => registerModel.RedirectOnSucces) - @Html.HiddenFor(m => registerModel.RedirectUrl) + @Html.HiddenFor(m => registerModel.RedirectUrl) @Html.HiddenFor(m => registerModel.UsernameIsEmail) -
            - - @Html.RenderJsHere() + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml index 9cd2a415ef..7e1a6ca087 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml @@ -1,20 +1,20 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage -@*Render the sitemap by passing the root node to the traverse helper*@ +@* Render the sitemap by passing the root node to the traverse helper *@
            @Traverse(CurrentPage.AncestorOrSelf())
            -@*Helper method to travers through all descendants*@ +@* Helper method to travers through all descendants *@ @helper Traverse(dynamic node) { @* Update the level to reflect how deep you want the sitemap to go *@ var maxLevelForSitemap = 4; - @*Select visible children *@ + @* Select visible children *@ var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap); - @*If any items are returned, render a list *@ + @* If any items are returned, render a list *@ if (items.Any()) {
              @@ -23,7 +23,7 @@
            • @item.Name - @*Run the traverse helper again *@ + @* Run the traverse helper again *@ @Traverse(item)
            • } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml deleted file mode 100644 index 4c79cfef3d..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage -@* - This snippet makes a breadcrumb of parents using an unordred html list. - - How it works: - - It uses the Ancestors() method to get all parents and then generates links so the visitor get go back - - Finally it outputs the name of the current page (without a link) -*@ - -@if (CurrentPage.Ancestors().Any()) -{ - -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml deleted file mode 100644 index ef7bd8a7eb..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using umbraco.cms.businesslogic.member -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var profileModel = new ProfileModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@if (Member.IsLoggedOn()) -{ - @Html.RenderJsHere() - - using (Html.BeginUmbracoForm("HandleUpdateProfile")) - { -
              - Edit profile - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => profileModel.Name) - @Html.TextBoxFor(m => profileModel.Name) - @Html.ValidationMessageFor(m => profileModel.Name) -
              - - @Html.LabelFor(m => profileModel.Email) - @Html.TextBoxFor(m => profileModel.Email) - @Html.ValidationMessageFor(m => profileModel.Email) -
              - - @for (var i = 0; i < profileModel.MemberProperties.Count; i++) - { - @Html.LabelFor(m => profileModel.MemberProperties[i].Value, profileModel.MemberProperties[i].Name) - @Html.EditorFor(m => profileModel.MemberProperties[i].Value) - @Html.HiddenFor(m => profileModel.MemberProperties[i].Alias) -
              - } - - @Html.HiddenFor(m => profileModel.MemberTypeAlias) - - -
              - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml deleted file mode 100644 index 8d10a3a9c7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml +++ /dev/null @@ -1 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoViewPage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml deleted file mode 100644 index e1b5d1280b..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml +++ /dev/null @@ -1,16 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* Check the current page has ancestors *@ -@if (CurrentPage.Ancestors().Any()) -{ -
                - @* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@ - @foreach (var page in CurrentPage.Ancestors().OrderBy("Level")) - { -
              • @page.Name »
              • - } - - @* Display the current page as the last item in the list *@ -
              • @CurrentPage.Name
              • -
              -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml deleted file mode 100644 index 91726cd546..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ -@if (CurrentPage.Children.Where("Visible").Any()) -{ -
                - @* For each child page under the root node, where the property umbracoNaviHide is not True *@ - @foreach (var childPage in CurrentPage.Children.Where("Visible")) - { -
              • - @childPage.Name -
              • - } -
              -} diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml deleted file mode 100644 index 344b72247c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -
                - @*OrderBy() takes the property to sort by and optionally order desc/asc *@ - - @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("CreateDate desc")) - { -
              • @page.Name
              • - } -
              diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml deleted file mode 100644 index 191f4dbf3b..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage -
                - @*OrderBy() takes the property to sort by*@ - @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("Name")) - { -
              • @page.Name
              • - } -
              diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml deleted file mode 100644 index f44964335e..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - This snippet shows how simple it is to fetch only children of a certain Document Type using Razor. Instead of - calling .Children, simply call .AliasOfDocumentType in plural. - For instance .Textpages or .NewsArticles (you can find the alias of your Document Type by editing it in the - Settings section). -*@ - - -@{ - @*Build a query and return the visible items *@ - var selection= CurrentPage.Textpages.Where("Visible"); - - @* - Example of more querying, if you have a true/false property with the alias of shouldBeFeatured: - var selection= Model.Textpages.Where("shouldBeFeatured == true").Where("Visible"); - *@ -} - - -@*Determine if there are any nodes in the selection, then render list *@ -@if(selection.Any()){ - -
                - @foreach(var page in selection){ -
              • @page.Name
              • - } -
              - -} - diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml deleted file mode 100644 index 40c343688c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage - -@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ -@if (CurrentPage.Children.Where("Visible").Any()) -{ - @* Get the first page in the children, where the property umbracoNaviHide is not True *@ - var naviLevel = CurrentPage.Children.Where("Visible").First().Level; - - @* Add in level for a CSS hook *@ -
                - @* For each child page under the root node, where the property umbracoNaviHide is not True *@ - @foreach (var childPage in CurrentPage.Children.Where("Visible")) - { -
              • - @childPage.Name - - @* if the current page has any children, where the property umbracoNaviHide is not True *@ - @if (childPage.Children.Where("Visible").Any()) - { - @* Call our helper to display the children *@ - @childPages(childPage.Children) - } -
              • - } -
              -} - - -@helper childPages(dynamic pages) - { - @* Ensure that we have a collection of pages *@ - if (pages.Any()) - { - @* Get the first page in pages and get the level *@ - var naviLevel = pages.First().Level; - - @* Add in level for a CSS hook *@ -
                - @foreach (var page in pages.Where("Visible")) - { -
              • - @page.Name - - @* if the current page has any children, where the property umbracoNaviHide is not True *@ - @if (page.Children.Where("Visible").Any()) - { - @* Call our helper to display the children *@ - @childPages(page.Children) - } -
              • - } -
              - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml deleted file mode 100644 index 9f0a8018b7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var loginModel = new LoginModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@Html.RenderJsHere() - -@using (Html.BeginUmbracoForm("HandleLogin")) -{ -
              - Login - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => loginModel.Username) - @Html.TextBoxFor(m => loginModel.Username) - @Html.ValidationMessageFor(m => loginModel.Username) -
              - - @Html.LabelFor(m => loginModel.Password) - @Html.PasswordFor(m => loginModel.Password) - @Html.ValidationMessageFor(m => loginModel.Password) -
              - - -
              -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml deleted file mode 100644 index 996c21820c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml +++ /dev/null @@ -1,31 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var loginStatusModel = new LoginStatusModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@Html.RenderJsHere() - -@if (loginStatusModel.IsLoggedIn) -{ -

              You are currently logged in as @loginStatusModel.Name

              - - using (Html.BeginUmbracoForm("HandleLogout")) - { -
              - Logout - -
              - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml deleted file mode 100644 index 39efceb361..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml +++ /dev/null @@ -1,20 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - Macro to list nodes from a Multinode tree picker, using the pickers default settings. - Content Values stored as xml. - - To get it working with any site's data structure, simply set the selection equal to the property which has the - multinode treepicker (so: replace "PropertyWithPicker" with the alias of your property). -*@ - -@* Lists each selected value from the picker as a link *@ -
                - @foreach(var id in CurrentPage.PropertyWithPicker){ - - @*For each link, get the node, and display its name and url*@ - var content = Umbraco.Content(id.InnerText); - -
              • @content.Name
              • - } -
              \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml deleted file mode 100644 index 626dade1ab..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - Macro to display child pages below the root page of a standard website. - Also highlights the current active page/section in the navigation with - the css class "current". -*@ - - -@{ - @*Get the root of the website *@ - var root = CurrentPage.AncestorOrSelf(1); -} - -
                - @foreach (var page in root.Children.Where("Visible")) - { -
              • - @page.Name -
              • - } -
              diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml deleted file mode 100644 index dad5d256d5..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml +++ /dev/null @@ -1,90 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var registerModel = new RegisterModel(); - - @* - Configurable here: - - registerModel.MemberTypeAlias - the default is "Member" - - registerModel.RedirectOnSucces - the default is false - - registerModel.RedirectUrl - what page do we go to if the registration was successful? - the default is "/" unless RedirectOnSuccess is set to false - - registerModel.UsernameIsEmail - the default is true - if you want the username to be different from the email - address, set this to true and add a new Username field in - the form below - - @Html.LabelFor(m => registerModel.Username) - @Html.TextBoxFor(m => registerModel.Username) - @Html.ValidationMessageFor(m => registerModel.Username) - *@ - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); - - var success = TempData["FormSuccess"] != null; -} - -@if (success) -{ - // This message will show if RedirectOnSucces is set to false (default) -

              Registration succeeeded.

              -} -else -{ - using (Html.BeginUmbracoForm("HandleRegisterMember")) - { -
              - Register Member - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => registerModel.Name) - @Html.TextBoxFor(m => registerModel.Name) - @Html.ValidationMessageFor(m => registerModel.Name) -
              - - @Html.LabelFor(m => registerModel.Email) - @Html.TextBoxFor(m => registerModel.Email) - @Html.ValidationMessageFor(m => registerModel.Email) -
              - - @Html.LabelFor(m => registerModel.Password) - @Html.PasswordFor(m => registerModel.Password) - @Html.ValidationMessageFor(m => registerModel.Password) -
              - - @if (registerModel.MemberProperties != null) { - for (var i = 0; i < registerModel.MemberProperties.Count; i++) - { - @Html.LabelFor(m => registerModel.MemberProperties[i].Value, registerModel.MemberProperties[i].Name) - @Html.EditorFor(m => registerModel.MemberProperties[i].Value) - @Html.HiddenFor(m => registerModel.MemberProperties[i].Alias) -
              - } - } - - @Html.HiddenFor(m => registerModel.MemberTypeAlias) - @Html.HiddenFor(m => registerModel.RedirectOnSucces) - @Html.HiddenFor(m => registerModel.RedirectUrl) - @Html.HiddenFor(m => registerModel.UsernameIsEmail) - - -
              - - @Html.RenderJsHere() - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml deleted file mode 100644 index 011cadbd64..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@*Render the sitemap by passing the root node to the traverse helper*@ -
              - @Traverse(CurrentPage.AncestorOrSelf()) -
              - -@*Helper method to travers through all descendants*@ -@helper Traverse(dynamic node) -{ - @* Update the level to reflect how deep you want the sitemap to go *@ - var maxLevelForSitemap = 4; - - @*Select visible children *@ - var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap); - - @*If any items are returned, render a list *@ - if (items.Any()) - { -
                - @foreach (var item in items) - { -
              • - @item.Name - - @*Run the traverse helper again *@ - @Traverse(item) -
              • - } -
              - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx index bf10210713..b34560a19e 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx @@ -22,5 +22,5 @@ <%=umbraco.ui.Text("cancel")%> - + diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs index 5fcd310656..07a918acc0 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs @@ -1,4 +1,6 @@ -using System; +using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Web.UI; using System.Web.UI.WebControls; @@ -21,26 +23,66 @@ namespace Umbraco.Web.UI.Umbraco.Create private static void LoadTemplates(ListControl list) { - var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"); + var partialViewTemplatePath = new + { + path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"), + include = new string[]{} + }; + //include these templates from the partial view macro templates! + var partialViewMacrosTemplatePath = new + { + path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/"), + include = new[] + { + "Breadcrumb", + "EditProfile", + "Empty (ForUseWithCustomViews)", + "Empty", + "ListAncestorsFromCurrentPage", + "ListChildPagesFromCurrentPage", + "ListChildPagesOrderedByDate", + "ListChildPagesOrderedByName", + "ListChildPagesWithDoctype", + "ListDescendantsFromCurrentPage", + "Login", + "LoginStatus", + "MultinodeTree-picker", + "Navigation", + "RegisterMember", + "SiteMap" + } + }; + var paths = new[] { partialViewTemplatePath, partialViewMacrosTemplatePath }; + const string extension = ".cshtml"; + var namesAdded = new List(); + list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); - - if (System.IO.Directory.Exists(path)) + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); + + foreach (var pathFilter in paths) { - const string extension = ".cshtml"; - - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + if (Directory.Exists(pathFilter.path) == false) continue; + + var p = pathFilter; + foreach (var fileInfo in new DirectoryInfo(pathFilter.path).GetFiles("*" + extension) + //check if we've already added this name + .Where(f => namesAdded.InvariantContains(f.Name) == false) + //Already adding Empty as the first item, so don't add it again + .Where(f => f.Name.StartsWith("Empty") == false) + //don't add if not in the inclusion list + .Where(f => p.include.Length == 0 || (p.include.Length > 0 && p.include.InvariantContains(f.Name) == false))) { - var filename = System.IO.Path.GetFileName(fileInfo.FullName); - + var filename = Path.GetFileName(fileInfo.FullName); + namesAdded.Add(filename); var liText = filename.Replace(extension, "").SplitPascalCasing().ToFirstUpperInvariant(); list.Items.Add(new ListItem(liText, filename)); } } + + } protected void SubmitButton_Click(object sender, System.EventArgs e) diff --git a/src/Umbraco.Web.UI/Umbraco/js/install.js b/src/Umbraco.Web.UI/Umbraco/js/install.js new file mode 100644 index 0000000000..41aab12c80 --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/install.js @@ -0,0 +1,30 @@ +yepnope({ + + load: [ + 'lib/jquery/jquery-2.0.3.min.js', + + /* 1.1.5 */ + 'lib/angular/1.1.5/angular.min.js', + 'lib/angular/1.1.5/angular-cookies.min.js', + 'lib/angular/1.1.5/angular-mobile.min.js', + 'lib/angular/1.1.5/angular-mocks.js', + 'lib/angular/1.1.5/angular-sanitize.min.js', + 'lib/underscore/underscore.js', + 'js/umbraco.servervariables.js', + 'js/app.dev.js' + ], + + complete: function () { + jQuery(document).ready(function () { + + angular.module('umbraco.install', [ + 'umbraco.resources', + 'umbraco.services', + 'umbraco.httpbackend', + 'ngMobile' + ]); + + angular.bootstrap(document, ['umbraco.install']); + }); + } +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js new file mode 100644 index 0000000000..c7b4bb0473 --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js @@ -0,0 +1,22 @@ +yepnope({ + + load: [ + 'lib/jquery/jquery-2.0.3.min.js', + + /* 1.1.5 */ + 'lib/angular/1.1.5/angular.min.js', + 'lib/angular/1.1.5/angular-cookies.min.js', + 'lib/angular/1.1.5/angular-mobile.min.js', + 'lib/angular/1.1.5/angular-mocks.js', + 'lib/angular/1.1.5/angular-sanitize.min.js', + 'lib/underscore/underscore.js', + 'js/umbraco.installer.js' + ], + + complete: function () { + jQuery(document).ready(function () { + + angular.bootstrap(document, ['ngSanitize', 'umbraco.install']); + }); + } +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index 089dec0ec8..d26bfe176a 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -9,32 +9,32 @@ It also manages the combination, compression and minification of all JS & CSS fi NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files ---> - +--> + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - diff --git a/src/Umbraco.Web.UI/config/Dashboard.Release.config b/src/Umbraco.Web.UI/config/Dashboard.Release.config index ec88b57e47..6e961a3972 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.Release.config +++ b/src/Umbraco.Web.UI/config/Dashboard.Release.config @@ -35,7 +35,7 @@ views/dashboard/media/mediafolderbrowser.html - +
              @@ -49,12 +49,12 @@ admin + views/dashboard/default/startupdashboardintro.html - - + - +
              diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index dd4032e74a..6e961a3972 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -49,10 +49,10 @@ admin + views/dashboard/default/startupdashboardintro.html -
              diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config index d19fce7c95..19b63dee7f 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config @@ -33,7 +33,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami + analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net" enableLeadingWildcard="true"/> diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 244bd1b12e..131867ac31 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -10,7 +10,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami @@ -19,7 +19,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami supportProtected="true" analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"/> - + @@ -30,11 +30,11 @@ More information and documentation can be found on CodePlex: http://umbracoexami - - - + + + diff --git a/src/Umbraco.Web.UI/config/FileSystemProviders.config b/src/Umbraco.Web.UI/config/FileSystemProviders.config index 457259fae9..ae2addc39a 100644 --- a/src/Umbraco.Web.UI/config/FileSystemProviders.config +++ b/src/Umbraco.Web.UI/config/FileSystemProviders.config @@ -7,5 +7,5 @@ - + diff --git a/src/Umbraco.Web.UI/config/UrlRewriting.config b/src/Umbraco.Web.UI/config/UrlRewriting.config index 348a07c972..108f53bf5e 100644 --- a/src/Umbraco.Web.UI/config/UrlRewriting.config +++ b/src/Umbraco.Web.UI/config/UrlRewriting.config @@ -29,6 +29,5 @@ Any bugs or problems with the rewriter, contact Anders/Duckie --> - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/applications.Release.config b/src/Umbraco.Web.UI/config/applications.Release.config index 042bd551cc..062500e1fc 100644 --- a/src/Umbraco.Web.UI/config/applications.Release.config +++ b/src/Umbraco.Web.UI/config/applications.Release.config @@ -1,4 +1,4 @@ - + diff --git a/src/Umbraco.Web.UI/config/feedProxy.config b/src/Umbraco.Web.UI/config/feedProxy.config index afce5316b0..e76284c13f 100644 --- a/src/Umbraco.Web.UI/config/feedProxy.config +++ b/src/Umbraco.Web.UI/config/feedProxy.config @@ -1,11 +1,11 @@  - - + + - - - + + + diff --git a/src/Umbraco.Web.UI/config/log4net.Release.config b/src/Umbraco.Web.UI/config/log4net.Release.config index dd9a5e4629..384124e719 100644 --- a/src/Umbraco.Web.UI/config/log4net.Release.config +++ b/src/Umbraco.Web.UI/config/log4net.Release.config @@ -1,25 +1,26 @@ - - - - + + + + - - - + - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/config/log4net.config b/src/Umbraco.Web.UI/config/log4net.config index cf73f9afee..10808bb091 100644 --- a/src/Umbraco.Web.UI/config/log4net.config +++ b/src/Umbraco.Web.UI/config/log4net.config @@ -1,25 +1,26 @@ - - - - + + + + - - + - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/scripting.config b/src/Umbraco.Web.UI/config/scripting.config index 505907d38a..ec59c35acb 100644 --- a/src/Umbraco.Web.UI/config/scripting.config +++ b/src/Umbraco.Web.UI/config/scripting.config @@ -6,7 +6,6 @@ - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/splashes/booting.aspx b/src/Umbraco.Web.UI/config/splashes/booting.aspx index 267e36b369..1bea61f89d 100644 --- a/src/Umbraco.Web.UI/config/splashes/booting.aspx +++ b/src/Umbraco.Web.UI/config/splashes/booting.aspx @@ -2,7 +2,7 @@ <% // NH: Adds this inline check to avoid a simple codebehind file in the legacy project! - if (!umbraco.cms.helpers.url.ValidateProxyUrl(Request["url"], Request.Url.AbsoluteUri)) + if (Request["url"].ToLower().Contains("booting.aspx") || !umbraco.cms.helpers.url.ValidateProxyUrl(Request["url"], Request.Url.AbsoluteUri)) { throw new ArgumentException("Can't redirect to the requested url - it's not local or an approved proxy url", "url"); diff --git a/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config b/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config index 7fad4e496c..2cae6d6f32 100644 --- a/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config +++ b/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config @@ -205,6 +205,7 @@ anchor charmap table + lists anchor charmap table + lists + @@ -15,6 +15,7 @@ umbracoExtension + @@ -40,7 +41,7 @@ - robot@umbraco.dk + your@email.here @@ -49,8 +50,15 @@ False - - Raw + + UTF8 + + + false + + + + true True @@ -59,7 +67,7 @@ True - True + False False @@ -67,24 +75,24 @@ text - false - - true - - true - + In Preview Mode - click to end]]> 1800 + + + + false + + - inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour. + - silent - Silently suppress the error and do not render the offending macro. + - throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such + error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page. + Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. --> throw @@ -107,14 +115,18 @@ false - false + true - + + + + plus star @@ -122,15 +134,29 @@ ae oe aa + ae + oe + ue + ss + ae + oe + - + + + + true - true + + + Mvc + @@ -139,6 +165,29 @@ + + + + + p + div + ul + span + + + + + + + + - falses - 86400 - 1440 + + + @@ -168,9 +217,9 @@ 0 - - umblb1.dev - localhost + + + diff --git a/src/Umbraco.Web.UI/install/Default.aspx.cs b/src/Umbraco.Web.UI/install/Default.aspx.cs deleted file mode 100644 index 75e701d250..0000000000 --- a/src/Umbraco.Web.UI/install/Default.aspx.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Web; -using System.Web.UI.WebControls; -using Umbraco.Core.IO; -using Umbraco.Web.Install; -using Umbraco.Web.Security; -using Umbraco.Web.UI.Pages; -using umbraco; - -namespace Umbraco.Web.UI.Install -{ - public partial class Default : BasePage - { - private string _installStep = ""; - - protected string CurrentStepClass = ""; - - protected void Page_Load(object sender, System.EventArgs e) - { - rp_steps.DataSource = InstallHelper.InstallerSteps.Values; - rp_steps.DataBind(); - } - - private void LoadContent(InstallerStep currentStep) - { - PlaceHolderStep.Controls.Clear(); - PlaceHolderStep.Controls.Add(LoadControl(IOHelper.ResolveUrl(currentStep.UserControl))); - step.Value = currentStep.Alias; - CurrentStepClass = currentStep.Alias; - } - - int _stepCounter = 0; - protected void BindStep(object sender, RepeaterItemEventArgs e) - { - - if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) - { - var i = (InstallerStep)e.Item.DataItem; - - if (!i.HideFromNavigation) - { - var _class = (Literal)e.Item.FindControl("lt_class"); - var name = (Literal)e.Item.FindControl("lt_name"); - - if (i.Alias == CurrentStepClass) - _class.Text = "active"; - - _stepCounter++; - name.Text = (_stepCounter).ToString() + " - " + i.Name; - } - else - e.Item.Visible = false; - } - } - - override protected void OnInit(EventArgs e) - { - base.OnInit(e); - - _installStep = Request.GetItemAsString("installStep"); - - //if this is not an upgrade we will log in with the default user. - // It's not considered an upgrade if the ConfigurationStatus is missing or empty. - if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus) == false) - { - var result = Security.ValidateCurrentUser(false); - - switch (result) - { - case ValidateRequestAttempt.FailedNoPrivileges: - case ValidateRequestAttempt.FailedTimedOut: - case ValidateRequestAttempt.FailedNoContextId: - Response.Redirect(SystemDirectories.Umbraco + "/AuthorizeUpgrade?redir=" + Server.UrlEncode(Request.RawUrl)); - break; - } - } - - var s = string.IsNullOrEmpty(_installStep) - ? InstallHelper.InstallerSteps["welcome"] - : InstallHelper.InstallerSteps[_installStep]; - - LoadContent(s); - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/Default.aspx.designer.cs b/src/Umbraco.Web.UI/install/Default.aspx.designer.cs deleted file mode 100644 index 37e8062070..0000000000 --- a/src/Umbraco.Web.UI/install/Default.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install { - - - public partial class Default - { - /// - /// ScriptManager1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.ScriptManager ScriptManager1; - - /// - /// rp_steps control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Repeater rp_steps; - - /// - /// PlaceHolderStep control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderStep; - - /// - /// step control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - public global::System.Web.UI.HtmlControls.HtmlInputHidden step; - } -} diff --git a/src/Umbraco.Web.UI/install/InstallerRestService.aspx b/src/Umbraco.Web.UI/install/InstallerRestService.aspx deleted file mode 100644 index cf38b78a70..0000000000 --- a/src/Umbraco.Web.UI/install/InstallerRestService.aspx +++ /dev/null @@ -1 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="True" CodeBehind="InstallerRestService.aspx.cs" Inherits="Umbraco.Web.UI.Install.InstallerRestService" %> \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs b/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs deleted file mode 100644 index 248e6c17d2..0000000000 --- a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Configuration; -using System.Security.Authentication; -using System.Web.Script.Serialization; -using System.Web.Script.Services; -using System.Web.Services; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Web.Install; -using umbraco; -using umbraco.businesslogic.Exceptions; - -namespace Umbraco.Web.UI.Install -{ - public partial class InstallerRestService : System.Web.UI.Page - { - protected void Page_Load(object sender, EventArgs e) - { - LogHelper.Info(string.Format("Hitting Page_Load on InstallerRestService.aspx for the requested '{0}' feed", Request.QueryString["feed"])); - - // Stop Caching in IE - Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); - - // Stop Caching in Firefox - Response.Cache.SetNoStore(); - - string feed = Request.QueryString["feed"]; - string url = "http://our.umbraco.org/html/twitter"; - - if (feed == "progress") - { - Response.ContentType = "application/json"; - Response.Write(InstallHelper.GetProgress()); - } - else - { - if (feed == "blogs") - url = "http://our.umbraco.org/html/blogs"; - - if (feed == "sitebuildervids") - url = "http://umbraco.org/feeds/videos/site-builder-foundation-html"; - - if (feed == "developervids") - url = "http://umbraco.org/feeds/videos/developer-foundation-html"; - - string xmlResponse = library.GetXmlDocumentByUrl(url).Current.OuterXml; - - if (!xmlResponse.Contains("System.Net.WebException")) - { - Response.Write(library.GetXmlDocumentByUrl(url).Current.OuterXml); - } - else - { - Response.Write("We can't connect to umbraco.tv right now. Click Set up your new website above to continue."); - } - } - } - - - [WebMethod] - [ScriptMethod(ResponseFormat = ResponseFormat.Json)] - public static string Install() - { - //if its not configured then we can continue - if (ApplicationContext.Current == null || ApplicationContext.Current.IsConfigured) - { - throw new AuthenticationException("The application is already configured"); - } - - LogHelper.Info("Running 'Install' service"); - - var result = ApplicationContext.Current.DatabaseContext.CreateDatabaseSchemaAndData(); - - if (result.RequiresUpgrade == false) - { - HandleConnectionStrings(); - } - - var js = new JavaScriptSerializer(); - var jsonResult = js.Serialize(result); - return jsonResult; - } - - [WebMethod] - [ScriptMethod(ResponseFormat = ResponseFormat.Json)] - public static string Upgrade() - { - //if its not configured then we can continue - if (ApplicationContext.Current == null || ApplicationContext.Current.IsConfigured) - { - throw new AuthenticationException("The application is already configured"); - } - - LogHelper.Info("Running 'Upgrade' service"); - - var result = ApplicationContext.Current.DatabaseContext.UpgradeSchemaAndData(); - - HandleConnectionStrings(); - - var js = new JavaScriptSerializer(); - var jsonResult = js.Serialize(result); - return jsonResult; - } - - private static void HandleConnectionStrings() - { - // Remove legacy umbracoDbDsn configuration setting if it exists and connectionstring also exists - if (ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName] != null) - { - Core.Configuration.GlobalSettings.RemoveSetting(Core.Configuration.GlobalSettings.UmbracoConnectionName); - } - else - { - var ex = new ArgumentNullException(string.Format("ConfigurationManager.ConnectionStrings[{0}]", Core.Configuration.GlobalSettings.UmbracoConnectionName), "Install / upgrade did not complete successfully, umbracoDbDSN was not set in the connectionStrings section"); - LogHelper.Error("", ex); - throw ex; - } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.designer.cs b/src/Umbraco.Web.UI/install/InstallerRestService.aspx.designer.cs deleted file mode 100644 index 390aeeeaeb..0000000000 --- a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.designer.cs +++ /dev/null @@ -1,15 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install { - - - public partial class InstallerRestService { - } -} diff --git a/src/Umbraco.Web.UI/install/Title.ascx b/src/Umbraco.Web.UI/install/Title.ascx deleted file mode 100644 index 748e05dfe8..0000000000 --- a/src/Umbraco.Web.UI/install/Title.ascx +++ /dev/null @@ -1,3 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="True" CodeBehind="Title.ascx.cs" Inherits="Umbraco.Web.UI.Install.Title" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> -Umbraco <%=UmbracoVersion.Current.ToString(3)%> <%=UmbracoVersion.CurrentComment%> Configuration Wizard \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/Title.ascx.cs b/src/Umbraco.Web.UI/install/Title.ascx.cs deleted file mode 100644 index ae4881e776..0000000000 --- a/src/Umbraco.Web.UI/install/Title.ascx.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web.UI.Install -{ - public partial class Title : System.Web.UI.UserControl - { - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/Title.ascx.designer.cs b/src/Umbraco.Web.UI/install/Title.ascx.designer.cs deleted file mode 100644 index b5999c18bb..0000000000 --- a/src/Umbraco.Web.UI/install/Title.ascx.designer.cs +++ /dev/null @@ -1,15 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install { - - - public partial class Title { - } -} diff --git a/src/Umbraco.Web.UI/install/default.aspx b/src/Umbraco.Web.UI/install/default.aspx deleted file mode 100644 index 20deecb43f..0000000000 --- a/src/Umbraco.Web.UI/install/default.aspx +++ /dev/null @@ -1,161 +0,0 @@ -<%@ Page Language="c#" CodeBehind="Default.aspx.cs" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Install.Default" EnableViewState="False" %> - -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - -<%@ Register Src="~/install/Title.ascx" TagPrefix="umb1" TagName="PageTitle" %> - - - - - - - - " /> - - - - - - - - - - - - - - - - - - - - - - - -
              - - - - - - - -
              - -
              - - - - - - - -
              - - - - - - - -
              - -
              -
              -
              -
              - - - - - - - - - - -
              - - - - diff --git a/src/Umbraco.Web.UI/install/steps/Database.ascx.cs b/src/Umbraco.Web.UI/install/steps/Database.ascx.cs deleted file mode 100644 index ede7132da0..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Database.ascx.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Configuration; -using System.Data.Common; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using System.IO; -using Umbraco.Core.Persistence; -using Umbraco.Web.Install; -using umbraco.DataLayer; - -namespace Umbraco.Web.UI.Install.Steps -{ - /// - /// Database detection step in the installer wizard. - /// - public partial class Database : StepUserControl - { - /// - /// Returns whether the selected database is an embedded database. - /// - protected bool IsEmbeddedDatabase - { - get - { - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; - var configuredDatabaseIsEmbedded = databaseSettings != null && databaseSettings.ProviderName.ToLower().Contains("SqlServerCe".ToLower()); - - return Request["database"] == "embedded" || configuredDatabaseIsEmbedded; - } - } - - protected bool IsConfigured - { - get { return DatabaseType.SelectedValue != ""; } - } - - protected bool IsNewInstall - { - get - { - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; - if (databaseSettings != null && ( - databaseSettings.ConnectionString.Trim() == string.Empty - && databaseSettings.ProviderName.Trim() == string.Empty - && GlobalSettings.ConfigurationStatus == string.Empty)) - { - return true; - } - - return false; - } - } - - /// - /// Returns whether the connection string is set by direct text input. - /// - protected bool ManualConnectionString - { - get { return Request["database"] == "advanced"; } - } - - /// - /// Shows the right panel to the user. - /// - /// The sender. - /// The event arguments. - protected void Page_Load(object sender, System.EventArgs e) - { - // Does the user have to enter a connection string? - if (settings.Visible && !Page.IsPostBack) - { - //If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading. - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; - - var dbIsSqlCe = false; - if(databaseSettings != null && databaseSettings.ProviderName != null) - dbIsSqlCe = databaseSettings.ProviderName == "System.Data.SqlServerCe.4.0"; - var sqlCeDatabaseExists = false; - if (dbIsSqlCe) - { - var datasource = databaseSettings.ConnectionString.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()); - var filePath = datasource.Replace("Data Source=", string.Empty); - sqlCeDatabaseExists = File.Exists(filePath); - } - - // Either the connection details are not fully specified or it's a SQL CE database that doesn't exist yet - if (databaseSettings == null - || string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) || string.IsNullOrWhiteSpace(databaseSettings.ProviderName) - || (dbIsSqlCe && sqlCeDatabaseExists == false)) - { - installProgress.Visible = true; - upgradeProgress.Visible = false; - ShowDatabaseSettings(); - } - else - { - //Since a connection string was present we verify whether this is an upgrade or an empty db - var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema(); - var determinedVersion = result.DetermineInstalledVersion(); - if (determinedVersion.Equals(new Version(0, 0, 0))) - { - //Fresh install - installProgress.Visible = true; - upgradeProgress.Visible = false; - } - else - { - //Upgrade - installProgress.Visible = false; - upgradeProgress.Visible = true; - } - - settings.Visible = false; - installing.Visible = true; - } - } - } - - /// - /// Prepares and shows the database settings panel. - /// - protected void ShowDatabaseSettings() - { - // Parse the connection string - var connectionStringBuilder = new DbConnectionStringBuilder(); - - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; - if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false) - { - var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false); - connectionStringBuilder.ConnectionString = dataHelper.ConnectionString; - - // Prepare data layer type - var datalayerType = GetConnectionStringValue(connectionStringBuilder, "datalayer"); - if (datalayerType.Length > 0) - { - foreach (ListItem item in DatabaseType.Items) - if (item.Value != string.Empty && datalayerType.Contains(item.Value)) - DatabaseType.SelectedValue = item.Value; - } - else if (dataHelper.ConnectionString != "server=.\\SQLEXPRESS;database=DATABASE;user id=USER;password=PASS") - DatabaseType.SelectedValue = "SqlServer"; - } - else - { - DatabaseType.SelectedValue = "SqlServer"; - } - - DatabaseType_SelectedIndexChanged(this, new EventArgs()); - - // Prepare other fields - DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "server"); - if (string.IsNullOrEmpty(DatabaseServer.Text)) DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "Data Source"); - DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "database"); - if (string.IsNullOrEmpty(DatabaseName.Text)) DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "Initial Catalog"); - DatabaseUsername.Text = GetConnectionStringValue(connectionStringBuilder, "user id"); - DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "password"); - if (string.IsNullOrEmpty(DatabasePassword.Text)) DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "pwd"); - - toggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); - - if (IsNewInstall || IsEmbeddedDatabase) - dbinit.Text = "$('#databaseOptionEmbedded').click();$('#databaseOptionEmbedded').change();"; - else if (ManualConnectionString) - dbinit.Text = "$('#databaseOptionAdvanced').click();$('#databaseOptionAdvanced').change();"; - else if (DatabaseType.SelectedValue == "SqlServer") - dbinit.Text = "$('#databaseOptionBlank').click();$('#databaseOptionBlank').change();"; - else if (DatabaseType.SelectedValue == "SqlAzure") - dbinit.Text = "$('#databaseOptionBlank').click();$('#databaseOptionBlank').change();"; - //toggleVisible(DatabaseConnectionString, ManualConnectionString); - - // Make sure ASP.Net displays the password text - DatabasePassword.Attributes["value"] = DatabasePassword.Text; - } - - /// - /// Shows the installation/upgrade panel. - /// - protected void SaveDbConfig(object sender, EventArgs e) - { - try - { - var dbContext = ApplicationContext.Current.DatabaseContext; - - if (string.IsNullOrEmpty(ConnectionString.Text) == false) - { - dbContext.ConfigureDatabaseConnection(ConnectionString.Text); - } - else if (IsEmbeddedDatabase) - { - dbContext.ConfigureEmbeddedDatabaseConnection(); - } - else - { - var server = DatabaseServer.Text; - var databaseName = DatabaseName.Text; - - if (DatabaseType.SelectedValue == "SqlServer" && DatabaseIntegratedSecurity.Checked == true) - { - dbContext.ConfigureIntegratedSecurityDatabaseConnection(server, databaseName); - } - else - { - dbContext.ConfigureDatabaseConnection(server, databaseName, - DatabaseUsername.Text, DatabasePassword.Text, DatabaseType.SelectedValue - ); - } - } - } - catch (Exception ex) - { - LogHelper.Error("Exception was thrown during the setup of the database in 'saveDBConfig'.", ex); - } - - settings.Visible = false; - installing.Visible = true; - } - - /// - /// Gets the value of the specified item in the connection string. - /// - /// The connection string. - /// Name of the item. - /// The value of the item, or an empty string if not found. - protected string GetConnectionStringValue(DbConnectionStringBuilder connectionStringBuilder, string keyword) - { - object value = null; - connectionStringBuilder.TryGetValue(keyword, out value); - return (string)value ?? String.Empty; - } - - /// - /// Show the needed fields according to the database type. - /// - /// The sender. - /// The event arguments. - protected void DatabaseType_SelectedIndexChanged(object sender, EventArgs e) - { - toggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); - - //toggleVisible(DatabaseConnectionString, ManualConnectionString); - } - - private static void toggleVisible(HtmlGenericControl div, bool visible) - { - if (!visible) - div.Attributes["style"] = "display: none;"; - else - div.Attributes["style"] = "display: block;"; - } - - protected void GotoSettings(object sender, EventArgs e) - { - settings.Visible = true; - installing.Visible = false; - - ShowDatabaseSettings(); - - jsVars.Text = "showDatabaseSettings();"; - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/Database.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/Database.ascx.designer.cs deleted file mode 100644 index 2c41a8968e..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Database.ascx.designer.cs +++ /dev/null @@ -1,249 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class Database { - - /// - /// settings control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder settings; - - /// - /// DatabaseType control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList DatabaseType; - - /// - /// ph_dbError control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_dbError; - - /// - /// lt_dbError control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_dbError; - - /// - /// DatabaseServerItem control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl DatabaseServerItem; - - /// - /// DatabaseServerLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label DatabaseServerLabel; - - /// - /// DatabaseServer control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox DatabaseServer; - - /// - /// DatabaseNameItem control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl DatabaseNameItem; - - /// - /// DatabaseNameLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label DatabaseNameLabel; - - /// - /// DatabaseName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox DatabaseName; - - /// - /// DatabaseIntegratedSecurity control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox DatabaseIntegratedSecurity; - - /// - /// DatabaseUsernameItem control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl DatabaseUsernameItem; - - /// - /// DatabaseUsernameLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label DatabaseUsernameLabel; - - /// - /// DatabaseUsername control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox DatabaseUsername; - - /// - /// DatabasePasswordItem control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl DatabasePasswordItem; - - /// - /// DatabasePasswordLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label DatabasePasswordLabel; - - /// - /// DatabasePassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox DatabasePassword; - - /// - /// embeddedFilesMissing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl embeddedFilesMissing; - - /// - /// DatabaseConnectionString control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl DatabaseConnectionString; - - /// - /// ConnectionStringLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label ConnectionStringLabel; - - /// - /// ConnectionString control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox ConnectionString; - - /// - /// jsVars control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal jsVars; - - /// - /// dbinit control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal dbinit; - - /// - /// installing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder installing; - - /// - /// installProgress control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder installProgress; - - /// - /// upgradeProgress control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder upgradeProgress; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs b/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs deleted file mode 100644 index 897fcb5e5e..0000000000 --- a/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Web.Security; -using Umbraco.Core.Configuration; -using Umbraco.Web.Install; -using Umbraco.Web.Security; -using umbraco.BusinessLogic; -using umbraco.providers; -using System.Collections.Specialized; - -namespace Umbraco.Web.UI.Install.Steps -{ - /// - /// Summary description for defaultUser. - /// - public partial class DefaultUser : StepUserControl - { - - protected MembershipProvider CurrentProvider - { - get - { - var provider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; - if (provider == null) - { - throw new InvalidOperationException("No MembershipProvider found with name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); - } - return provider; - } - } - - protected void ChangePasswordClick(object sender, System.EventArgs e) - { - Page.Validate(); - - if (Page.IsValid) - { - var u = User.GetUser(0); - var user = CurrentProvider.GetUser(0, true); - if (user == null) - { - throw new InvalidOperationException("No user found in membership provider with id of 0"); - } - - //NOTE: This will throw an exception if the membership provider - try - { - var success = user.ChangePassword(u.GetPassword(), tb_password.Text.Trim()); - if (!success) - { - PasswordValidator.IsValid = false; - PasswordValidator.ErrorMessage = "Password must be at least " + CurrentProvider.MinRequiredPasswordLength + " characters long and contain at least " + CurrentProvider.MinRequiredNonAlphanumericCharacters + " symbols"; - return; - } - } - catch (Exception ex) - { - PasswordValidator.IsValid = false; - PasswordValidator.ErrorMessage = "Password must be at least " + CurrentProvider.MinRequiredPasswordLength + " characters long and contain at least " + CurrentProvider.MinRequiredNonAlphanumericCharacters + " symbols"; - return; - } - - // Is it using the default membership provider - if (CurrentProvider is UsersMembershipProvider) - { - // Save user in membership provider - var umbracoUser = user as UsersMembershipUser; - umbracoUser.FullName = tb_name.Text.Trim(); - CurrentProvider.UpdateUser(umbracoUser); - - // Save user details - u.Email = tb_email.Text.Trim(); - } - else - { - u.Name = tb_name.Text.Trim(); - if ((CurrentProvider is ActiveDirectoryMembershipProvider) == false) - { - CurrentProvider.UpdateUser(user); - } - } - - // we need to update the login name here as it's set to the old name when saving the user via the membership provider! - u.LoginName = tb_login.Text; - - u.Save(); - - if (cb_newsletter.Checked) - { - try - { - var client = new System.Net.WebClient(); - var values = new NameValueCollection {{"name", tb_name.Text}, {"email", tb_email.Text}}; - - client.UploadValues("http://umbraco.org/base/Ecom/SubmitEmail/installer.aspx", values); - - } - catch { /* fail in silence */ } - } - - - if (String.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus)) - UmbracoContext.Current.Security.PerformLogin(u.Id); - - InstallHelper.RedirectToNextStep(Page, GetCurrentStep()); - } - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/License.ascx.cs b/src/Umbraco.Web.UI/install/steps/License.ascx.cs deleted file mode 100644 index ff6a6c6c50..0000000000 --- a/src/Umbraco.Web.UI/install/steps/License.ascx.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Umbraco.Web.Install; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class License : StepUserControl - { - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/License.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/License.ascx.designer.cs deleted file mode 100644 index 76f8b87c8f..0000000000 --- a/src/Umbraco.Web.UI/install/steps/License.ascx.designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class License { - - /// - /// btnNext control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton btnNext; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/Renaming.ascx.cs b/src/Umbraco.Web.UI/install/steps/Renaming.ascx.cs deleted file mode 100644 index 496690a715..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Renaming.ascx.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.IO; -using System.Xml; -using Umbraco.Core.IO; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class Renaming : StepUserControl - { - private readonly string _oldAccessFilePath = IOHelper.MapPath(SystemDirectories.Data + "/access.xml"); - private readonly string _newAccessFilePath = IOHelper.MapPath(SystemDirectories.Data + "/access.config"); - private bool _changesNeeded = false; - - protected void Page_Load(object sender, EventArgs e) - { - // check access.xml file - identifyResult.Text += CheckAccessFile(); - - if (_changesNeeded) - { - changesNeeded.Visible = true; - } - else - { - noChangedNeeded.Visible = true; - changesNeeded.Visible = false; - } - } - - private string CheckAccessFile() - { - if (!NewAccessFileExist() && OldAccessFileExist()) - { - _changesNeeded = true; - return "
            • Access.xml found. Needs to be renamed to access.config
            • "; - } - return "
            • Public Access file is all good. No changes needed
            • "; - } - - private bool OldAccessFileExist() - { - return File.Exists(_oldAccessFilePath); - } - - private bool NewAccessFileExist() - { - return File.Exists(_newAccessFilePath); - } - - protected void UpdateChangesClick(object sender, EventArgs e) - { - bool succes = true; - string progressText = ""; - - // rename access file - if (OldAccessFileExist()) - { - try - { - File.Move(_oldAccessFilePath, IOHelper.MapPath(SystemFiles.AccessXml)); - progressText += String.Format("
            • Public Access file renamed
            • "); - } - catch (Exception ee) - { - progressText += String.Format("
            • Error renaming access file: {0}
            • ", ee.ToString()); - succes = false; - } - } - - string resultClass = succes ? "success" : "error"; - resultText.Text = String.Format("

              {1}

              ", - resultClass, - progressText); - result.Visible = true; - init.Visible = false; - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/Renaming.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/Renaming.ascx.designer.cs deleted file mode 100644 index 90743813a4..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Renaming.ascx.designer.cs +++ /dev/null @@ -1,78 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class Renaming { - - /// - /// init control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel init; - - /// - /// noChangedNeeded control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel noChangedNeeded; - - /// - /// changesNeeded control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel changesNeeded; - - /// - /// identifyResult control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal identifyResult; - - /// - /// updateChanges control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button updateChanges; - - /// - /// result control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel result; - - /// - /// resultText control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal resultText; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx b/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx deleted file mode 100644 index 1b94e7ebd9..0000000000 --- a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx +++ /dev/null @@ -1,27 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="RenderingEngine.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.RenderingEngine" %> - -
              -
              -

              - Chose how you like to work with Templates

              -

              - Umbraco works with both ASP.NET WebForms (also known as MasterPages) and ASP.NET MVC (called Views). If you're not sure, we recommend using the MVC templates. You can of course use both but let's select a default one to get started. -

              -
              -
              -
              -

              - Choose a default template type: -

              - - MVC - Web forms - -
              -
              - -
              -
               
              - Continue -
              -
              diff --git a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.cs b/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.cs deleted file mode 100644 index b47830ecbe..0000000000 --- a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core.Configuration; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class RenderingEngine : StepUserControl - { - protected override void GotoNextStep(object sender, EventArgs e) - { - ////set the default engine - //UmbracoSettings.DefaultRenderingEngine = Core.RenderingEngine.Mvc; - //UmbracoSettings.Save(); - - base.GotoNextStep(sender, e); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.designer.cs deleted file mode 100644 index 44e5bc8d04..0000000000 --- a/src/Umbraco.Web.UI/install/steps/RenderingEngine.ascx.designer.cs +++ /dev/null @@ -1,33 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class RenderingEngine { - - /// - /// EngineSelection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButtonList EngineSelection; - - /// - /// btnNext control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton btnNext; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx b/src/Umbraco.Web.UI/install/steps/StarterKits.ascx deleted file mode 100644 index a71ff25d9b..0000000000 --- a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx +++ /dev/null @@ -1,122 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="StarterKits.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.StarterKits" %> - - - - - - - - - - - - - -
              -
              -

              Starter kits

              -

              To help you get started here are some basic starter kits. They have been tailored to suit common site configurations and install useful functionality.

              -
              - - -
              -
              - - - - -
              -
              -

              Install a Skin

              -
              -

              You can now further enhance your site by choosing one of these great skins. This will apply a default look and feel to all the pages in your site, considerably reducing development time.

              -
              -
              - - -
              -
              - -
              -
              - - - - - - diff --git a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.cs b/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.cs deleted file mode 100644 index 7a51a2eede..0000000000 --- a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Umbraco.Core.IO; -using Umbraco.Web.Install; -using Umbraco.Web.UI.Install.Steps.Skinning; -using umbraco.cms.businesslogic.packager; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class StarterKits : StepUserControl - { - - protected void Page_Load(object sender, EventArgs e) - { - if (InstalledPackage.GetAllInstalledPackages().Count > 0) - GotoNextStep(sender, e); - - ShowStarterKits(); - } - - - private void ShowStarterKits() - { - ph_starterKits.Controls.Add(LoadControl(SystemDirectories.Install + "/steps/Skinning/loadStarterKits.ascx")); - - pl_starterKit.Visible = true; - pl_starterKitDesign.Visible = false; - - - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.designer.cs deleted file mode 100644 index f46284e985..0000000000 --- a/src/Umbraco.Web.UI/install/steps/StarterKits.ascx.designer.cs +++ /dev/null @@ -1,60 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class StarterKits { - - /// - /// udp control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.UpdatePanel udp; - - /// - /// pl_starterKit control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder pl_starterKit; - - /// - /// ph_starterKits control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_starterKits; - - /// - /// pl_starterKitDesign control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder pl_starterKitDesign; - - /// - /// ph_starterKitDesigns control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_starterKitDesigns; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/StepUserControl.cs b/src/Umbraco.Web.UI/install/steps/StepUserControl.cs deleted file mode 100644 index 49a72a5da1..0000000000 --- a/src/Umbraco.Web.UI/install/steps/StepUserControl.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Web.UI; -using Umbraco.Web.Install; - -namespace Umbraco.Web.UI.Install.Steps -{ - public abstract class StepUserControl : UserControl - { - protected string GetCurrentStep() - { - var defaultPage = (Default) Page; - return defaultPage.step.Value; - } - - protected virtual void GotoNextStep(object sender, EventArgs e) - { - InstallHelper.RedirectToNextStep(Page, GetCurrentStep()); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs b/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs deleted file mode 100644 index 4053f0c782..0000000000 --- a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class TheEnd : StepUserControl - { - protected void Page_Load(object sender, System.EventArgs e) - { - // Update configurationStatus - try - { - GlobalSettings.ConfigurationStatus = UmbracoVersion.Current.ToString(3); - } - catch (Exception) - { - //errorLiteral.Text = ex.ToString(); - } - - // Update ClientDependency version - var clientDependencyConfig = new ClientDependencyConfiguration(); - var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber(); - - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.designer.cs deleted file mode 100644 index 2c7c5fe114..0000000000 --- a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.designer.cs +++ /dev/null @@ -1,15 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class TheEnd { - } -} diff --git a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx b/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx deleted file mode 100644 index e254965090..0000000000 --- a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx +++ /dev/null @@ -1,73 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UpgradeReport.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.UpgradeReport" %> - -
              -
              -

              Major version upgrade from <%= CurrentVersion %> to <%= NewVersion %>

              - - - -

              - This installation step will determine if there are compatibility issues with Property Editors that you have defined in your current installation. -

              -
              - - - - -

              There were <%=Report.Count() %> issues detected

              -

              - The following compatibility issues were found. If you continue all non-compatible property editors will be converted to a Readonly/Label. - You will be able to change the property editor to a compatible type manually by editing the data type after installation. -

              -

              - Otherwise if you choose not to proceed you will need to fix the errors listed below. - Refer to v<%= NewVersion%> upgrade instructions for full details. -

              -
              - -

              No issues detected

              -

              - Click 'Continue' to proceed with the upgrade -

              -
              -
              -
              -
              - -
              -
              -
              - - -

              - Click 'Continue' to generate the compatibility report -

              -
              - - - - <% foreach (var item in Report) - { %> - - - - - - - <% } %> -
              - - - <%=item.Item2 %> -
              -
              -
              - -
              -
              - -
              -
               
              - Continue -
              -
              diff --git a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.cs b/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.cs deleted file mode 100644 index 8377930abd..0000000000 --- a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class UpgradeReport : StepUserControl - { - protected Version CurrentVersion { get; private set; } - protected Version NewVersion { get; private set; } - protected IEnumerable> Report { get; private set; } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema(); - var determinedVersion = result.DetermineInstalledVersion(); - - CurrentVersion = determinedVersion; - NewVersion = UmbracoVersion.Current; - Report = new List>(); - } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - if (!IsPostBack) - { - DataBind(); - } - } - - protected void NextButtonClick(object sender, EventArgs e) - { - if (ToggleView.ActiveViewIndex == 1) - { - GotoNextStep(sender, e); - } - else - { - CreateReport(); - ToggleView.ActiveViewIndex = 1; - DataBind(); - } - } - - private void CreateReport() - { - var errorReport = new List>(); - - var sql = new Sql(); - sql - .Select( - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumn("cmsDataType", "controlId"), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumn("umbracoNode", "text")) - .From(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsDataType")) - .InnerJoin(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode")) - .On( - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumn("cmsDataType", "nodeId") + " = " + - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumn("umbracoNode", "id")); - - var list = ApplicationContext.Current.DatabaseContext.Database.Fetch(sql); - foreach (var item in list) - { - Guid legacyId = item.controlId; - //check for a map entry - var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(legacyId); - if (alias != null) - { - //check that the new property editor exists with that alias - var editor = PropertyEditorResolver.Current.GetByAlias(alias); - if (editor == null) - { - errorReport.Add(new Tuple(false, string.Format("Property Editor with ID '{0}' (assigned to Data Type '{1}') has a valid GUID -> Alias map but no property editor was found. It will be replaced with a Readonly/Label property editor.", item.controlId, item.text))); - } - } - else - { - errorReport.Add(new Tuple(false, string.Format("Property Editor with ID '{0}' (assigned to Data Type '{1}') does not have a valid GUID -> Alias map. It will be replaced with a Readonly/Label property editor.", item.controlId, item.text))); - } - } - - Report = errorReport; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.designer.cs deleted file mode 100644 index 443a42398a..0000000000 --- a/src/Umbraco.Web.UI/install/steps/UpgradeReport.ascx.designer.cs +++ /dev/null @@ -1,60 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class UpgradeReport { - - /// - /// MultiView1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.MultiView MultiView1; - - /// - /// View1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.View View1; - - /// - /// View2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.View View2; - - /// - /// ToggleView control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.MultiView ToggleView; - - /// - /// btnNext control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton btnNext; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.cs b/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.cs deleted file mode 100644 index 13dc4fb89a..0000000000 --- a/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.IO; -using Umbraco.Core.IO; -using Umbraco.Web.Install; -using umbraco; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class ValidatePermissions : StepUserControl - { - protected void Page_Load(object sender, EventArgs e) - { - var permissionsOk = true; - var packageOk = true; - var foldersOk = true; - var cacheOk = true; - var valResult = ""; - - // Test default dir permissions - foreach (var dir in FilePermissionHelper.PermissionDirs) - { - var result = SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt")); - - if (!result) - { - permissionsOk = false; - permSummary.Text += "
            • Directory: ./" + dir + "
            • "; - } - - // Print - valResult += " " + dir + " : " + SuccessOrFailure(result) + "!
              "; - } - - // Test default file permissions - foreach (var file in FilePermissionHelper.PermissionFiles) - { - var result = OpenFileForWrite(IOHelper.MapPath(file)); - if (!result) - { - permissionsOk = false; - permSummary.Text += "
            • File: " + file + "
            • "; - } - - // Print - valResult += " " + file + " : " + SuccessOrFailure(result) + "!
              "; - } - permissionResults.Text = valResult; - - // Test package dir permissions - string packageResult = ""; - foreach (var dir in FilePermissionHelper.PackagesPermissionsDirs) - { - var result = - SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt")); - if (!result) - { - packageOk = false; - permSummary.Text += "
            • Directory: " + dir + "
            • "; - } - - // Print - packageResult += " ./" + dir + " : " + SuccessOrFailure(result) + "!
              "; - } - packageResults.Text = packageResult; - - // Test umbraco.xml file - try - { - content.Instance.PersistXmlToFile(); - xmlResult.Text = "Success!"; - } - catch (Exception ee) - { - cacheOk = false; - xmlResult.Text = "Failed!"; - string tempFile = SystemFiles.ContentCacheXml; - - if (tempFile.Substring(0, 1) == "/") - tempFile = tempFile.Substring(1, tempFile.Length - 1); - - permSummary.Text += string.Format("
            • File ./{0}
              Error message: {1}
            • ", tempFile, ee); - } - - // Test creation of folders - try - { - string tempDir = IOHelper.MapPath(SystemDirectories.Media + "/testCreatedByConfigWizard"); - Directory.CreateDirectory(tempDir); - Directory.Delete(tempDir); - foldersResult.Text = "Success!"; - } - catch - { - foldersOk = false; - foldersResult.Text = "Failure!"; - } - - // update config files - if (permissionsOk) - { - foreach ( - var configFile in new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)).GetFiles("*.xml")) - { - try - { - if (File.Exists(configFile.FullName.Replace(".xml", ".config"))) - File.Delete(configFile.FullName.Replace(".xml", ".config")); - - configFile.MoveTo(configFile.FullName.Replace(".xml", ".config")); - } - catch { } - - } - } - - // Generate summary - howtoResolve.Visible = true; - if (permissionsOk && cacheOk && packageOk && foldersOk) - { - perfect.Visible = true; - howtoResolve.Visible = false; - } - else if (permissionsOk && cacheOk && foldersOk) - noPackages.Visible = true; - else if (permissionsOk && cacheOk) - { - folderWoes.Visible = true; - grant.Visible = false; - noFolders.Visible = true; - } - else - { - error.Visible = true; - if (!foldersOk) - folderWoes.Visible = true; - } - } - - private static string SuccessOrFailure(bool result) - { - return result ? "Success" : "Failure"; - } - - 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; - } - - } - - private bool OpenFileForWrite(string file) - { - try - { - File.AppendText(file).Close(); - } - catch - { - return false; - } - return true; - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.designer.cs deleted file mode 100644 index d595bc11f9..0000000000 --- a/src/Umbraco.Web.UI/install/steps/ValidatePermissions.ascx.designer.cs +++ /dev/null @@ -1,132 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class ValidatePermissions { - - /// - /// perfect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal perfect; - - /// - /// noPackages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal noPackages; - - /// - /// noFolders control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal noFolders; - - /// - /// error control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal error; - - /// - /// howtoResolve control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel howtoResolve; - - /// - /// grant control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel grant; - - /// - /// permSummary control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal permSummary; - - /// - /// folderWoes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel folderWoes; - - /// - /// permissionResults control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal permissionResults; - - /// - /// packageResults control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal packageResults; - - /// - /// xmlResult control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal xmlResult; - - /// - /// foldersResult control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal foldersResult; - - /// - /// btnNext control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton btnNext; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/Welcome.ascx.cs b/src/Umbraco.Web.UI/install/steps/Welcome.ascx.cs deleted file mode 100644 index ca95acda1f..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Welcome.ascx.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Umbraco.Core; -using umbraco; - -namespace Umbraco.Web.UI.Install.Steps -{ - public partial class Welcome : StepUserControl - { - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - if (!IsPostBack) - { - //clear the plugin cache when installation starts (just a safety check) - PluginManager.Current.ClearPluginCache(); - } - } - - protected void Page_Load(object sender, EventArgs e) - { - // Check for config! SD: I've moved this config check above the other stuff since there's no point in doing all that processing if we're - // just going to redirect if this setting is true. - if (GlobalSettings.Configured) - { - Response.Redirect(Request.QueryString["url"] ?? "/", true); - } - - var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema(); - var determinedVersion = result.DetermineInstalledVersion(); - - // Display the Umbraco upgrade message if Umbraco is already installed - if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus) == false || determinedVersion.Equals(new Version(0, 0, 0)) == false) - { - ph_install.Visible = false; - ph_upgrade.Visible = true; - } - - } - - - - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/Welcome.ascx.designer.cs b/src/Umbraco.Web.UI/install/steps/Welcome.ascx.designer.cs deleted file mode 100644 index 43350478db..0000000000 --- a/src/Umbraco.Web.UI/install/steps/Welcome.ascx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps { - - - public partial class Welcome { - - /// - /// ph_install control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_install; - - /// - /// ph_upgrade control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_upgrade; - - /// - /// btnNext control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton btnNext; - } -} diff --git a/src/Umbraco.Web.UI/install/steps/database.ascx b/src/Umbraco.Web.UI/install/steps/database.ascx deleted file mode 100644 index 75071ba979..0000000000 --- a/src/Umbraco.Web.UI/install/steps/database.ascx +++ /dev/null @@ -1,442 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" CodeBehind="database.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.Database" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> - - - -
              -
              -

              Database configuration

              -

              - To complete this step you will either need a blank database or, if you do not have a blank database available, choose the SQL CE 4 embedded - database (This is the recommended approach for first time users or if you are unsure). -

              -

              - If you are not using the SQL CE 4 embedded database you will need the connection details for your database, such as the - "connection string". You may need to contact your system administrator or web host for this information. -

              -
              - -
              -
              -
              -
              -

              - 1. Select which database option best fits you: -

              - -
                -
              • - - - -
              • -
              • - - - -
              • -
              • - - - -
              • -
              • - - - -
              • -
              - -
              -
              - -
              - - - -
              - - -
              -
              -

              - 2. Now choose your database type below. -

              -
              - - - - - - - -
              -
              -
              - - -
              -
              -

              - 3. Connection details: Please fill out the connection information for your database. -

              -
              - - - -
              -

              - - -

              -
              - -
              -
              - Server: - - -
              -
              - Database name: - - -
              - -
              - Integrated security: - -
              - -
              - Username: - - -
              -
              - Password: - - -
              - -
              -
              - - -
              -
              - - -
              -
              -
              - -

              - 2. Simple file-based database: -

              -
              - - - - -
              -
              -
              -
              - -
              -
              -
              -

              - 2. Connection details: Please fill out the connection information for your database. -

              - -
              - -
              - Connection string: - - -
              -
              -

              - Example: datalayer=MySQL;server=192.168.2.8;user id=user;password=***;database=umbraco -

              -
              - -
              -
              -
              -
              - - -
              -
              -
              -

              - 2. Getting a database setup for umbraco.
              - For first time users, we recommend you select "quick-and-simple embedded database". - This will install an easy to use database, that does - not require any additional software to use.
              - Alternatively, you can install Microsoft SQL Server, which will require a bit more - work to get up and running.
              - We have provided a step-by-step guide in the video instructions below. -

              - Open video instructions -
              -
              -
              - -
              -
               
              - install -
              - -
              - - - -
              -
              -
              - -
              - - - -
              -
              - -
              -

              Installing Umbraco

              -

              - The Umbraco database is being configured. This process populates your chosen database with a blank Umbraco instance. -

              -
              - -
              - -
              -

              Upgrading Umbraco

              -

              - The Umbraco database is being configured. This process upgrades your Umbraco database. -

              -
              - -
              -
              -
              -
              -
              - 0% -
              - -
              -
              - -
              -
               
              - Continue - -
              -
              - - - -
              \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/defaultUser.ascx b/src/Umbraco.Web.UI/install/steps/defaultUser.ascx deleted file mode 100644 index 4fec7e243c..0000000000 --- a/src/Umbraco.Web.UI/install/steps/defaultUser.ascx +++ /dev/null @@ -1,89 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="DefaultUser.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.DefaultUser" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> - - - -
              - -
              - -

              Create User

              -
              -

              You can now setup a new admin user to log into Umbraco, we recommend using a stong password for this (a password which is more than 4 characters and contains a mix of letters, numbers and symbols). - Please make a note of the chosen password.

              -

              The password can be changed once you have completed the installation and logged into the admin interface.

              -
              - -
              - -
              - -
              - -
              - -
              - -
              - -
              - Name: - - -
              - -
              - Email: - - -
              - -
              - Username: - - -
              - -
              - Password: - - - -
              - -
              - Confirm Password: - - - -
              - -
              - - Sign up for our monthly newsletter -
              -
              -
              -
              -
               
              - Create user -
              -
              -
              -
              -
              -
              - - diff --git a/src/Umbraco.Web.UI/install/steps/license.ascx b/src/Umbraco.Web.UI/install/steps/license.ascx deleted file mode 100644 index f8d8bccec3..0000000000 --- a/src/Umbraco.Web.UI/install/steps/license.ascx +++ /dev/null @@ -1,24 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="True" CodeBehind="license.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.License" %> - -
              -
              -

              License

              -
              -

              Accept the license for Umbraco CMS

              -

              By clicking the "accept and continue" button (or by modifying the Umbraco Configuration Status in the web.config), you accept the license for this software as specified in the text below.

              -
              -

              The License (MIT):

              -
              -

              Copyright (c) 2002 - <%=DateTime.Now.Year %> 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 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 NONINFRINGMENT. 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 USER OR OTHER DEALINGS IN THE SOFTWARE.

              -
              -

              Thats all. That didnt hurt did it?

              -
              - -
              -
               
              - Accept and Continue -
              -
              \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/renaming.ascx b/src/Umbraco.Web.UI/install/steps/renaming.ascx deleted file mode 100644 index 27bdcb41ce..0000000000 --- a/src/Umbraco.Web.UI/install/steps/renaming.ascx +++ /dev/null @@ -1,25 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="True" CodeBehind="Renaming.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.Renaming" %> -

              Step 3/5: Updating old conventions

              - -

              - This version of Umbraco introduces new conventions for naming XSLT and REST extensions as well as a renaming of the Public Access storage file.
              - You no longer need to prefix your extension references with /bin and the public access storage file is now called access.config instead of access.xml.
              - This step of the installer will try to update your old references. If it fails due to permission settings, you'll need to make these changes manually! -

              - - -

              Everything looks good. No changes needed for the upgrade, just press next.

              -
              - -

              -The following changes will need to be made. Press to update changes button to proceed: -

              - - -
              -
              - - - - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/theend.ascx b/src/Umbraco.Web.UI/install/steps/theend.ascx deleted file mode 100644 index 2f123741c6..0000000000 --- a/src/Umbraco.Web.UI/install/steps/theend.ascx +++ /dev/null @@ -1,63 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" CodeBehind="TheEnd.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.TheEnd" - TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> -<%@ Import Namespace="Umbraco.Core.IO" %> - - - - -
              -
              -

              Youre done...now what?

              -

              Excellent, you are now ready to start using Umbraco, one of the worlds most popular open source .NET CMS.

              - -
              -
              -
              -  
              -
              - - - -
              -
              -
              diff --git a/src/Umbraco.Web.UI/install/steps/validatePermissions.ascx b/src/Umbraco.Web.UI/install/steps/validatePermissions.ascx deleted file mode 100644 index 3d533a46a4..0000000000 --- a/src/Umbraco.Web.UI/install/steps/validatePermissions.ascx +++ /dev/null @@ -1,89 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="ValidatePermissions.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.ValidatePermissions" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> -

              Step 3/5: Validating File Permissions

              -

              -umbraco needs write/modify access to certain directories in order to store files like pictures and PDF's. -It also stores temporary data (aka: cache) for enhancing the performance of your website. -

              - - -
              -

              Your permission settings are perfect!

              -

              You are ready to run umbraco and install packages!

              -
              -
              - - -
              -

              Your permission settings are almost perfect!

              -

              -You can run umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of umbraco. -

              -
              -
              - - -
              -

              -Your permission settings might be an issue! -

              -

              You can run umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of umbraco.

              -
              -
              - - -
              -

              -Your permission settings are not ready for umbraco! -

              -

              -In order to run umbraco, you'll need to update your permission settings. -

              -
              -
              - - -

              How to Resolve

              - -
              - -

              Affected files and folders

              -

              - You need to grant ASP.NET modify permissions to the following files/folders: -

              - -
                - -
              -
              - - -

              Resolving folder issue

              - -

              Follow this link for more information on problems with ASP.NET and creating - folders. -

              -
              - - -

              View Permission Error Details:

              - -
              -
              - -
              -
               
              - Continue anyway -
              \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/welcome.ascx b/src/Umbraco.Web.UI/install/steps/welcome.ascx deleted file mode 100644 index 6cd504a364..0000000000 --- a/src/Umbraco.Web.UI/install/steps/welcome.ascx +++ /dev/null @@ -1,55 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" CodeBehind="Welcome.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.Welcome" - TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> - -
              -
              - - - -

              - Welcome to the Umbraco installation

              -

              - Thanks for downloading the Umbraco CMS installer. -

              -

              - You are just a few minutes away from getting up and running. The installer will - take you through the following process:-

              -
                -
              • 1.Accept the easy to read License.
              • -
              • 2.Set up a database. There are a number of options available - such as MS SQL Server, MS SQL Express Edition and MYSQL or you may wish to use the - Microsoft SQL CE 4 database. You may need to consult your web host or system administrator.
              • -
              • 3.Set an Umbraco Admin password.
              • -
              • 4.You can then choose to install one of our great starter - kits and a skin.
              • -
              • 5.But whatever you do don't forget to become part of the Umbraco community, one of the friendliest developer communities you will find. Its what makes Umbraco such a great product and so much fun to use.
              • -
              - Enjoy! -
              - - -

              Upgrading Umbraco

              -

              - Welcome to the umbraco upgrade wizard. This will make sure that you upgrade safely from your old version to Umbraco version <%=UmbracoVersion.Current.ToString(3) %> <%=UmbracoVersion.CurrentComment %> -

              -

              - As this is an upgrade, the wizard might skip steps that are only needed for new umbraco installations. It might also ask you questions you've already answered once. But do not worry, - everything is in order. Click Let's get started below to begin your upgrade. -

              - Enjoy! -
              - -
              - -
              -
               
              - Let's get started! -
              - -
              - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 87da8738ae..250a9ef252 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -1,9 +1,11 @@  - + - + + + @@ -15,14 +17,15 @@ + - + - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx new file mode 100644 index 0000000000..fd9c5182f2 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx @@ -0,0 +1,23 @@ +<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> +<%-- + This page is required because we cannot reload the angular app with a changed Hash since it just detects the hash and doesn't reload. + So this is used purely for a full reload of an angular app with a changed hash. +--%> + + + + Redirecting... + + + + Redirecting... + + diff --git a/src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.cs b/src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.cs similarity index 78% rename from src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.cs rename to src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.cs index c27ce02c2f..013cd28c00 100644 --- a/src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.cs @@ -1,104 +1,106 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using Umbraco.Core.Logging; -using Umbraco.Web.Install; - -namespace Umbraco.Web.UI.Install.Steps.Skinning -{ - public delegate void StarterKitInstalledEventHandler(); - - public partial class LoadStarterKits : StepUserControl - { - /// - /// Returns the string for the package installer web service base url - /// - protected string PackageInstallServiceBaseUrl { get; private set; } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - //Get the URL for the package install service base url - var umbracoPath = Core.Configuration.GlobalSettings.UmbracoMvcArea; - var urlHelper = new UrlHelper(Context.Request.RequestContext); - PackageInstallServiceBaseUrl = urlHelper.Action("Index", "InstallPackage", new { area = umbracoPath }); - } - - /// - /// Flag to show if we can connect to the repo or not - /// - protected bool CannotConnect { get; private set; } - - public event StarterKitInstalledEventHandler StarterKitInstalled; - - protected virtual void OnStarterKitInstalled() - { - StarterKitInstalled(); - } - - - private readonly global::umbraco.cms.businesslogic.packager.repositories.Repository _repo; - private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; - - public LoadStarterKits() - { - _repo = global::umbraco.cms.businesslogic.packager.repositories.Repository.getByGuid(RepoGuid); - } - - protected void Page_Load(object sender, EventArgs e) - { - - } - - protected void NextStep(object sender, EventArgs e) - { - var p = (Default)this.Page; - InstallHelper.RedirectToNextStep(Page, Request.GetItemAsString("installStep")); - } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - if (_repo == null) - { - throw new InvalidOperationException("Could not find repository with id " + RepoGuid); - } - - //clear progressbar cache - InstallHelper.ClearProgress(); - - if (_repo.HasConnection()) - { - try - { - var r = new org.umbraco.our.Repository(); - - rep_starterKits.DataSource = r.Modules(); - rep_starterKits.DataBind(); - } - catch (Exception ex) - { - LogHelper.Error("Cannot connect to package repository", ex); - CannotConnect = true; - - } - } - else - { - CannotConnect = true; - } - } - - - protected void GotoLastStep(object sender, EventArgs e) - { - InstallHelper.RedirectToLastStep(Page); - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using System.Web.UI; +using Umbraco.Core.Logging; +using Umbraco.Web.Install; + +namespace Umbraco.Web.UI.Install.Steps.Skinning +{ + public delegate void StarterKitInstalledEventHandler(); + + public partial class LoadStarterKits : UserControl + { + /// + /// Returns the string for the package installer web service base url + /// + protected string PackageInstallServiceBaseUrl { get; private set; } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + //Get the URL for the package install service base url + var umbracoPath = Core.Configuration.GlobalSettings.UmbracoMvcArea; + var urlHelper = new UrlHelper(Context.Request.RequestContext); + //PackageInstallServiceBaseUrl = urlHelper.Action("Index", "InstallPackage", new { area = "UmbracoInstall" }); + PackageInstallServiceBaseUrl = urlHelper.GetUmbracoApiService("Index", "InstallPackage", "UmbracoInstall"); + } + + /// + /// Flag to show if we can connect to the repo or not + /// + protected bool CannotConnect { get; private set; } + + public event StarterKitInstalledEventHandler StarterKitInstalled; + + protected virtual void OnStarterKitInstalled() + { + StarterKitInstalled(); + } + + + private readonly global::umbraco.cms.businesslogic.packager.repositories.Repository _repo; + private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; + + public LoadStarterKits() + { + _repo = global::umbraco.cms.businesslogic.packager.repositories.Repository.getByGuid(RepoGuid); + } + + protected void Page_Load(object sender, EventArgs e) + { + + } + + //protected void NextStep(object sender, EventArgs e) + //{ + // var p = (Default)this.Page; + // //InstallHelper.RedirectToNextStep(Page, Request.GetItemAsString("installStep")); + //} + + protected override void OnInit(EventArgs e) + { + base.OnInit(e); + + if (_repo == null) + { + throw new InvalidOperationException("Could not find repository with id " + RepoGuid); + } + + //clear progressbar cache + //InstallHelper.ClearProgress(); + + if (_repo.HasConnection()) + { + try + { + var r = new org.umbraco.our.Repository(); + + rep_starterKits.DataSource = r.Modules(); + rep_starterKits.DataBind(); + } + catch (Exception ex) + { + LogHelper.Error("Cannot connect to package repository", ex); + CannotConnect = true; + + } + } + else + { + CannotConnect = true; + } + } + + + protected void GotoLastStep(object sender, EventArgs e) + { + //InstallHelper.RedirectToLastStep(Page); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.designer.cs b/src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.designer.cs similarity index 97% rename from src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.designer.cs rename to src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.designer.cs index 3af8ec681c..f9f25ad3a1 100644 --- a/src/Umbraco.Web.UI/install/steps/Skinning/LoadStarterKits.ascx.designer.cs +++ b/src/Umbraco.Web.UI/umbraco/Install/Legacy/LoadStarterKits.ascx.designer.cs @@ -1,60 +1,60 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Install.Steps.Skinning { - - - public partial class LoadStarterKits { - - /// - /// pl_loadStarterKits control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder pl_loadStarterKits; - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// rep_starterKits control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Repeater rep_starterKits; - - /// - /// LinkButton1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton LinkButton1; - - /// - /// LinkButton2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton LinkButton2; - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Umbraco.Web.UI.Install.Steps.Skinning { + + + public partial class LoadStarterKits { + + /// + /// pl_loadStarterKits control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder pl_loadStarterKits; + + /// + /// JsInclude1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; + + /// + /// rep_starterKits control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Repeater rep_starterKits; + + /// + /// LinkButton1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.LinkButton LinkButton1; + + /// + /// LinkButton2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.LinkButton LinkButton2; + } +} diff --git a/src/Umbraco.Web.UI/install/steps/Skinning/loadStarterKits.ascx b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx similarity index 75% rename from src/Umbraco.Web.UI/install/steps/Skinning/loadStarterKits.ascx rename to src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx index 586162a2fd..aadd444a42 100644 --- a/src/Umbraco.Web.UI/install/steps/Skinning/loadStarterKits.ascx +++ b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx @@ -1,98 +1,98 @@ -<%@ Control Language="C#" AutoEventWireup="True" CodeBehind="LoadStarterKits.ascx.cs" Inherits="Umbraco.Web.UI.Install.Steps.Skinning.LoadStarterKits" %> -<%@ Import Namespace="Umbraco.Web.org.umbraco.our" %> - -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - - <% if (!CannotConnect) { %> - - <% } %> - - - - - - No thanks, do not install a starterkit! - - - - - - -
              "> - -
              -

              Oops...the installer can't connect to the repository

              - Starter Kits could not be fetched from the repository as there was no connection - which can occur if you are using a proxy server or firewall with certain configurations, - or if you are not currently connected to the internet. -
              - Click Continue to complete the installation then navigate to the Developer section of your Umbraco installation - where you will find the Starter Kits listed in the Packages tree. -
              - - -
              -
               
              - Continue -
              - -
              - - - - + + - + + - + - + - + - +
              +
              Search for a regular expression
              - +
              - \ No newline at end of file + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs b/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs index 08ef23ffe5..fbf30a776d 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs @@ -18,7 +18,8 @@ namespace Umbraco.Web.UI.Umbraco.Controls umbPasswordChanger_passwordCurrent.Text = null; umbPasswordChanger_passwordNew.Text = null; umbPasswordChanger_passwordNewConfirm.Text = null; - + //reset the flag always + IsChangingPasswordField.Value = "false"; this.DataBind(); } diff --git a/src/Umbraco.Web.UI/umbraco/controls/Tree/TreeControl.ascx b/src/Umbraco.Web.UI/umbraco/controls/Tree/TreeControl.ascx index b62fda5481..2b932bd027 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/Tree/TreeControl.ascx +++ b/src/Umbraco.Web.UI/umbraco/controls/Tree/TreeControl.ascx @@ -1,23 +1,15 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TreeControl.ascx.cs" Inherits="umbraco.controls.Tree.TreeControl" %> +<%@ Control Language="C#" AutoEventWireup="true" Inherits="umbraco.controls.Tree.TreeControl" %> <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> - - - - - - - - - - - - - + + + + + @@ -34,6 +36,7 @@ +

              @@ -93,6 +96,7 @@ +
              @@ -102,6 +106,7 @@ notifies you the install is completed.

              + @@ -120,8 +125,8 @@
              -

              - Binary files in the package!

              +

              Binary files in the package!

              + Read more...

              @@ -139,6 +144,7 @@

              +
              @@ -157,6 +163,7 @@

              +

              @@ -197,6 +204,7 @@

              +

              @@ -218,6 +226,7 @@

              +

              @@ -239,6 +248,7 @@

              +
              - - - - - - + + <%=umbraco.ui.Text("paLoginPageHelp")%> + +
              +
              +
              + + + + +
              + + <%=umbraco.ui.Text("paErrorPageHelp")%> + +
              +
              + +
              + + + + + + +
              - - - - \ No newline at end of file + + + + diff --git a/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoDialog.master.designer.cs b/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoDialog.master.designer.cs index b07d0a35c9..66f2c928bd 100644 --- a/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoDialog.master.designer.cs +++ b/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoDialog.master.designer.cs @@ -20,23 +20,5 @@ namespace Umbraco.Web.UI.Umbraco.Masterpages { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.ContentPlaceHolder DocType; - - /// - /// JsInclude11 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude11; - - /// - /// JsInclude12 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude12; } } diff --git a/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoPage.master.designer.cs b/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoPage.master.designer.cs index 2cc35025cf..52f17f5672 100644 --- a/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoPage.master.designer.cs +++ b/src/Umbraco.Web.UI/umbraco/masterpages/UmbracoPage.master.designer.cs @@ -20,32 +20,5 @@ namespace Umbraco.Web.UI.Umbraco.Masterpages { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.ContentPlaceHolder DocType; - - /// - /// JsInclude10 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude10; - - /// - /// JsInclude11 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude11; - - /// - /// JsInclude12 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude12; } } diff --git a/src/Umbraco.Web.UI/umbraco/masterpages/umbracoDialog.Master b/src/Umbraco.Web.UI/umbraco/masterpages/umbracoDialog.Master index 4b7150938e..f1474e915c 100644 --- a/src/Umbraco.Web.UI/umbraco/masterpages/umbracoDialog.Master +++ b/src/Umbraco.Web.UI/umbraco/masterpages/umbracoDialog.Master @@ -1,7 +1,12 @@ -<%@ Master Language="C#" AutoEventWireup="True" CodeBehind="UmbracoDialog.master.cs" Inherits="Umbraco.Web.UI.Umbraco.Masterpages.UmbracoDialog" %> +<%@ Master Language="C#" AutoEventWireup="True" CodeBehind="UmbracoDialog.master.cs" Inherits="Umbraco.Web.UI.Umbraco.Masterpages.UmbracoDialog" %> + + + + <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -12,12 +17,10 @@ - - - - - - + + + + diff --git a/src/Umbraco.Web.UI/umbraco/masterpages/umbracoPage.Master b/src/Umbraco.Web.UI/umbraco/masterpages/umbracoPage.Master index 192080e476..ece621ec85 100644 --- a/src/Umbraco.Web.UI/umbraco/masterpages/umbracoPage.Master +++ b/src/Umbraco.Web.UI/umbraco/masterpages/umbracoPage.Master @@ -1,8 +1,12 @@ -<%@ Master Language="C#" AutoEventWireup="True" CodeBehind="UmbracoPage.master.cs" - Inherits="Umbraco.Web.UI.Umbraco.Masterpages.UmbracoPage" %> +<%@ Master Language="C#" AutoEventWireup="True" CodeBehind="UmbracoPage.master.cs" Inherits="Umbraco.Web.UI.Umbraco.Masterpages.UmbracoPage" %> + + + + <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -11,16 +15,11 @@ - - - - - - - - - - + + + + + diff --git a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx index 7d386232d1..6388b243ee 100644 --- a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx +++ b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx @@ -4,6 +4,7 @@ <%@ Register TagPrefix="umb2" TagName="Tree" Src="../../controls/Tree/TreeControl.ascx" %> <%@ Register TagPrefix="umb3" TagName="Image" Src="../../controls/Images/ImageViewer.ascx" %> <%@ Register TagName="MediaUpload" TagPrefix="umb4" Src="../../controls/Images/UploadMediaImage.ascx" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -14,7 +15,10 @@ - + + + + diff --git a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx index 24de8c77b9..a55780b7c3 100644 --- a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx +++ b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx @@ -2,6 +2,7 @@ <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register Src="../../controls/Tree/TreeControl.ascx" TagName="TreeControl" TagPrefix="umbraco" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -14,14 +15,18 @@ - + + + + + - - - -
              -
              -
              -
              \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.cs index f31b395079..ff16b6ef3a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.cs @@ -64,6 +64,7 @@ namespace umbraco.controls.Tree private TreeMode m_TreeType = TreeMode.Standard; private bool m_IsInit = false; private TreeService m_TreeService = new TreeService(); + private string m_SelectedNodePath; #region Public Properties @@ -102,6 +103,15 @@ namespace umbraco.controls.Tree } } + public string SelectedNodePath + { + get { return m_SelectedNodePath; } + set + { + m_SelectedNodePath = value; + } + } + public string TreeType { get { return m_TreeService.TreeType; } @@ -418,5 +428,149 @@ namespace umbraco.controls.Tree //if everything is null then return the default app return DEFAULT_APP; } + + /// + /// CssInclude2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.CssInclude CssInclude2; + + /// + /// CssInclude3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.CssInclude CssInclude3; + + /// + /// CssInclude1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.CssInclude CssInclude1; + + /// + /// JsInclude1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; + + /// + /// JsInclude2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; + + /// + /// JsInclude3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude3; + + /// + /// JsInclude4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude4; + + /// + /// JsInclude5 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude5; + + /// + /// JsInclude6 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude6; + + /// + /// JsInclude8 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude8; + + /// + /// JsInclude11 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude11; + + /// + /// JsInclude7 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude7; + + /// + /// JsInclude12 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude12; + + /// + /// JsInclude9 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude9; + + /// + /// JsInclude10 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude10; + + /// + /// TreeContainer control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlGenericControl TreeContainer; } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.designer.cs deleted file mode 100644 index 829bf5312d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/Tree/TreeControl.ascx.designer.cs +++ /dev/null @@ -1,159 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.controls.Tree { - - - public partial class TreeControl { - - /// - /// CssInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.CssInclude CssInclude2; - - /// - /// CssInclude3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.CssInclude CssInclude3; - - /// - /// CssInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.CssInclude CssInclude1; - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// JsInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; - - /// - /// JsInclude3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude3; - - /// - /// JsInclude4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude4; - - /// - /// JsInclude5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude5; - - /// - /// JsInclude6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude6; - - /// - /// JsInclude8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude8; - - /// - /// JsInclude11 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude11; - - /// - /// JsInclude7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude7; - - /// - /// JsInclude12 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude12; - - /// - /// JsInclude9 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude9; - - /// - /// JsInclude10 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude10; - - /// - /// TreeContainer control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl TreeContainer; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/passwordChanger.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/passwordChanger.ascx.cs index f19afe4f26..c7e5aedc5a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/passwordChanger.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/passwordChanger.ascx.cs @@ -15,7 +15,7 @@ using Umbraco.Web.Models; namespace umbraco.controls { - public partial class passwordChanger : System.Web.UI.UserControl + public partial class passwordChanger : UserControl { public string MembershipProviderName { get; set; } @@ -38,9 +38,12 @@ namespace umbraco.controls var umbProvider = Provider as MembershipProviderBase; if (umbProvider != null && umbProvider.AllowManuallyChangingPassword) { - return false; + _showOldPassword = false; + } + else + { + _showOldPassword = Provider.EnablePasswordRetrieval == false; } - _showOldPassword = Provider.EnablePasswordRetrieval == false; } return _showOldPassword.Value; } @@ -75,9 +78,9 @@ namespace umbraco.controls get { return ChangingPasswordModel.NewPassword; } } - protected override void OnInit(EventArgs e) + protected override void OnLoad(EventArgs e) { - base.OnInit(e); + base.OnLoad(e); if (Membership.Providers[MembershipProviderName] == null) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/DLRScripting.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/DLRScripting.ascx.cs index 987f66e43e..1b8a29ee56 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/DLRScripting.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/DLRScripting.ascx.cs @@ -37,6 +37,24 @@ namespace umbraco.presentation.create LoadTemplates(template, filetype.SelectedValue); } + protected void MacroExistsValidator_OnServerValidate(object source, ServerValidateEventArgs args) + { + if (createMacro.Checked) + { + //TODO: Shouldn't this use our string functions to create the alias ? + var fileName = rename.Text + "." + filetype.SelectedValue; + var name = fileName + .Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') + .SplitPascalCasing().ToFirstUpperInvariant(); + + var macro = ApplicationContext.Current.Services.MacroService.GetByAlias(name); + if (macro != null) + { + args.IsValid = false; + } + } + } + protected void sbmt_Click(object sender, System.EventArgs e) { if (Page.IsValid) @@ -90,6 +108,8 @@ namespace umbraco.presentation.create } } + protected global::System.Web.UI.WebControls.CustomValidator MacroExistsValidator; + /// /// rename control. /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs index 8104c41004..7bde1e2b9a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs @@ -1,4 +1,6 @@ -using System.IO; +using System; +using System.IO; +using System.Text.RegularExpressions; using System.Web; using Umbraco.Core.CodeAnnotations; using Umbraco.Core.IO; @@ -17,7 +19,10 @@ namespace umbraco [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] public class PartialViewMacroTasks : LegacyDialogTask { + private const string CodeHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; private string _returnUrl = ""; + private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled); + protected virtual string EditViewFile { @@ -54,14 +59,23 @@ namespace umbraco { using (var templateFile = File.OpenText(IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/" + template))) { - var templateContent = templateFile.ReadToEnd(); - sw.Write(templateContent); + var templateContent = templateFile.ReadToEnd().Trim(); + + //strip the @inherits if it's there + templateContent = _headerMatch.Replace(templateContent, string.Empty); + + sw.Write( + "{0}{1}{2}", + CodeHeader, + Environment.NewLine, + templateContent); } } // Create macro? if (ParentID == 1) { + //TODO: Shouldn't this use our string functions to create the alias ? var name = fileName .Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') .SplitPascalCasing().ToFirstUpperInvariant(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs index 3078c46a03..f4722da86b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs @@ -1,4 +1,6 @@ -using System.IO; +using System; +using System.IO; +using System.Text.RegularExpressions; using System.Web; using Umbraco.Core.CodeAnnotations; using Umbraco.Core.IO; @@ -17,7 +19,10 @@ namespace umbraco [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] public class PartialViewTasks : LegacyDialogTask { + private const string CodeHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; + private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled); + protected virtual string EditViewFile { @@ -52,10 +57,24 @@ namespace umbraco //create the file using (var sw = File.CreateText(fullFilePath)) { - using (var templateFile = File.OpenText(IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/" + template))) + var templatePathAttempt = TryGetTemplatePath(template); + if (templatePathAttempt.Success == false) { - var templateContent = templateFile.ReadToEnd(); - sw.Write(templateContent); + throw new InvalidOperationException("Could not load template with name " + template); + } + + using (var templateFile = File.OpenText(templatePathAttempt.Result)) + { + var templateContent = templateFile.ReadToEnd().Trim(); + + //strip the @inherits if it's there + templateContent = _headerMatch.Replace(templateContent, string.Empty); + + sw.Write( + "{0}{1}{2}", + CodeHeader, + Environment.NewLine, + templateContent); } } @@ -107,5 +126,26 @@ namespace umbraco get { return DefaultApps.settings.ToString(); } } + + /// + /// Looks first in the Partial View template folder and then in the macro partials template folder if not found + /// + /// + /// + private Attempt TryGetTemplatePath(string fileName) + { + var templatePath = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/" + fileName); + + if (File.Exists(templatePath)) + { + return Attempt.Succeed(templatePath); + } + + templatePath = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/" + fileName); + + return File.Exists(templatePath) + ? Attempt.Succeed(templatePath) + : Attempt.Fail(); + } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/ScriptTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/ScriptTasks.cs index 0a4b2dc1d3..f0d14929b3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/ScriptTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/ScriptTasks.cs @@ -1,6 +1,8 @@ using Umbraco.Core.IO; using Umbraco.Web.UI; +using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models; using umbraco.BusinessLogic; using umbraco.BasePages; @@ -11,44 +13,43 @@ namespace umbraco public override bool PerformSave() { - string[] scriptFileAr = Alias.Split('\u00A4'); - + var scriptFileAr = Alias.Split('\u00A4'); + var relPath = scriptFileAr[0]; var fileName = scriptFileAr[1]; var fileType = scriptFileAr[2]; - + var createFolder = ParentID; - var basePath = IOHelper.MapPath(SystemDirectories.Scripts + "/" + relPath + fileName); - if (System.IO.File.Exists(basePath)) + if (createFolder == 1) + { + ApplicationContext.Current.Services.FileService.CreateScriptFolder(relPath + fileName); + return true; + } + + var found = ApplicationContext.Current.Services.FileService.GetScriptByName(relPath + fileName + "." + fileType); + if (found != null) { _returnUrl = string.Format("settings/scripts/editScript.aspx?file={0}{1}.{2}", relPath, fileName, fileType); return true; } - if (createFolder == 1) - { - System.IO.Directory.CreateDirectory(basePath); - } - else - { - System.IO.File.Create(basePath + "." + fileType).Close(); - _returnUrl = string.Format("settings/scripts/editScript.aspx?file={0}{1}.{2}", relPath, fileName, - fileType); - } + ApplicationContext.Current.Services.FileService.SaveScript(new Script(relPath + fileName + "." + fileType)); + _returnUrl = string.Format("settings/scripts/editScript.aspx?file={0}{1}.{2}", relPath, fileName, fileType); return true; } public override bool PerformDelete() { - var path = IOHelper.MapPath(SystemDirectories.Scripts + "/" + Alias.TrimStart('/')); - - if (System.IO.File.Exists(path)) - System.IO.File.Delete(path); - else if (System.IO.Directory.Exists(path)) - System.IO.Directory.Delete(path, true); - - LogHelper.Info(string.Format("{0} Deleted by user {1}", Alias, User.Id)); + if (Alias.Contains(".") == false) + { + //there is no extension so we'll assume it's a folder + ApplicationContext.Current.Services.FileService.DeleteScriptFolder(Alias.TrimStart('/')); + } + else + { + ApplicationContext.Current.Services.FileService.DeleteScript(Alias.TrimStart('/')); + } return true; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs index 795be90741..a003f210bc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/content.ascx.cs @@ -43,7 +43,8 @@ namespace umbraco.cms.presentation.create.controls { string docDescription = "No description available..."; if (string.IsNullOrEmpty(dt.Description) == false) - docDescription = dt.Description; + docDescription = System.Web.HttpUtility.HtmlEncode(dt.Description); + docDescription = "" + dt.Text + "
              " + docDescription.Replace(Environment.NewLine, "
              "); docDescription = docDescription.Replace("'", "\\'"); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx deleted file mode 100644 index 272b618d1d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx +++ /dev/null @@ -1,11 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> - - -
              <%=umbraco.ui.Text("choose")%> <%=umbraco.ui.Text("language")%>:
              - -
              - -
              -<%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -
              diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs index 176fc1e46f..5799f932e1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Globalization; using System.Web; @@ -16,20 +16,19 @@ namespace umbraco.cms.presentation.create.controls ///
    public partial class language : UserControl { - private Language[] m_langs = Language.getAll; - - protected void Page_Load(object sender, EventArgs e) { // get all existing languages + pp1.Text = ui.Text("choose") + " " + ui.Text("language"); sbmt.Text = ui.Text("create"); + SortedList sortedCultures = new SortedList(); Cultures.Items.Clear(); Cultures.Items.Add(new ListItem(ui.Text("choose") + "...", "")); foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) { - sortedCultures.Add(ci.DisplayName + "|||" + Guid.NewGuid().ToString(), ci.Name); + sortedCultures.Add(ci.DisplayName + "|||" + Guid.NewGuid().ToString(), ci.Name); } IDictionaryEnumerator ide = sortedCultures.GetEnumerator(); @@ -40,37 +39,6 @@ namespace umbraco.cms.presentation.create.controls } } - private bool languageExists(string culture) - { - foreach (Language l in m_langs) - { - if (l.CultureAlias == culture) - return true; - } - return false; - } - - #region Web Form Designer generated code - - protected override void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - protected void sbmt_Click(object sender, EventArgs e) { LegacyDialogHandler.Create( @@ -80,10 +48,47 @@ namespace umbraco.cms.presentation.create.controls -1, Cultures.SelectedValue); - BasePage.Current.ClientTools - .ChildNodeCreated() - .CloseModalWindow(); + BasePage.Current.ClientTools + .ChildNodeCreated() + .CloseModalWindow(); } + + /// + /// pp1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp1; + + /// + /// Cultures control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList Cultures; + + /// + /// RequiredFieldValidator1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; + + /// + /// sbmt control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button sbmt; + } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs deleted file mode 100644 index 40ed65c41f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs +++ /dev/null @@ -1,34 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.4200 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.create.controls { - - - public partial class language { - - /// - /// Cultures control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList Cultures; - - /// - /// sbmt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button sbmt; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs index 9286a953f7..871c48e09d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs @@ -1,4 +1,6 @@ using Umbraco.Web.UI; +using System.Globalization; +using Umbraco.Core.Security; namespace umbraco.cms.presentation.create.controls { @@ -22,12 +24,14 @@ namespace umbraco.cms.presentation.create.controls protected void Page_Load(object sender, System.EventArgs e) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + sbmt.Text = ui.Text("create"); - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (provider.IsUmbracoMembershipProvider()) { nameLiteral.Text = ui.Text("name"); memberChooser.Attributes.Add("style", "padding-top: 10px"); - foreach (cms.businesslogic.member.MemberType dt in cms.businesslogic.member.MemberType.GetAll) + foreach (var dt in MemberType.GetAll) { ListItem li = new ListItem(); li.Text = dt.Text; @@ -41,9 +45,14 @@ namespace umbraco.cms.presentation.create.controls memberChooser.Visible = false; } - string[] pwRules = { Membership.MinRequiredPasswordLength.ToString(), Membership.MinRequiredNonAlphanumericCharacters.ToString() }; + string[] pwRules = + { + provider.MinRequiredPasswordLength.ToString(CultureInfo.InvariantCulture), + provider.MinRequiredNonAlphanumericCharacters.ToString(CultureInfo.InvariantCulture) + }; + PasswordRules.Text = PasswordRules.Text = ui.Text( - "errorHandling", "", pwRules, BasePages.UmbracoEnsuredPage.CurrentUser); + "errorHandling", "", pwRules, UmbracoEnsuredPage.CurrentUser); if (!IsPostBack) { @@ -55,7 +64,7 @@ namespace umbraco.cms.presentation.create.controls emailExistsCheck.ErrorMessage = ui.Text("errorHandling", "errorExistsWithoutTab", "E-mail", BasePages.UmbracoEnsuredPage.CurrentUser); memberTypeRequired.ErrorMessage = ui.Text("errorHandling", "errorMandatoryWithoutTab", "Member Type", BasePages.UmbracoEnsuredPage.CurrentUser); Password.Text = - Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters); + Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters); } @@ -63,26 +72,6 @@ namespace umbraco.cms.presentation.create.controls } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - protected void sbmt_Click(object sender, System.EventArgs e) { if (Page.IsValid) @@ -127,10 +116,17 @@ namespace umbraco.cms.presentation.create.controls /// protected void EmailExistsCheck(object sender, ServerValidateEventArgs e) { - if (Email.Text != "" && Member.GetMemberFromEmail(Email.Text.ToLower()) != null && Membership.Providers[Member.UmbracoMemberProviderName].RequiresUniqueEmail) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (Email.Text != "" && Member.GetMemberFromEmail(Email.Text.ToLower()) != null && provider.RequiresUniqueEmail) e.IsValid = false; else e.IsValid = true; } + + protected void EmailValidator_OnServerValidate(object source, ServerValidateEventArgs args) + { + args.IsValid = MembershipProviderBase.IsEmailValid(args.Value); + } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.designer.cs index 6af3644064..62289980ca 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.designer.cs @@ -11,7 +11,16 @@ namespace umbraco.cms.presentation.create.controls { public partial class member { - + + /// + /// EmailValidator control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator EmailValidator; + /// /// validationSummary control. /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs index d09e93b85f..2e03036fa5 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs @@ -4,6 +4,7 @@ using System.Web; using System.Globalization; using System.Web.Security; using Umbraco.Web.UI; +using Umbraco.Core.Security; using umbraco.BusinessLogic; using umbraco.DataLayer; using umbraco.BasePages; @@ -63,17 +64,29 @@ namespace umbraco var email = nameAndMail.Length > 0 ? nameAndMail[1] : ""; var password = nameAndMail.Length > 1 ? nameAndMail[2] : ""; var loginName = nameAndMail.Length > 2 ? nameAndMail[3] : ""; - if (Member.InUmbracoMemberMode() && TypeID != -1) + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider() && TypeID != -1) { - var dt = new MemberType(TypeID); - var provider = (providers.members.UmbracoMembershipProvider) Membership.Provider; + var dt = new MemberType(TypeID); + var castedProvider = (UmbracoMembershipProviderBase)provider; MembershipCreateStatus status; + + //First create with the membership provider //TODO: We are not supporting q/a - passing in empty here - var created = provider.CreateUser(dt.Alias, + var created = castedProvider.CreateUser(dt.Alias, loginName.Replace(" ", "").ToLower(), //dunno why we're doing this but that's how it has been so i'll leave it i guess password, email, "", "", true, Guid.NewGuid(), out status); + if (status != MembershipCreateStatus.Success) + { + throw new Exception("Error creating Member: " + status); + } + //update the name var member = Member.GetMemberFromLoginName(created.UserName); + member.Text = name; + member.Save(); var e = new NewMemberUIEventArgs(); this.OnNewMember(e, password, member); @@ -82,8 +95,8 @@ namespace umbraco } else { - var mc = new MembershipCreateStatus(); - Membership.CreateUser(name, password, email, "empty", "empty", true, out mc); + MembershipCreateStatus mc; + provider.CreateUser(name, password, email, "empty", "empty", true, null, out mc); if (mc != MembershipCreateStatus.Success) { throw new Exception("Error creating Member: " + mc); @@ -96,9 +109,10 @@ namespace umbraco public override bool PerformDelete() { - var u = Membership.GetUser(Alias); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var u = provider.GetUser(Alias, false); if (u == null) return false; - Membership.DeleteUser(u.UserName, true); + provider.DeleteUser(u.UserName, true); return true; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs index ba73303dfb..3cb32f364b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs @@ -1,5 +1,7 @@ +using System.Globalization; using Umbraco.Core; using Umbraco.Web.UI; +using Umbraco.Web; namespace umbraco.cms.presentation.create.controls { @@ -20,21 +22,21 @@ namespace umbraco.cms.presentation.create.controls { - protected void Page_Load(object sender, System.EventArgs e) + protected void Page_Load(object sender, EventArgs e) { sbmt.Text = ui.Text("create"); pp_name.Text = ui.Text("name"); if (!IsPostBack) { - string nodeId = umbraco.helper.Request("nodeId"); + string nodeId = Request.GetItemAsString("nodeId"); if (String.IsNullOrEmpty(nodeId) || nodeId == "init") { masterType.Items.Add(new ListItem(ui.Text("none") + "...", "0")); - foreach (cms.businesslogic.web.DocumentType dt in cms.businesslogic.web.DocumentType.GetAllAsList()) + foreach (DocumentType dt in DocumentType.GetAllAsList()) { // if (dt.MasterContentType == 0) - masterType.Items.Add(new ListItem(dt.Text, dt.Id.ToString())); + masterType.Items.Add(new ListItem(dt.Text, dt.Id.ToString(CultureInfo.InvariantCulture))); } } else @@ -58,7 +60,7 @@ namespace umbraco.cms.presentation.create.controls e.IsValid = false; } - protected void sbmt_Click(object sender, System.EventArgs e) + protected void sbmt_Click(object sender, EventArgs e) { if (Page.IsValid) { @@ -67,12 +69,12 @@ namespace umbraco.cms.presentation.create.controls createTemplateVal = 1; // check master type - var masterTypeVal = String.IsNullOrEmpty(umbraco.helper.Request("nodeId")) || umbraco.helper.Request("nodeId") == "init" ? masterType.SelectedValue : umbraco.helper.Request("nodeId"); + string masterTypeVal = String.IsNullOrEmpty(Request.GetItemAsString("nodeId")) || Request.GetItemAsString("nodeId") == "init" ? masterType.SelectedValue : Request.GetItemAsString("nodeId"); var returnUrl = LegacyDialogHandler.Create( new HttpContextWrapper(Context), BasePage.Current.getUser(), - helper.Request("nodeType"), + Request.GetItemAsString("nodeType"), createTemplateVal, rename.Text, int.Parse(masterTypeVal)); @@ -85,24 +87,5 @@ namespace umbraco.cms.presentation.create.controls } } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx b/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx deleted file mode 100644 index d09d4189b3..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx +++ /dev/null @@ -1,18 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="simple.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.simple" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - * - - - - - - -
    public partial class simple : UmbracoUserControl - { + { - protected void Page_Load(object sender, System.EventArgs e) - { - sbmt.Text = ui.Text("create"); + protected void Page_Load(object sender, EventArgs e) + { + sbmt.Text = ui.Text("create"); rename.Attributes["placeholder"] = ui.Text("name"); - // Put user code to initialize the page here - } + // Put user code to initialize the page here + } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - - protected void sbmt_Click(object sender, System.EventArgs e) - { - if (Page.IsValid) - { - int nodeId; - if (int.TryParse(Request.QueryString["nodeId"], out nodeId) == false) - nodeId = -1; + protected void sbmt_Click(object sender, EventArgs e) + { + if (Page.IsValid) + { + int nodeId; + if (int.TryParse(Request.QueryString["nodeId"], out nodeId) == false) + nodeId = -1; + try + { var returnUrl = LegacyDialogHandler.Create( new HttpContextWrapper(Context), new User(Security.CurrentUser), Request.GetItemAsString("nodeType"), - nodeId, - rename.Text.Trim()); + nodeId, + rename.Text.Trim()); - BasePage.Current.ClientTools - .ChangeContentFrameUrl(returnUrl) + BasePage.Current.ClientTools + .ChangeContentFrameUrl(returnUrl) .ReloadActionNode(false, true) - .CloseModalWindow(); + .CloseModalWindow(); + } + catch (Exception ex) + { + CustomValidation.ErrorMessage = "* " + ex.Message; + CustomValidation.IsValid = false; + } } - - } - } + + } + + protected CustomValidator CustomValidation; + + /// + /// RequiredFieldValidator1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; + + /// + /// rename control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox rename; + + /// + /// Textbox1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox Textbox1; + + /// + /// sbmt control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button sbmt; + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.designer.cs deleted file mode 100644 index f4bb903739..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.create.controls { - - - public partial class simple { - - /// - /// rename control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox rename; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// sbmt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button sbmt; - - /// - /// Textbox1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox Textbox1; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs index 436aabc97f..f62915cfb1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Web.UI; using umbraco.BusinessLogic; +using Umbraco.Core.Security; using umbraco.DataLayer; using umbraco.BasePages; using Umbraco.Core.IO; @@ -33,15 +34,32 @@ namespace umbraco //BusinessLogic.User.MakeNew(Alias, Alias, "", BusinessLogic.UserType.GetUserType(1)); //return true; + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); + var status = MembershipCreateStatus.ProviderError; try { // Password is auto-generated. They are they required to change the password by editing the user information. - var u = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider].CreateUser(Alias, - Membership.GeneratePassword( - Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider].MinRequiredPasswordLength, - Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider].MinRequiredNonAlphanumericCharacters), - "", "", "", true, null, out status); + + var password = Membership.GeneratePassword( + provider.MinRequiredPasswordLength, + provider.MinRequiredNonAlphanumericCharacters); + + var parts = Alias.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 2) + { + return false; + } + var login = parts[0]; + var email = parts[1]; + + var u = provider.CreateUser( + login, password, email.Trim().ToLower(), "", "", true, null, out status); + + if (u == null) + { + return false; + } _returnUrl = string.Format("users/EditUser.aspx?id={0}", u.ProviderUserKey); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx index 87027c0318..f12ab67b7b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx @@ -1,4 +1,4 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" Title="Assembly Browser" Codebehind="assemblyBrowser.aspx.cs" AutoEventWireup="True" +<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoDialog.Master" Title="Assembly Browser" Codebehind="assemblyBrowser.aspx.cs" AutoEventWireup="True" Inherits="umbraco.developer.assemblyBrowser" %> <%@ Register TagPrefix="wc1" Namespace="umbraco.controls" Assembly="umbraco" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> @@ -7,7 +7,8 @@ -

    +

    +

    The following list shows the Public Properties from the Control. By checking the Properties and click the "Save Properties" button at diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs index b58dbc3836..d186d9ef50 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.cs @@ -44,7 +44,7 @@ namespace umbraco.developer if (Request.QueryString["type"] == null) { isUserControl = true; - var fileName = Request.CleanForXss("fileName"); + var fileName = Request.GetItemAsString("fileName"); if (!fileName.StartsWith("~")) { if (fileName.StartsWith("/")) @@ -112,7 +112,7 @@ namespace umbraco.developer } catch (Exception err) { - AssemblyName.Text = "Error reading " + Request["fileName"]; + AssemblyName.Text = "Error reading " + Request.CleanForXss("fileName"); Button1.Visible = false; ChooseProperties.Controls.Add(new LiteralControl("

    " + err.ToString() + "

    ")); } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs index 812f64117a..fe16914cfa 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/assemblyBrowser.aspx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.4200 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,15 +12,6 @@ namespace umbraco.developer { public partial class assemblyBrowser { - ///

    - /// JsInclude6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude6; - /// /// AssemblyName control. /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx deleted file mode 100644 index e271bf7413..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx +++ /dev/null @@ -1,261 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" CodeBehind="installer.aspx.cs" - AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" - Trace="false" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - -
    -

    - Only install packages from sources you know and trust!

    -

    - When installing an Umbraco package you should use the same caution as when you install - an application on your computer.

    -

    - A malicious package could damage your Umbraco installation just like a malicious - application can damage your computer. -

    -

    - It is recommended to install from the official Umbraco package - repository or a custom repository whenever it's possible. -

    -

    - - -

    -
    -
    - -

    - -
    - - <%= umbraco.ui.Text("packager", "chooseLocalPackageText") %> - -

    -
    - - - - -
    - - -
    -

    - This repository requires authentication before you can download any packages from - it.
    - Please enter email and password to login. -

    -
    -
    - - - - - - -
    - -
    -
    -

    - Please note: 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. -

    -
    - - - - - - - - - - - - - - - -
    -

    - Binary files in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Macro Conflicts in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Template Conflicts in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Stylesheet Conflicts in the package!

    -

    - Read more... -

    - -
    -
    - -
    - - -
    -
    - -
    - - - - - - - - - -

    - All items in the package has been installed

    -

    - Overview of what was installed can found under "installed package" in the developer - section.

    -

    - Uninstall is available at the same location.

    -

    - - -

    - -
    -
    - -
    -
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index e18d46c625..a78e2293f0 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Globalization; using System.Threading; using System.Web; using System.Web.SessionState; @@ -36,6 +37,8 @@ namespace umbraco.presentation.developer.packages private readonly cms.businesslogic.packager.Installer _installer = new cms.businesslogic.packager.Installer(); private string _tempFileName = ""; + protected string RefreshQueryString { get; set; } + protected void Page_Load(object sender, EventArgs e) { var ex = new Exception(); @@ -53,9 +56,9 @@ namespace umbraco.presentation.developer.packages } //if we are actually in the middle of installing something... meaning we keep redirecting back to this page with - // custom query strings - // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController - // http://issues.umbraco.org/issue/U4-1047 + // custom query strings + // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController + // http://issues.umbraco.org/issue/U4-1047 if (!string.IsNullOrEmpty(Request.GetItemAsString("installing"))) { HideAllPanes(); @@ -63,22 +66,22 @@ namespace umbraco.presentation.developer.packages ProcessInstall(Request.GetItemAsString("installing")); //process the current step } - else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet - && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) + else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet + && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) { //we'll fetch the local information we have about our repo, to find out what webservice to query. - _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); - + _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); + if (_repo != null && _repo.HasConnection()) { //from the webservice we'll fetch some info about the package. - cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); + cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); //if the package is protected we will ask for the users credentials. (this happens every time they try to fetch anything) if (!pack.Protected) { //if it isn't then go straigt to the accept licens screen - tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); + tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); UpdateSettings(); } @@ -94,7 +97,7 @@ namespace umbraco.presentation.developer.packages else { fb.Style.Add("margin-top", "7px"); - fb.type = global::umbraco.uicontrols.Feedback.feedbacktype.error; + fb.type = uicontrols.Feedback.feedbacktype.error; fb.Text = "No connection to repository. Runway could not be installed as there was no connection to: '" + _repo.RepositoryUrl + "'"; pane_upload.Visible = false; } @@ -107,7 +110,7 @@ namespace umbraco.presentation.developer.packages acceptCheckbox.Attributes.Add("onmouseup", "document.getElementById('" + ButtonInstall.ClientID + "').disabled = false;"); } - protected void uploadFile(object sender, System.EventArgs e) + protected void uploadFile(object sender, EventArgs e) { try { @@ -131,7 +134,7 @@ namespace umbraco.presentation.developer.packages string memberGuid = _repo.Webservice.authenticate(tb_email.Text, library.md5(tb_password.Text)); //if we auth correctly and get a valid key back, we will fetch the file from the repo webservice. - if (!string.IsNullOrEmpty(memberGuid)) + if (string.IsNullOrEmpty(memberGuid) == false) { tempFile.Value = _installer.Import(_repo.fetch(helper.Request("guid"), memberGuid)); UpdateSettings(); @@ -212,77 +215,80 @@ namespace umbraco.presentation.developer.packages private void ProcessInstall(string currentStep) { - var dir = Request.GetItemAsString("dir"); + var dir = Request.GetItemAsString("dir"); var packageId = 0; - int.TryParse(Request.GetItemAsString("pId"), out packageId); + int.TryParse(Request.GetItemAsString("pId"), out packageId); switch (currentStep) { case "businesslogic": - //first load in the config from the temporary directory - //this will ensure that the installer have access to all the new files and the package manifest - _installer.LoadConfig(dir); + //first load in the config from the temporary directory + //this will ensure that the installer have access to all the new files and the package manifest + _installer.LoadConfig(dir); _installer.InstallBusinessLogic(packageId, dir); //making sure that publishing actions performed from the cms layer gets pushed to the presentation library.RefreshContent(); - - if (!string.IsNullOrEmpty(_installer.Control)) + + if (string.IsNullOrEmpty(_installer.Control) == false) { - Response.Redirect("installer.aspx?installing=customInstaller&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); } else { - Response.Redirect("installer.aspx?installing=finished&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); } break; case "customInstaller": - var customControl = Request.GetItemAsString("customControl"); + var customControl = Request.GetItemAsString("customControl"); - if (!customControl.IsNullOrWhiteSpace()) + if (customControl.IsNullOrWhiteSpace() == false) { HideAllPanes(); - _configControl = new System.Web.UI.UserControl().LoadControl(SystemDirectories.Root + customControl); + _configControl = LoadControl(SystemDirectories.Root + customControl); _configControl.ID = "packagerConfigControl"; pane_optional.Controls.Add(_configControl); pane_optional.Visible = true; - if (!IsPostBack) - { - //We still need to clean everything up which is normally done in the Finished Action - PerformPostInstallCleanup(packageId, dir); - } - + if (!IsPostBack) + { + //We still need to clean everything up which is normally done in the Finished Action + PerformPostInstallCleanup(packageId, dir); + } + } else { //if the custom installer control is empty here (though it should never be because we've already checked for it previously) - //then we should run the normal FinishedAction - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + //then we should run the normal FinishedAction + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); } break; + case "refresh": + PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl"), Request.GetItemAsString("customControl")); + break; case "finished": - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); break; default: break; } } - /// - /// Perform the 'Finished' action of the installer - /// - /// - /// - /// - private void PerformFinishedAction(int packageId, string dir, string url) - { - HideAllPanes(); - //string url = _installer.Url; - string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(); + /// + /// Perform the 'Finished' action of the installer + /// + /// + /// + /// + private void PerformFinishedAction(int packageId, string dir, string url) + { + HideAllPanes(); + //string url = _installer.Url; + string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(CultureInfo.InvariantCulture); bt_viewInstalledPackage.OnClientClick = "document.location = '" + packageViewUrl + "'; return false;"; @@ -292,23 +298,70 @@ namespace umbraco.presentation.developer.packages pane_success.Visible = true; - PerformPostInstallCleanup(packageId, dir); - } + PerformPostInstallCleanup(packageId, dir); + } - /// - /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... - /// - /// - /// - private void PerformPostInstallCleanup(int packageId, string dir) - { - BasePage.Current.ClientTools.ReloadActionNode(true, true); - _installer.InstallCleanUp(packageId, dir); - //clear the tree cache - ClientTools.ClearClientTreeCache().RefreshTree("packager"); - TreeDefinitionCollection.Instance.ReRegisterTrees(); - BizLogicAction.ReRegisterActionsAndHandlers(); - } + /// + /// Perform the 'Refresh' action of the installer + /// + /// + /// + /// + /// + private void PerformRefreshAction(int packageId, string dir, string url, string customControl) + { + HideAllPanes(); + + //create the URL to refresh to + // /umbraco/developer/packages/installer.aspx?installing=finished + // &dir=X:\Projects\Umbraco\Umbraco_7.0\src\Umbraco.Web.UI\App_Data\aef8c41f-63a0-494b-a1e2-10d761647033 + // &pId=3 + // &customUrl=http:%2f%2four.umbraco.org%2fprojects%2fwebsite-utilities%2fmerchello + + if (customControl.IsNullOrWhiteSpace()) + { + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=finished&dir={0}&pId={1}&customUrl={2}", + dir, packageId, url)); + } + else + { + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=customInstaller&dir={0}&pId={1}&customUrl={2}&customControl={3}", + dir, packageId, url, customControl)); + } + + pane_refresh.Visible = true; + + PerformPostInstallCleanup(packageId, dir); + } + + /// + /// Runs Post refresh actions such reloading the correct tree nodes, etc... + /// + private void PerformPostRefreshAction() + { + BasePage.Current.ClientTools.ReloadActionNode(true, true); + } + + /// + /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... + /// + /// + /// + private void PerformPostInstallCleanup(int packageId, string dir) + { + _installer.InstallCleanUp(packageId, dir); + + // Update ClientDependency version + var clientDependencyConfig = new Umbraco.Core.Configuration.ClientDependencyConfiguration(); + var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber(); + + //clear the tree cache - we'll do this here even though the browser will reload, but just in case it doesn't can't hurt. + ClientTools.ClearClientTreeCache().RefreshTree("packager"); + TreeDefinitionCollection.Instance.ReRegisterTrees(); + BizLogicAction.ReRegisterActionsAndHandlers(); + } //this accepts the package, creates the manifest and then installs the files. protected void startInstall(object sender, System.EventArgs e) @@ -323,11 +376,11 @@ namespace umbraco.presentation.developer.packages //and then copy over the files. This will take some time if it contains .dlls that will reboot the system.. _installer.InstallFiles(pId, tempFile.Value); - //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the - // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business - // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app - // pool will be restarted after redirect. - Thread.Sleep(2000); + //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the + // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business + // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app + // pool will be restarted after redirect. + Thread.Sleep(2000); Response.Redirect("installer.aspx?installing=businesslogic&dir=" + tempFile.Value + "&pId=" + pId.ToString()); } @@ -339,7 +392,428 @@ namespace umbraco.presentation.developer.packages pane_installing.Visible = false; pane_optional.Visible = false; pane_success.Visible = false; + pane_refresh.Visible = false; pane_upload.Visible = false; } + + /// + /// Panel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.UmbracoPanel Panel1; + + /// + /// fb control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback fb; + + /// + /// pane_upload control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_upload; + + /// + /// PropertyPanel9 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; + + /// + /// file1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; + + /// + /// ButtonLoadPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; + + /// + /// progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progbar1; + + /// + /// pane_authenticate control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_authenticate; + + /// + /// tb_email control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_email; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// tb_password control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_password; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// Button1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button Button1; + + /// + /// pane_acceptLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; + + /// + /// pane_acceptLicenseInner control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// LabelName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelName; + + /// + /// PropertyPanel5 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; + + /// + /// LabelAuthor control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelAuthor; + + /// + /// PropertyPanel4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; + + /// + /// LabelMore control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelMore; + + /// + /// PropertyPanel6 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; + + /// + /// LabelLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelLicense; + + /// + /// PropertyPanel7 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; + + /// + /// acceptCheckbox control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; + + /// + /// PropertyPanel8 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; + + /// + /// readme control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal readme; + + /// + /// pp_unsecureFiles control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; + + /// + /// lt_files control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lt_files; + + /// + /// pp_macroConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; + + /// + /// ltrMacroAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; + + /// + /// pp_templateConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; + + protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; + protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; + protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; + + /// + /// ltrTemplateAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; + + /// + /// pp_stylesheetConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; + + /// + /// ltrStylesheetNames control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; + + /// + /// _progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar _progbar1; + + /// + /// ButtonInstall control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonInstall; + + /// + /// pane_installing control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_installing; + + /// + /// progBar2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progBar2; + + /// + /// lit_installStatus control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_installStatus; + + /// + /// pane_optional control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_optional; + + /// + /// pane_success control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_success; + + protected global::umbraco.uicontrols.Pane pane_refresh; + + /// + /// bt_viewInstalledPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; + + /// + /// lit_authorUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_authorUrl; + + /// + /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// processState control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs deleted file mode 100644 index abe1f67506..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs +++ /dev/null @@ -1,433 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.developer.packages { - - - public partial class Installer { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel Panel1; - - /// - /// fb control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback fb; - - /// - /// pane_upload control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_upload; - - /// - /// PropertyPanel9 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; - - /// - /// file1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; - - /// - /// ButtonLoadPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; - - /// - /// progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progbar1; - - /// - /// pane_authenticate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_authenticate; - - /// - /// tb_email control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_email; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// tb_password control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_password; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - - /// - /// pane_acceptLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; - - /// - /// pane_acceptLicenseInner control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// LabelName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelName; - - /// - /// PropertyPanel5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; - - /// - /// LabelAuthor control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelAuthor; - - /// - /// PropertyPanel4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; - - /// - /// LabelMore control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelMore; - - /// - /// PropertyPanel6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; - - /// - /// LabelLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelLicense; - - /// - /// PropertyPanel7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; - - /// - /// acceptCheckbox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; - - /// - /// PropertyPanel8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; - - /// - /// readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal readme; - - /// - /// pp_unsecureFiles control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; - - /// - /// lt_files control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_files; - - /// - /// pp_macroConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; - - /// - /// ltrMacroAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; - - /// - /// pp_templateConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; - - protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; - protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; - protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; - - /// - /// ltrTemplateAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; - - /// - /// pp_stylesheetConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; - - /// - /// ltrStylesheetNames control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; - - /// - /// _progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar _progbar1; - - /// - /// ButtonInstall control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonInstall; - - /// - /// pane_installing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_installing; - - /// - /// progBar2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progBar2; - - /// - /// lit_installStatus control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_installStatus; - - /// - /// pane_optional control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_optional; - - /// - /// pane_success control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_success; - - /// - /// bt_viewInstalledPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; - - /// - /// lit_authorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_authorUrl; - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// processState control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs index b8fbdf25a5..f2298089a2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs @@ -3,6 +3,7 @@ using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Linq; using System.Web; using System.Web.SessionState; using System.Web.UI; @@ -10,6 +11,7 @@ using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using Umbraco.Core.Configuration; using umbraco.cms.businesslogic.propertytype; +using Umbraco.Core; namespace umbraco.developer { @@ -35,7 +37,9 @@ namespace umbraco.developer // generic properties string existingGenProps = ","; - foreach (PropertyType pt in PropertyType.GetAll()) + var exclude = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); + foreach (PropertyType pt in PropertyType.GetAll().Where(x => exclude.Contains(x.Alias) == false)) + { if (!existingGenProps.Contains("," + pt.Alias + ",")) { if(UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema) @@ -45,6 +49,8 @@ namespace umbraco.developer existingGenProps += pt.Alias + ","; } + } + preValuesSource.Sort(); preValues.DataSource = preValuesSource; @@ -57,24 +63,5 @@ namespace umbraco.developer valueOf.Text = Request.QueryString["value"]; } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs index 208b2a9780..78c4549a55 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs @@ -8,6 +8,7 @@ using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; +using Umbraco.Web; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; using umbraco.cms.businesslogic.web; @@ -32,9 +33,9 @@ namespace umbraco.dialogs int docId; if (int.TryParse(Request.QueryString["id"], out docId)) { - - //TODO Send to publish!! - + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification( + ApplicationContext.Services.ContentService.GetById(docId), ActionToPublish.Instance, ApplicationContext); } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs index 23005d62d2..27c1724bff 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs @@ -80,6 +80,16 @@ namespace umbraco.presentation.umbraco.dialogs if (contentType != null) dtNameConfirm.Text = contentType.Name; + // Try to clean up the temporary file. + try + { + System.IO.File.Delete(tempFile.Value); + } + catch(Exception ex) + { + Umbraco.Core.Logging.LogHelper.Error(typeof(importDocumentType), "Error cleaning up temporary udt file in App_Data: " + ex.Message, ex); + } + Wizard.Visible = false; Confirm.Visible = false; done.Visible = true; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs index db46d2a96a..25bd4afae3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs @@ -88,6 +88,10 @@ namespace umbraco.dialogs currContent = Services.MediaService.GetById(Request.GetItemAs("id")); } + // Preselect the parent of the seslected item. + if (currContent.ParentId > 0) + JTree.SelectedNodePath = currContent.Path.Substring(0, currContent.Path.LastIndexOf(',')); + var validAction = true; if (CurrentApp == Constants.Applications.Content && Umbraco.Core.Models.ContentExtensions.HasChildren(currContent, Services)) { @@ -141,12 +145,30 @@ namespace umbraco.dialogs private void HandleDocumentTypeCopy() { + + //TODO: This should be a method on the service!!! + var contentTypeService = ApplicationContext.Current.Services.ContentTypeService; var contentType = contentTypeService.GetContentType( int.Parse(Request.GetItemAsString("id"))); - var alias = rename.Text.Replace("'", "''"); + var alias = rename.Text.Trim().Replace("'", "''"); var clone = ((Umbraco.Core.Models.ContentType) contentType).Clone(alias); + clone.Name = rename.Text.Trim(); + + //set the master + //http://issues.umbraco.org/issue/U4-2843 + //http://issues.umbraco.org/issue/U4-3552 + var parent = int.Parse(masterType.SelectedValue); + if (parent > 0) + { + clone.ParentId = parent; + } + else + { + clone.ParentId = -1; + } + contentTypeService.Save(clone); var returnUrl = string.Format("{0}/settings/editNodeTypeNew.aspx?id={1}", SystemDirectories.Umbraco, clone.Id); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx deleted file mode 100644 index 19edd39dfc..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx +++ /dev/null @@ -1,170 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="protectPage.aspx.cs" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - - - - -
    -

    <%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%>

    -

    <%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%>

    -
    -
    - - -
    -

    <%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%>

    -

    <%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%>

    - - -

    - <%= umbraco.ui.Text("publicAccess", "paAdvancedNoGroups", base.getUser())%> -

    -
    - -
    -
    -

    -   <%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -

    -
    - - - - -

    <%= umbraco.ui.Text("publicAccess", "paSetLogin", base.getUser())%>

    -
    - - - - - - -
    - - - -

    <%= umbraco.ui.Text("publicAccess", "paSelectRoles", base.getUser())%>

    -
    - - - -
    - - - - - -
    - - <%=umbraco.ui.Text("paLoginPageHelp")%> - -

    -
    - - - -
    - - <%=umbraco.ui.Text("paErrorPageHelp")%> - -
    -
    - -
    -

    - - -   <%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -

    -
    - - -
    - - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs index b2116c7cf4..b89091c017 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -1,13 +1,18 @@ -using System; +using System; +using System.Globalization; +using System.Linq; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Windows.Forms.VisualStyles; +using Umbraco.Core; +using Umbraco.Core.Logging; using umbraco.cms.businesslogic.member; using umbraco.cms.businesslogic.web; using umbraco.controls; using umbraco.cms.helpers; using umbraco.BasePages; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.dialogs { @@ -22,11 +27,16 @@ namespace umbraco.presentation.umbraco.dialogs } - protected System.Web.UI.WebControls.Literal jsShowWindow; + protected Literal jsShowWindow; protected DualSelectbox _memberGroups = new DualSelectbox(); protected ContentPicker loginPagePicker = new ContentPicker(); protected ContentPicker errorPagePicker = new ContentPicker(); + override protected void OnInit(EventArgs e) + { + base.OnInit(e); + } + protected void selectMode(object sender, EventArgs e) { p_mode.Visible = false; @@ -46,11 +56,11 @@ namespace umbraco.presentation.umbraco.dialogs } } - protected void Page_Load(object sender, System.EventArgs e) + protected void Page_Load(object sender, EventArgs e) { // Check for editing int documentId = int.Parse(helper.Request("nodeId")); - cms.businesslogic.web.Document documentObject = new cms.businesslogic.web.Document(documentId); + var documentObject = new Document(documentId); jsShowWindow.Text = ""; ph_errorpage.Controls.Add(errorPagePicker); @@ -61,12 +71,12 @@ namespace umbraco.presentation.umbraco.dialogs pp_loginPage.Text = ui.Text("paLoginPage"); pp_errorPage.Text = ui.Text("paErrorPage"); - pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", base.getUser()); - pane_pages.Text = ui.Text("publicAccess", "paSelectPages", base.getUser()); - pane_simple.Text = ui.Text("publicAccess", "paSimple", base.getUser()); - pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", base.getUser()); + pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", UmbracoUser); + pane_pages.Text = ui.Text("publicAccess", "paSelectPages", UmbracoUser); + pane_simple.Text = ui.Text("publicAccess", "paSimple", UmbracoUser); + pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", UmbracoUser); - if (!IsPostBack) + if (IsPostBack == false) { if (Access.IsProtected(documentId, documentObject.Path) && Access.GetProtectionType(documentId) != ProtectionType.NotProtected) { @@ -78,25 +88,32 @@ namespace umbraco.presentation.umbraco.dialogs int loginPage = Access.GetLoginPage(documentObject.Path); try { - Document loginPageObj = new Document(loginPage); + var loginPageObj = new Document(loginPage); if (loginPageObj != null) { - loginPagePicker.Value = loginPage.ToString(); + loginPagePicker.Value = loginPage.ToString(CultureInfo.InvariantCulture); } - Document errorPageObj = new Document(errorPage); - errorPagePicker.Value = errorPage.ToString(); + var errorPageObj = new Document(errorPage); + errorPagePicker.Value = errorPage.ToString(CultureInfo.InvariantCulture); } - catch + catch (Exception ex) { + LogHelper.Error("An error occurred initializing the protect page editor", ex); } if (Access.GetProtectionType(documentId) == ProtectionType.Simple) { MembershipUser m = Access.GetAccessingMembershipUser(documentId); - simpleLogin.Text = m.UserName; - pane_simple.Visible = true; - pane_advanced.Visible = false; - bt_protect.CommandName = "simple"; + if (m != null) + { + pane_simple.Visible = true; + pp_pass.Visible = false; + simpleLogin.Visible = false; + SimpleLoginLabel.Visible = true; + SimpleLoginLabel.Text = m.UserName; + pane_advanced.Visible = false; + bt_protect.CommandName = "simple"; + } } else if (Access.GetProtectionType(documentId) == ProtectionType.Advanced) @@ -114,17 +131,17 @@ namespace umbraco.presentation.umbraco.dialogs // Load up membergrouops _memberGroups.ID = "Membergroups"; _memberGroups.Width = 175; - string selectedGroups = ""; - string[] _roles = Roles.GetAllRoles(); + var selectedGroups = ""; + var roles = Roles.GetAllRoles().OrderBy(x => x); - if (_roles.Length > 0) + if (roles.Any()) { - foreach (string role in _roles) + foreach (string role in roles) { ListItem li = new ListItem(role, role); - if (!IsPostBack) + if (IsPostBack == false) { - if (cms.businesslogic.web.Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) + if (Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) selectedGroups += role + ","; } _memberGroups.Items.Add(li); @@ -145,26 +162,14 @@ namespace umbraco.presentation.umbraco.dialogs // Put user code to initialize the page here } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) + protected void ChangeOnClick(object sender, EventArgs e) { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); + SimpleLoginNameValidator.IsValid = true; + SimpleLoginLabel.Visible = false; + simpleLogin.Visible = true; + pp_pass.Visible = true; } - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - protected void protect_Click(object sender, CommandEventArgs e) { if (string.IsNullOrEmpty(errorPagePicker.Value)) @@ -173,63 +178,66 @@ namespace umbraco.presentation.umbraco.dialogs if (string.IsNullOrEmpty(loginPagePicker.Value)) cv_loginPage.IsValid = false; + //reset + SimpleLoginNameValidator.IsValid = true; + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); if (Page.IsValid) { int pageId = int.Parse(helper.Request("nodeId")); - p_buttons.Visible = false; - pane_advanced.Visible = false; - pane_simple.Visible = false; if (e.CommandName == "simple") { - MembershipUser member = Membership.GetUser(simpleLogin.Text); + var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; + + var member = provider.GetUser(memberLogin, false); if (member == null) { + var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com"; + // this needs to work differently depending on umbraco members or external membership provider - if (!cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (provider.IsUmbracoMembershipProvider() == false) { - member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text); + member = provider.CreateUser(memberLogin, simplePassword.Text, tempEmail); } else { - try + //if it's the umbraco membership provider, then we need to tell it what member type to create it with + if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) { - if ( - cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType") == null) - { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); - } + MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } - catch + var castedProvider = provider.AsUmbracoMembershipProvider(); + MembershipCreateStatus status; + member = castedProvider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, + memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); + if (status != MembershipCreateStatus.Success) { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); + SimpleLoginNameValidator.IsValid = false; + SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; + SimpleLoginNameValidator.Text = "Could not create user: " + status; + return; } - - // create member - Member mem = cms.businesslogic.member.Member.MakeNew(simpleLogin.Text, "", cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType"), base.getUser()); - // working around empty password restriction for Umbraco Member Mode - mem.Password = simplePassword.Text; - member = Membership.GetUser(simpleLogin.Text); } } - else + else if (pp_pass.Visible) { - // change password if it's not empty - if (string.IsNullOrWhiteSpace(simplePassword.Text) == false) - { - var mem = Member.GetMemberFromLoginName(simpleLogin.Text); - mem.Password = simplePassword.Text; - } + SimpleLoginNameValidator.IsValid = false; + SimpleLoginLabel.Visible = true; + SimpleLoginLabel.Text = memberLogin; + simpleLogin.Visible = false; + pp_pass.Visible = false; + return; } // Create or find a memberGroup - string simpleRoleName = "__umbracoRole_" + simpleLogin.Text; - if (!Roles.RoleExists(simpleRoleName)) + var simpleRoleName = "__umbracoRole_" + member.UserName; + if (Roles.RoleExists(simpleRoleName) == false) { Roles.CreateRole(simpleRoleName); } - if (!Roles.IsUserInRole(member.UserName, simpleRoleName)) + if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false) { Roles.AddUserToRole(member.UserName, simpleRoleName); } @@ -240,17 +248,21 @@ namespace umbraco.presentation.umbraco.dialogs } else if (e.CommandName == "advanced") { - cms.businesslogic.web.Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); + Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); foreach (ListItem li in _memberGroups.Items) - if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",") > -1) - cms.businesslogic.web.Access.AddMembershipRoleToDocument(pageId, li.Value); + if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",", StringComparison.Ordinal) > -1) + Access.AddMembershipRoleToDocument(pageId, li.Value); else - cms.businesslogic.web.Access.RemoveMembershipRoleFromDocument(pageId, li.Value); + Access.RemoveMembershipRoleFromDocument(pageId, li.Value); } feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text) + "

    " + ui.Text("closeThisWindow") + ""; + p_buttons.Visible = false; + pane_advanced.Visible = false; + pane_simple.Visible = false; + ClientTools.ReloadActionNode(true, false); feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success; @@ -273,5 +285,289 @@ namespace umbraco.presentation.umbraco.dialogs feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success; } + + protected CustomValidator SimpleLoginNameValidator; + protected Label SimpleLoginLabel; + + ///

    + /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// feedback control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback feedback; + + /// + /// p_mode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_mode; + + /// + /// pane_chooseMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_chooseMode; + + /// + /// rb_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_simple; + + /// + /// rb_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_advanced; + + /// + /// p_noGroupsFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; + + /// + /// bt_selectMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_selectMode; + + /// + /// pane_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_simple; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// pp_login control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_login; + + /// + /// simpleLogin control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simpleLogin; + + /// + /// pp_pass control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_pass; + + /// + /// simplePassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simplePassword; + + /// + /// pane_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_advanced; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// groupsSelector control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; + + /// + /// p_buttons control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_buttons; + + /// + /// pane_pages control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_pages; + + /// + /// pp_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; + + /// + /// ph_loginpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; + + /// + /// cv_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; + + /// + /// pp_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; + + /// + /// ph_errorpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; + + /// + /// cv_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; + + /// + /// bt_protect control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_protect; + + /// + /// bt_buttonRemoveProtection control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; + + /// + /// errorId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; + + /// + /// loginId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; + + /// + /// js control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder js; + + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs deleted file mode 100644 index 6ed66eea24..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs +++ /dev/null @@ -1,295 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.4200 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class protectPage { - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// feedback control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback feedback; - - /// - /// p_mode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_mode; - - /// - /// pane_chooseMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_chooseMode; - - /// - /// rb_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_simple; - - /// - /// rb_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_advanced; - - /// - /// p_noGroupsFound control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; - - /// - /// bt_selectMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_selectMode; - - /// - /// pane_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_simple; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// pp_login control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_login; - - /// - /// simpleLogin control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simpleLogin; - - /// - /// pp_pass control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_pass; - - /// - /// simplePassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simplePassword; - - /// - /// pane_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_advanced; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// groupsSelector control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; - - /// - /// p_buttons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_buttons; - - /// - /// pane_pages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_pages; - - /// - /// pp_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; - - /// - /// ph_loginpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; - - /// - /// cv_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; - - /// - /// pp_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; - - /// - /// ph_errorpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; - - /// - /// cv_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; - - /// - /// bt_protect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_protect; - - /// - /// bt_buttonRemoveProtection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; - - /// - /// errorId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; - - /// - /// loginId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; - - /// - /// js control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder js; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs index 9142e38c1d..14fed8fb17 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs @@ -39,10 +39,9 @@ namespace umbraco.cms.presentation Server.ScriptTimeout = 100000; System.Xml.XmlDocument xd = new System.Xml.XmlDocument(); - //store children array here because iterating over an Array object is very inneficient. var doc = new cms.businesslogic.web.Document(int.Parse(Request.GetItemAsString("refreshNodes"))); - var c = doc.Children; - foreach (cms.businesslogic.web.Document d in c) + + foreach (cms.businesslogic.web.Document d in doc.Children) { d.XmlGenerate(xd); Response.Write("
  • Creating xml for " + d.Text + "
  • "); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs index 4a60b0605b..30f9fbb12d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs @@ -20,6 +20,16 @@ namespace umbraco.dialogs base.OnLoad(e); TreeParams = TreeRequestParams.FromQueryStrings().CreateTreeService(); DataBind(); + + if(Request.QueryString["selected"] != null && TreeParams.TreeType == "content") + { + var currContent = Services.ContentService.GetById(int.Parse(Request.QueryString["selected"])); + if (currContent != null) + { + if (currContent.ParentId > 0) + DialogTree.SelectedNodePath = currContent.Path; + } + } } protected TreeService TreeParams { get; private set; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs index 3204058780..d875b856e9 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs @@ -3,12 +3,15 @@ using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Linq; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using Umbraco.Core.Configuration; +using Umbraco.Core; +using Umbraco.Core.Persistence.SqlSyntax; using umbraco.DataLayer; namespace umbraco.dialogs @@ -56,7 +59,13 @@ namespace umbraco.dialogs } else { - fieldSql = "select distinct alias from cmsPropertyType order by alias"; + //exclude built-in memberhip properties from showing up here + var exclude = Constants.Conventions.Member.GetStandardPropertyTypeStubs() + .Select(x => SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(x.Key)).ToArray(); + + fieldSql = string.Format( + "select distinct alias from cmsPropertyType where alias not in ({0}) order by alias", + string.Join(",", exclude)); pp_insertField.Text = ui.Text("templateEditor", "chooseField"); } @@ -96,24 +105,6 @@ namespace umbraco.dialogs } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - #endregion /// /// JsInclude1 control. diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs index aa43f877d7..2a0c5517b4 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx.cs @@ -38,14 +38,9 @@ namespace umbraco.presentation.umbraco.dialogs if (action == "new") action = "create"; ArrayList actions = BusinessLogic.Actions.Action.GetAll(); - foreach (interfaces.IAction a in actions) + foreach (interfaces.IAction a in actions) { - if (string.Compare(a.Alias, action, true) == 0) { - if(a.Icon.StartsWith(".")) - return "
    " + ui.Text(action) + "
    "; - else - return "\"" " + ui.Text(action); - } + return ui.Text(action); } return action; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/js/language.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/js/language.aspx.cs index f6fd2f200b..315cfc6f22 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/js/language.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/js/language.aspx.cs @@ -22,8 +22,7 @@ namespace umbraco.js return; StringBuilder sb = new StringBuilder(); - - sb.Append("{"); + foreach(XmlNode x in all.DocumentElement.ChildNodes) { if(x == null) @@ -50,16 +49,10 @@ namespace umbraco.js sb.Append("\"" + _tempKey + "\": \"" + tmpStr + "\","); - - } - - } - sb.Remove(sb.Length - 1, 1); - sb.Append("}"); - - Response.Write(sb.ToString()); + var f = "{" + sb.ToString().Trim().Trim(',').Trim() + "}"; + Response.Write(f); } #region Web Form Designer generated code diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMemberType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMemberType.aspx.cs index 94413d2fd2..27c6fbbffb 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMemberType.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMemberType.aspx.cs @@ -1,14 +1,18 @@ using System; -using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Globalization; +using System.Linq; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; +using ClientDependency.Core; +using umbraco.cms.businesslogic.member; using umbraco.cms.presentation.Trees; +using umbraco.controls; namespace umbraco.cms.presentation.members { @@ -20,46 +24,55 @@ namespace umbraco.cms.presentation.members CurrentApp = BusinessLogic.DefaultApps.member.ToString(); } - protected System.Web.UI.WebControls.PlaceHolder plc; - private cms.businesslogic.member.MemberType dt; + protected PlaceHolder plc; + private businesslogic.member.MemberType _memberType; - private System.Collections.ArrayList ExtraPropertyTypeInfos = new System.Collections.ArrayList(); - protected global::umbraco.controls.ContentTypeControlNew ContentTypeControlNew1; + protected ContentTypeControlNew ContentTypeControlNew1; - protected void Page_Load(object sender, System.EventArgs e) - { - dt = new cms.businesslogic.member.MemberType(int.Parse(Request.QueryString["id"])); - setupExtraEditorControls(); - ContentTypeControlNew1.InfoTabPage.Controls.Add(Pane1andmore); + protected override void OnInit(EventArgs e) + { + base.OnInit(e); - if (!IsPostBack) - { - ClientTools - .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias) - .SyncTree(dt.Id.ToString(), false); - } + ContentTypeControlNew1.SavingContentType += ContentTypeControlNew1_SavingContentType; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + _memberType = new businesslogic.member.MemberType(int.Parse(Request.QueryString["id"])); + SetupExtraEditorControls(); + ContentTypeControlNew1.InfoTabPage.Controls.Add(Pane1andmore); + + if (!IsPostBack) + { + ClientTools + .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias) + .SyncTree(_memberType.Id.ToString(CultureInfo.InvariantCulture), false); + } + } - } protected override bool OnBubbleEvent(object source, EventArgs args) { - bool handled = false; - if (args is controls.SaveClickEventArgs) + var handled = false; + var eventArgs = args as SaveClickEventArgs; + if (eventArgs != null) { - controls.SaveClickEventArgs e = (controls.SaveClickEventArgs) args; + var e = eventArgs; if (e.Message == "Saved") { - saveExtras(); + SetupExtraEditorControls(); ClientTools .ShowSpeechBubble(speechBubbleIcon.save, "Membertype saved", "") - .SyncTree(dt.Id.ToString(), true); + .SyncTree(_memberType.Id.ToString(CultureInfo.InvariantCulture), true); } else { ClientTools .ShowSpeechBubble(e.IconType, e.Message, "") - .SyncTree(dt.Id.ToString(), true); + .SyncTree(_memberType.Id.ToString(CultureInfo.InvariantCulture), true); } handled = true; @@ -67,69 +80,60 @@ namespace umbraco.cms.presentation.members return handled; } - private void setupExtraEditorControls(){ - DataTable dt1 = new DataTable(); - dt1.Columns.Add("id"); - dt1.Columns.Add("name"); - dt1.Columns.Add("canedit"); - dt1.Columns.Add("canview"); - foreach (cms.businesslogic.propertytype.PropertyType pt in dt.PropertyTypes) - { - DataRow dr = dt1.NewRow(); - dr["name"] = pt.Name; - dr["id"] = pt.Id; - dt1.Rows.Add(dr); - } - dgEditExtras.DataSource = dt1; - dgEditExtras.DataBind(); - } + private void SetupExtraEditorControls() + { + var dt1 = new DataTable(); + dt1.Columns.Add("id"); + dt1.Columns.Add("name"); + dt1.Columns.Add("canedit"); + dt1.Columns.Add("canview"); - protected void saveExtras() { - foreach (DataGridItem dgi in dgEditExtras.Items) { - if(dgi.ItemType == ListItemType.Item || dgi.ItemType == ListItemType.AlternatingItem) - { - cms.businesslogic.propertytype.PropertyType pt = cms.businesslogic.propertytype.PropertyType.GetPropertyType(int.Parse(dgi.Cells[0].Text)); - dt.setMemberCanEdit(pt,((CheckBox)dgi.FindControl("ckbMemberCanEdit")).Checked); - dt.setMemberViewOnProfile(pt,((CheckBox)dgi.FindControl("ckbMemberCanView")).Checked); - dt.Save(); - } - } - - } - protected void dgEditExtras_itemdatabound(object sender,DataGridItemEventArgs e) + //filter out the 'built-in' property types as we don't want to display these options for them + var builtIns = Umbraco.Core.Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); + var propTypes = _memberType.PropertyTypes.Where(x => builtIns.Contains(x.Alias) == false); + + foreach (var pt in propTypes) + { + var dr = dt1.NewRow(); + dr["name"] = pt.Name; + dr["id"] = pt.Id; + dt1.Rows.Add(dr); + } + dgEditExtras.DataSource = dt1; + dgEditExtras.DataBind(); + } + + /// + /// Executes some code before the member type is saved, this allows us to save the member can edit/member can view information + /// before the Save() command is executed. + /// + /// + void ContentTypeControlNew1_SavingContentType(businesslogic.ContentType e) + { + var mt = e as MemberType; + if (mt == null) return; //This should not happen! + foreach (DataGridItem dgi in dgEditExtras.Items) + { + if (dgi.ItemType == ListItemType.Item || dgi.ItemType == ListItemType.AlternatingItem) + { + var pt = cms.businesslogic.propertytype.PropertyType.GetPropertyType(int.Parse(dgi.Cells[0].Text)); + mt.setMemberCanEdit(pt, ((CheckBox)dgi.FindControl("ckbMemberCanEdit")).Checked); + mt.setMemberViewOnProfile(pt, ((CheckBox)dgi.FindControl("ckbMemberCanView")).Checked); + } + } + } + + protected void dgEditExtras_itemdatabound(object sender,DataGridItemEventArgs e) { if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { - cms.businesslogic.propertytype.PropertyType pt = cms.businesslogic.propertytype.PropertyType.GetPropertyType(int.Parse(((DataRowView)e.Item.DataItem).Row["id"].ToString())); - ((CheckBox)e.Item.FindControl("ckbMemberCanEdit")).Checked = dt.MemberCanEdit(pt); - ((CheckBox)e.Item.FindControl("ckbMemberCanView")).Checked = dt.ViewOnProfile(pt); + var pt = cms.businesslogic.propertytype.PropertyType.GetPropertyType(int.Parse(((DataRowView)e.Item.DataItem).Row["id"].ToString())); + ((CheckBox)e.Item.FindControl("ckbMemberCanEdit")).Checked = _memberType.MemberCanEdit(pt); + ((CheckBox)e.Item.FindControl("ckbMemberCanView")).Checked = _memberType.ViewOnProfile(pt); } } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - - InitializeComponent(); - base.OnInit(e); - - - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs index c165310b3c..305bd8bfee 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs @@ -6,6 +6,7 @@ using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; using umbraco.cms.businesslogic.member; using System.Web.Security; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.members { @@ -13,7 +14,9 @@ namespace umbraco.presentation.umbraco.members { protected void Page_Load(object sender, EventArgs e) { - if (Member.InUmbracoMemberMode()) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider()) ButtonSearch.Text = ui.Text("search"); } @@ -21,8 +24,8 @@ namespace umbraco.presentation.umbraco.members protected void ButtonSearch_Click(object sender, EventArgs e) { resultsPane.Visible = true; - - if (Member.InUmbracoMemberMode()) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) { var query = searchQuery.Text.ToLower(); var internalSearcher = UmbracoContext.Current.InternalMemberSearchProvider; @@ -55,7 +58,7 @@ namespace umbraco.presentation.umbraco.members IEnumerable results; if (searchQuery.Text.Contains("@")) { - results = from MembershipUser x in Membership.FindUsersByEmail(searchQuery.Text) + results = from MembershipUser x in provider.FindUsersByEmail(searchQuery.Text) select new MemberSearchResult() { @@ -67,7 +70,7 @@ namespace umbraco.presentation.umbraco.members } else { - results = from MembershipUser x in Membership.FindUsersByName(searchQuery.Text + "%") + results = from MembershipUser x in provider.FindUsersByName(searchQuery.Text + "%") select new MemberSearchResult() { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs index 271f71e8b9..562073ecf1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs @@ -1,47 +1,58 @@ using System; using System.Collections.Generic; using System.Web; +using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; +using Umbraco.Core.Security; -namespace umbraco.presentation.members { - public partial class ViewMembers : BasePages.UmbracoEnsuredPage { +namespace umbraco.presentation.members +{ + public partial class ViewMembers : BasePages.UmbracoEnsuredPage + { public ViewMembers() { CurrentApp = BusinessLogic.DefaultApps.member.ToString(); } - protected void Page_Load(object sender, EventArgs e) { + protected void Page_Load(object sender, EventArgs e) + { panel1.Text = ui.Text("member"); - bindRp(); + BindRp(); } - private void bindRp() { - string _letter = Request.QueryString["letter"]; - if (!string.IsNullOrEmpty(_letter)) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + private void BindRp() + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + string letter = Request.QueryString["letter"]; + if (string.IsNullOrEmpty(letter) == false) + { + if (provider.IsUmbracoMembershipProvider()) { - if (_letter == "#") + if (letter == "#") { rp_members.DataSource = cms.businesslogic.member.Member.getAllOtherMembers(); } else { - rp_members.DataSource = cms.businesslogic.member.Member.getMemberFromFirstLetter(_letter.ToCharArray()[0]); + rp_members.DataSource = cms.businesslogic.member.Member.getMemberFromFirstLetter(letter.ToCharArray()[0]); } } else { - rp_members.DataSource = System.Web.Security.Membership.FindUsersByName(_letter + "%"); + rp_members.DataSource = provider.FindUsersByName(letter + "%"); } rp_members.DataBind(); } } - public void bindMember(object sender, RepeaterItemEventArgs e) { - if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + public void bindMember(object sender, RepeaterItemEventArgs e) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) + { + if (provider.IsUmbracoMembershipProvider()) { cms.businesslogic.member.Member mem = (cms.businesslogic.member.Member)e.Item.DataItem; Literal _name = (Literal)e.Item.FindControl("lt_name"); @@ -59,7 +70,7 @@ namespace umbraco.presentation.members { } else { - System.Web.Security.MembershipUser mem = (System.Web.Security.MembershipUser)e.Item.DataItem; + var mem = (MembershipUser)e.Item.DataItem; Literal _name = (Literal)e.Item.FindControl("lt_name"); Literal _email = (Literal)e.Item.FindControl("lt_email"); Literal _login = (Literal)e.Item.FindControl("lt_login"); @@ -74,17 +85,19 @@ namespace umbraco.presentation.members { } } - public void deleteMember(object sender, CommandEventArgs e) { + public void deleteMember(object sender, CommandEventArgs e) + { int memid = 0; - if (int.TryParse(e.CommandArgument.ToString(), out memid)) { + if (int.TryParse(e.CommandArgument.ToString(), out memid)) + { cms.businesslogic.member.Member mem = new global::umbraco.cms.businesslogic.member.Member(memid); if (mem != null) mem.delete(); - bindRp(); + BindRp(); } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs index 0373667939..954d36f699 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs @@ -301,7 +301,12 @@ namespace umbraco.NodeFactory _pageXmlNode = ((IHasXmlNode)library.GetXmlNodeById(NodeId.ToString()).Current).GetNode(); else { - if (presentation.UmbracoContext.Current != null) + if (UmbracoContext.Current != null) + { + var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache; + _pageXmlNode = cache.GetXml(UmbracoContext.Current, false).DocumentElement; + } + else if (presentation.UmbracoContext.Current != null) { _pageXmlNode = umbraco.presentation.UmbracoContext.Current.GetXml().DocumentElement; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/UmbracoSiteMapProvider.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/UmbracoSiteMapProvider.cs index f3f082e9e8..ff9cbfc9c0 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/UmbracoSiteMapProvider.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/UmbracoSiteMapProvider.cs @@ -14,125 +14,150 @@ using System.Configuration.Provider; using System.Collections; #endregion -namespace umbraco.presentation.nodeFactory { - public sealed class UmbracoSiteMapProvider : System.Web.StaticSiteMapProvider { +namespace umbraco.presentation.nodeFactory +{ + public sealed class UmbracoSiteMapProvider : StaticSiteMapProvider + { - private SiteMapNode m_root; - private Dictionary m_nodes = new Dictionary(16); - private string m_defaultDescriptionAlias = ""; - private bool m_enableSecurityTrimming; - - public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { + private SiteMapNode _root; + private readonly Dictionary _nodes = new Dictionary(16); + private string _defaultDescriptionAlias = ""; + private bool _enableSecurityTrimming; + public override void Initialize(string name, NameValueCollection config) + { if (config == null) throw new ArgumentNullException("Config is null"); - if (String.IsNullOrEmpty(name)) + if (string.IsNullOrEmpty(name)) name = "UmbracoSiteMapProvider"; - if (!String.IsNullOrEmpty(config["defaultDescriptionAlias"])) { - m_defaultDescriptionAlias = config["defaultDescriptionAlias"].ToString(); + if (string.IsNullOrEmpty(config["defaultDescriptionAlias"]) == false) + { + _defaultDescriptionAlias = config["defaultDescriptionAlias"]; config.Remove("defaultDescriptionAlias"); } - if (config["securityTrimmingEnabled"] != null) { - m_enableSecurityTrimming = bool.Parse(config["securityTrimmingEnabled"]); + if (config["securityTrimmingEnabled"] != null) + { + _enableSecurityTrimming = bool.Parse(config["securityTrimmingEnabled"]); } base.Initialize(name, config); // Throw an exception if unrecognized attributes remain - if (config.Count > 0) { + if (config.Count > 0) + { string attr = config.GetKey(0); - if (!String.IsNullOrEmpty(attr)) + if (string.IsNullOrEmpty(attr) == false) throw new ProviderException - (String.Format("Unrecognized attribute: {0}", attr)); + (string.Format("Unrecognized attribute: {0}", attr)); } } + public override SiteMapNode BuildSiteMap() + { + lock (this) + { + if (_root != null) + return _root; - - public override System.Web.SiteMapNode BuildSiteMap() { - lock (this) { - if (m_root != null) - return m_root; - - m_root = createNode("-1", "root", "umbraco root", "/", null); - try { - AddNode(m_root, null); - } catch (Exception ex) { + _root = CreateNode("-1", "root", "umbraco root", "/", null); + try + { + AddNode(_root, null); + } + catch (Exception ex) + { LogHelper.Error("Error adding to SiteMapProvider", ex); } - loadNodes(m_root.Key, m_root); + LoadNodes(_root.Key, _root); } - return m_root; + return _root; } - public void UpdateNode(NodeFactory.Node node) { - lock (this) { + public void UpdateNode(NodeFactory.Node node) + { + lock (this) + { // validate sitemap BuildSiteMap(); SiteMapNode n; - if (!m_nodes.ContainsKey(node.Id.ToString())) { - n = createNode(node.Id.ToString(), + if (_nodes.ContainsKey(node.Id.ToString()) == false) + { + n = CreateNode(node.Id.ToString(), node.Name, - node.GetProperty(m_defaultDescriptionAlias) != null ? node.GetProperty(m_defaultDescriptionAlias).Value : "", + node.GetProperty(_defaultDescriptionAlias) != null ? node.GetProperty(_defaultDescriptionAlias).Value : "", node.Url, - findRoles(node.Id, node.Path)); + FindRoles(node.Id, node.Path)); string parentNode = node.Parent == null ? "-1" : node.Parent.Id.ToString(); - try { - AddNode(n, m_nodes[parentNode]); - } catch (Exception ex) { + try + { + AddNode(n, _nodes[parentNode]); + } + catch (Exception ex) + { LogHelper.Error(String.Format("Error adding node with url '{0}' and Id {1} to SiteMapProvider", node.Name, node.Id), ex); } - } else { - n = m_nodes[node.Id.ToString()]; + } + else + { + n = _nodes[node.Id.ToString()]; n.Url = node.Url; - n.Description = node.GetProperty(m_defaultDescriptionAlias) != null ? node.GetProperty(m_defaultDescriptionAlias).Value : ""; + n.Description = node.GetProperty(_defaultDescriptionAlias) != null ? node.GetProperty(_defaultDescriptionAlias).Value : ""; n.Title = node.Name; - n.Roles = findRoles(node.Id, node.Path).Split(",".ToCharArray()); + n.Roles = FindRoles(node.Id, node.Path).Split(",".ToCharArray()); } } } - public void RemoveNode(int nodeId) { - lock (this) { - if (m_nodes.ContainsKey(nodeId.ToString())) - RemoveNode(m_nodes[nodeId.ToString()]); + public void RemoveNode(int nodeId) + { + lock (this) + { + if (_nodes.ContainsKey(nodeId.ToString())) + RemoveNode(_nodes[nodeId.ToString()]); } } - private void loadNodes(string parentId, SiteMapNode parentNode) { - lock (this) { + private void LoadNodes(string parentId, SiteMapNode parentNode) + { + lock (this) + { NodeFactory.Node n = new NodeFactory.Node(int.Parse(parentId)); foreach (NodeFactory.Node child in n.Children) { - string roles = findRoles(child.Id, child.Path); - SiteMapNode childNode = createNode( + string roles = FindRoles(child.Id, child.Path); + SiteMapNode childNode = CreateNode( child.Id.ToString(), child.Name, - child.GetProperty(m_defaultDescriptionAlias) != null ? child.GetProperty(m_defaultDescriptionAlias).Value : "", + child.GetProperty(_defaultDescriptionAlias) != null ? child.GetProperty(_defaultDescriptionAlias).Value : "", child.Url, roles); - try { + try + { AddNode(childNode, parentNode); - } catch (Exception ex) { + } + catch (Exception ex) + { LogHelper.Error(string.Format("Error adding node {0} to SiteMapProvider in loadNodes()", child.Id), ex); } - loadNodes(child.Id.ToString(), childNode); + LoadNodes(child.Id.ToString(), childNode); } } } - private string findRoles(int nodeId, string nodePath) { + private string FindRoles(int nodeId, string nodePath) + { // check for roles string roles = ""; - if (m_enableSecurityTrimming && !String.IsNullOrEmpty(nodePath) && nodePath.Length > 0) { + if (_enableSecurityTrimming && !string.IsNullOrEmpty(nodePath) && nodePath.Length > 0) + { string[] roleArray = cms.businesslogic.web.Access.GetAccessingMembershipRoles(nodeId, nodePath); if (roleArray != null) roles = String.Join(",", roleArray); @@ -142,13 +167,15 @@ namespace umbraco.presentation.nodeFactory { return roles; } - protected override SiteMapNode GetRootNodeCore() { + protected override SiteMapNode GetRootNodeCore() + { BuildSiteMap(); - return m_root; + return _root; } - public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node) { - if (!m_enableSecurityTrimming) + public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node) + { + if (!_enableSecurityTrimming) return true; if (node.Roles == null || node.Roles.Count == -1) @@ -164,9 +191,11 @@ namespace umbraco.presentation.nodeFactory { return false; } - private SiteMapNode createNode(string id, string name, string description, string url, string roles) { - lock (this) { - if (m_nodes.ContainsKey(id)) + private SiteMapNode CreateNode(string id, string name, string description, string url, string roles) + { + lock (this) + { + if (_nodes.ContainsKey(id)) throw new ProviderException(String.Format("A node with id '{0}' already exists", id)); // Get title, URL, description, and roles from the DataReader @@ -178,21 +207,11 @@ namespace umbraco.presentation.nodeFactory { // Create a SiteMapNode SiteMapNode node = new SiteMapNode(this, id, url, name, description, rolelist, null, null, null); - m_nodes.Add(id, node); + _nodes.Add(id, node); // Return the node return node; } } - - - private SiteMapNode GetParentNode(string id) { - - // Make sure the parent ID is valid - if (!m_nodes.ContainsKey(id)) - throw new ProviderException(String.Format("No parent with id '{0}' is found", id)); - // Return the parent SiteMapNode - return m_nodes[id]; - } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx index 7d386232d1..1ae2936713 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx @@ -4,6 +4,7 @@ <%@ Register TagPrefix="umb2" TagName="Tree" Src="../../controls/Tree/TreeControl.ascx" %> <%@ Register TagPrefix="umb3" TagName="Image" Src="../../controls/Images/ImageViewer.ascx" %> <%@ Register TagName="MediaUpload" TagPrefix="umb4" Src="../../controls/Images/UploadMediaImage.ascx" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" %> @@ -14,7 +15,10 @@ - + + + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx.designer.cs index 467d6fa5e3..1ffa914459 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertImage.aspx.designer.cs @@ -21,15 +21,6 @@ namespace umbraco.presentation.plugins.tinymce3 { /// protected global::umbraco.uicontrols.UmbracoClientDependencyLoader ClientLoader; - /// - /// JsInclude3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude3; - /// /// JsInclude9 control. /// @@ -136,7 +127,7 @@ namespace umbraco.presentation.plugins.tinymce3 { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::umbraco.controls.Images.ImageViewer ImageViewer; + protected global::System.Web.UI.UserControl ImageViewer; /// /// pp_src control. @@ -208,7 +199,7 @@ namespace umbraco.presentation.plugins.tinymce3 { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::umbraco.controls.Tree.TreeControl DialogTree; + protected global::System.Web.UI.UserControl DialogTree; /// /// pane_upload control. diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx index 24de8c77b9..8f0a32ca16 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx @@ -2,6 +2,7 @@ <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register Src="../../controls/Tree/TreeControl.ascx" TagName="TreeControl" TagPrefix="umbraco" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" %> @@ -14,7 +15,10 @@ - + + + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx.designer.cs index c3fc0b59ef..6377250720 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertLink.aspx.designer.cs @@ -30,15 +30,6 @@ namespace umbraco.presentation.plugins.tinymce3 { /// protected global::umbraco.uicontrols.UmbracoClientDependencyLoader ClientLoader; - /// - /// JsInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; - /// /// JsInclude1 control. /// @@ -136,7 +127,7 @@ namespace umbraco.presentation.plugins.tinymce3 { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::umbraco.controls.Tree.TreeControl TreeControl2; + protected global::System.Web.UI.UserControl TreeControl2; /// /// pane_media control. @@ -154,6 +145,6 @@ namespace umbraco.presentation.plugins.tinymce3 { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::umbraco.controls.Tree.TreeControl TreeControl1; + protected global::System.Web.UI.UserControl TreeControl1; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertMacro.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertMacro.aspx index ca3e9f2bcd..49f12277e2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertMacro.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/plugins/tinymce3/insertMacro.aspx @@ -4,6 +4,7 @@ <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" %> @@ -11,8 +12,11 @@ - + + + + + protected global::umbraco.uicontrols.UmbracoClientDependencyLoader ClientLoader; - /// - /// JsInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; - /// /// JsInclude8 control. /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditMediaType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditMediaType.aspx.cs index a20106a0cb..93e060c01d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditMediaType.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditMediaType.aspx.cs @@ -48,24 +48,5 @@ namespace umbraco.cms.presentation.settings return false; } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs index 3dd21c5082..3773f137c6 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditNodeTypeNew.aspx.cs @@ -6,6 +6,7 @@ using System.Web.UI.WebControls; using umbraco.cms.presentation.Trees; using umbraco.cms.businesslogic.web; using System.Linq; +using umbraco.controls; namespace umbraco.settings { @@ -42,9 +43,10 @@ namespace umbraco.settings protected override bool OnBubbleEvent(object source, EventArgs args) { bool handled = false; - if (args is controls.SaveClickEventArgs) + var eventArgs = args as SaveClickEventArgs; + if (eventArgs != null) { - var e = (controls.SaveClickEventArgs)args; + var e = eventArgs; if (e.Message == "Saved") { ClientTools.ShowSpeechBubble(e.IconType, ui.Text("contentTypeSavedHeader"), ""); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx index 2e191230f5..797ca0d9bc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/editLanguage.aspx @@ -3,8 +3,7 @@ <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs index 00d1cd76f4..2e5749c4f9 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs @@ -1,9 +1,13 @@ using System; using System.Data; +using System.Globalization; using System.IO; using System.Text; using System.Xml; +using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Web; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; @@ -191,22 +195,15 @@ namespace umbraco.presentation.translation if (t != null) { //user auth and content node validation - if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == base.getUser().Id || t.ParentUser.Id == base.getUser().Id)) + if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == UmbracoUser.Id || t.ParentUser.Id == UmbracoUser.Id)) { // update node contents var d = new Document(t.Node.Id); - Document.Import(d.ParentId, getUser(), (XmlElement)taskNode); + Document.Import(d.ParentId, UmbracoUser, (XmlElement)taskNode); - /* d.Text = taskNode.Attributes.GetNamedItem("nodeName").Value.Trim(); - - // update data elements - foreach (XmlNode data in taskNode.SelectNodes("data")) - if (data.FirstChild != null) - d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = data.FirstChild.Value; - else - d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = ""; - */ + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification(d.Content, ActionTranslate.Instance, ApplicationContext); t.Closed = true; t.Save(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs index 30dbe63c58..5fbb56a5a9 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml.XPath; -using umbraco; using umbraco.NodeFactory; using Umbraco.Core; @@ -37,17 +36,17 @@ namespace umbraco XPathExpression xPathExpression; // Check to see if XPathExpression is in the cache - if (HttpContext.Current.Cache[xpath] == null) - { + if (HttpRuntime.Cache[xpath] == null) + { // Build Compiled XPath expression xPathExpression = xPathNavigator.Compile(xpath); // Store in Cache - HttpContext.Current.Cache[xpath] = xPathExpression; + HttpRuntime.Cache[xpath] = xPathExpression; } else // Get from Cache { - xPathExpression = (XPathExpression)HttpContext.Current.Cache[xpath]; + xPathExpression = (XPathExpression)HttpRuntime.Cache[xpath]; } // [LK] Interested in exploring options to call custom extension methods in XPath expressions. diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index fa2063be17..9ec1c69546 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -11,8 +11,10 @@ using System.Web.UI.WebControls; using System.Xml; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Security; using Umbraco.Web; using Umbraco.Web.Models; +using Umbraco.Web.Security; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.businesslogic.Exceptions; @@ -36,7 +38,7 @@ namespace umbraco.cms.presentation.user { public EditUser() { - CurrentApp = BusinessLogic.DefaultApps.users.ToString(); + CurrentApp = DefaultApps.users.ToString(); } protected HtmlTable macroProperties; protected TextBox uname = new TextBox(); @@ -49,8 +51,8 @@ namespace umbraco.cms.presentation.user protected CheckBox NoConsole = new CheckBox(); protected CheckBox Disabled = new CheckBox(); - protected controls.ContentPicker mediaPicker = new umbraco.controls.ContentPicker(); - protected controls.ContentPicker contentPicker = new umbraco.controls.ContentPicker(); + protected ContentPicker mediaPicker = new ContentPicker(); + protected ContentPicker contentPicker = new ContentPicker(); protected TextBox cName = new TextBox(); protected CheckBox cFulltree = new CheckBox(); @@ -58,30 +60,24 @@ namespace umbraco.cms.presentation.user protected DropDownList cDescription = new DropDownList(); protected DropDownList cCategories = new DropDownList(); protected DropDownList cExcerpt = new DropDownList(); - protected controls.ContentPicker cMediaPicker = new umbraco.controls.ContentPicker(); - protected controls.ContentPicker cContentPicker = new umbraco.controls.ContentPicker(); + protected ContentPicker cMediaPicker = new ContentPicker(); + protected ContentPicker cContentPicker = new ContentPicker(); protected CustomValidator sectionValidator = new CustomValidator(); protected Pane pp = new Pane(); private User u; + private MembershipHelper _membershipHelper; + private MembershipProvider BackOfficeProvider { - get - { - var provider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; - if (provider == null) - { - throw new ProviderException("The membership provider " + UmbracoSettings.DefaultBackofficeProvider + " was not found"); - } - return provider; - } + get { return global::Umbraco.Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); } } protected void Page_Load(object sender, EventArgs e) { - + _membershipHelper = new MembershipHelper(UmbracoContext.Current); int UID = int.Parse(Request.QueryString["id"]); u = BusinessLogic.User.GetUser(UID); @@ -118,13 +114,23 @@ namespace umbraco.cms.presentation.user { XmlDocument x = new XmlDocument(); x.Load(f); - ListItem li = - new ListItem(x.DocumentElement.Attributes.GetNamedItem("intName").Value, - x.DocumentElement.Attributes.GetNamedItem("alias").Value); - if (x.DocumentElement.Attributes.GetNamedItem("alias").Value == u.Language) - li.Selected = true; - userLanguage.Items.Add(li); + var alias = x.DocumentElement.Attributes.GetNamedItem("alias").Value; + + //ensure that only unique languages are added + if (userLanguage.Items.FindByValue(alias) == null) + { + ListItem li = + new ListItem(x.DocumentElement.Attributes.GetNamedItem("intName").Value, + alias); + + + if (x.DocumentElement.Attributes.GetNamedItem("alias").Value == u.Language) + li.Selected = true; + + userLanguage.Items.Add(li); + } + } // Console access and disabling @@ -158,14 +164,6 @@ namespace umbraco.cms.presentation.user var passwordChanger = (passwordChanger) LoadControl(SystemDirectories.Umbraco + "/controls/passwordChanger.ascx"); passwordChanger.MembershipProviderName = UmbracoSettings.DefaultBackofficeProvider; - //This is a hack to allow the admin to change a user's password to whatever they want - this will only work if we are using the - // default umbraco membership provider. - // See the notes below in the ChangePassword method. - if (BackOfficeProvider is UsersMembershipProvider) - { - passwordChanger.ShowOldPassword = false; - } - //Add a custom validation message for the password changer var passwordValidation = new CustomValidator { @@ -196,10 +194,9 @@ namespace umbraco.cms.presentation.user Pane ppNodes = new Pane(); ppNodes.addProperty(ui.Text("user", "startnode", UmbracoUser), content); ppNodes.addProperty(ui.Text("user", "mediastartnode", UmbracoUser), medias); - + //Generel umrbaco access Pane ppAccess = new Pane(); - ppAccess.addProperty(ui.Text("user", "noConsole", UmbracoUser), NoConsole); ppAccess.addProperty(ui.Text("user", "disabled", UmbracoUser), Disabled); @@ -211,11 +208,12 @@ namespace umbraco.cms.presentation.user TabPage userInfo = UserTabs.NewTabPage(u.Name); userInfo.Controls.Add(pp); - userInfo.Controls.Add(ppNodes); + userInfo.Controls.Add(ppAccess); - userInfo.Controls.Add(ppModules); - userInfo.Style.Add("text-align", "center"); + userInfo.Controls.Add(ppNodes); + userInfo.Controls.Add(ppModules); + userInfo.HasMenu = true; var save = userInfo.Menu.NewButton(); @@ -229,6 +227,7 @@ namespace umbraco.cms.presentation.user sectionValidator.ControlToValidate = lapps.ID; sectionValidator.ErrorMessage = ui.Text("errorHandling", "errorMandatoryWithoutTab", ui.Text("user", "modules", UmbracoUser), UmbracoUser); sectionValidator.CssClass = "error"; + sectionValidator.Style.Add("color", "red"); SetupForm(); SetupChannel(); @@ -350,23 +349,11 @@ namespace umbraco.cms.presentation.user if (!IsPostBack) { - MembershipUser user = BackOfficeProvider.GetUser(u.LoginName, true); + MembershipUser user = BackOfficeProvider.GetUser(u.LoginName, false); uname.Text = u.Name; lname.Text = (user == null) ? u.LoginName : user.UserName; email.Text = (user == null) ? u.Email : user.Email; - // Prevent users from changing information if logged in through active directory membership provider - // active directory-mapped accounts have empty passwords by default... so set update user fields to read only - // this will not be a security issue because empty passwords are not allowed in membership provider. - // This might change in version 4.0 - if (string.IsNullOrEmpty(u.GetPassword())) - { - uname.ReadOnly = true; - lname.ReadOnly = true; - email.ReadOnly = true; - passw.Visible = false; - } - contentPicker.Value = u.StartNodeId.ToString(CultureInfo.InvariantCulture); mediaPicker.Value = u.StartMediaId.ToString(CultureInfo.InvariantCulture); @@ -425,22 +412,8 @@ namespace umbraco.cms.presentation.user var changePasswordModel = passwordChangerControl.ChangingPasswordModel; - // Is it using the default membership provider - if (BackOfficeProvider is UsersMembershipProvider) - { - //This is a total hack so that an admin can change the password without knowing the previous one - // we do this by simply passing in the already stored hashed/encrypted password in the database - - // this shouldn't be allowed but to maintain backwards compatibility we need to do this because - // this logic was previously allowed. - - //For this editor, we set the passwordChanger.ShowOldPassword = false so that the old password - // field doesn't appear because we know we are going to manually set it here. - // We'll change the model to have the already encrypted password stored in the db and that will continue to validate. - changePasswordModel.OldPassword = u.Password; - } - //now do the actual change - var changePassResult = UmbracoContext.Current.Security.ChangePassword( + var changePassResult = _membershipHelper.ChangePassword( membershipUser.UserName, changePasswordModel, BackOfficeProvider); if (changePassResult.Success) @@ -470,7 +443,7 @@ namespace umbraco.cms.presentation.user { try { - var membershipUser = BackOfficeProvider.GetUser(u.LoginName, true); + var membershipUser = BackOfficeProvider.GetUser(u.LoginName, false); if (membershipUser == null) { throw new ProviderException("Could not find user in the membership provider with login name " + u.LoginName); @@ -481,41 +454,22 @@ namespace umbraco.cms.presentation.user //perform the changing password logic ChangePassword(passwordChangerControl, membershipUser, passwordChangerValidator); - - // Is it using the default membership provider - if (BackOfficeProvider is UsersMembershipProvider) - { - // Save user in membership provider - UsersMembershipUser umbracoUser = membershipUser as UsersMembershipUser; - umbracoUser.FullName = uname.Text.Trim(); - umbracoUser.Language = userLanguage.SelectedValue; - umbracoUser.UserType = UserType.GetUserType(int.Parse(userType.SelectedValue)); - BackOfficeProvider.UpdateUser(umbracoUser); - - // Save user details - u.Email = email.Text.Trim(); - u.Language = userLanguage.SelectedValue; - } - else - { - u.Name = uname.Text.Trim(); - u.Language = userLanguage.SelectedValue; - u.UserType = UserType.GetUserType(int.Parse(userType.SelectedValue)); - //SD: This check must be here for some reason but apparently we don't want to try to - // update when the AD provider is active. - if ((BackOfficeProvider is ActiveDirectoryMembershipProvider) == false) - { - BackOfficeProvider.UpdateUser(membershipUser); - } - } + //update the membership provider + UpdateMembershipProvider(membershipUser); + //update the Umbraco user properties - even though we are updating some of these properties in the membership provider that is + // ok since the membership provider might be storing these details someplace totally different! But we want to keep our UI in sync. + u.Name = uname.Text.Trim(); + u.Language = userLanguage.SelectedValue; + u.UserType = UserType.GetUserType(int.Parse(userType.SelectedValue)); + u.Email = email.Text.Trim(); u.LoginName = lname.Text; - //u.StartNodeId = int.Parse(startNode.Value); - + u.Disabled = Disabled.Checked; + u.NoConsole = NoConsole.Checked; int startNode; - if (!int.TryParse(contentPicker.Value, out startNode)) + if (int.TryParse(contentPicker.Value, out startNode) == false) { //set to default if nothing is choosen if (u.StartNodeId > 0) @@ -523,17 +477,11 @@ namespace umbraco.cms.presentation.user else startNode = -1; } - u.StartNodeId = startNode; - - - u.Disabled = Disabled.Checked; + u.StartNodeId = startNode; + - u.NoConsole = NoConsole.Checked; - //u.StartMediaId = int.Parse(mediaStartNode.Value); - - int mstartNode; - if (!int.TryParse(mediaPicker.Value, out mstartNode)) + if (int.TryParse(mediaPicker.Value, out mstartNode) == false) { //set to default if nothing is choosen if (u.StartMediaId > 0) @@ -544,7 +492,6 @@ namespace umbraco.cms.presentation.user u.StartMediaId = mstartNode; u.clearApplications(); - foreach (ListItem li in lapps.Items) { if (li.Selected) u.addApplication(li.Value); @@ -598,6 +545,20 @@ namespace umbraco.cms.presentation.user } } + private void UpdateMembershipProvider(MembershipUser membershipUser) + { + //SD: This check must be here for some reason but apparently we don't want to try to + // update when the AD provider is active. + if ((BackOfficeProvider is ActiveDirectoryMembershipProvider) == false) + { + var membershipHelper = new MembershipHelper(ApplicationContext, new HttpContextWrapper(Context)); + //set the writable properties that we are editing + membershipHelper.UpdateMember(membershipUser, BackOfficeProvider, + email.Text.Trim(), + Disabled.Checked == false); + } + } + /// /// UserTabs control. /// @@ -605,6 +566,6 @@ namespace umbraco.cms.presentation.user /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::umbraco.uicontrols.TabView UserTabs; + protected TabView UserTabs; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx.cs index 20bad2ea93..ae98eb5c2c 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/PermissionEditor.aspx.cs @@ -72,7 +72,7 @@ namespace umbraco.cms.presentation.user } /// - /// Since Umbraco stores users in cache, we'll use this method to retreive our user object by the selected id + /// Since Umbraco stores users in cache, we'll use this method to retrieve our user object by the selected id /// protected umbraco.BusinessLogic.User UmbracoUser { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs index b8df81f72b..33818d43df 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs @@ -8,6 +8,7 @@ using System.Collections; using System.Web.UI.WebControls; using System.Data.SqlClient; using System.Data; +using Umbraco.Core; using Umbraco.Web; using Umbraco.Web.Security; using umbraco; @@ -27,17 +28,11 @@ namespace umbraco.cms.presentation.user /// public class UserPermissions { - - User m_user; + readonly User _user; public UserPermissions(User user) { - m_user = user; - } - - private static ISqlHelper SqlHelper - { - get { return Application.SqlHelper; } + _user = user; } /// @@ -70,31 +65,32 @@ namespace umbraco.cms.presentation.user //get the complete list of node ids that this change will affect var allNodes = new List(); if (replaceChildren) + { foreach (var nodeId in nodeIDs) { allNodes.Add(nodeId); allNodes.AddRange(FindChildNodes(nodeId)); } + } else + { allNodes.AddRange(nodeIDs); - - //First remove all permissions for all nodes in question - Permission.DeletePermissions(m_user.Id, allNodes.ToArray()); + } //if permissions are to be assigned, then assign them if (permissions.Count > 0) { - foreach (var oPer in permissions) - { - InsertPermissions(allNodes.ToArray(), oPer); - } + ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( + _user.Id, permissions.Select(x => x.Letter), allNodes.ToArray()); } else { //If there are NO permissions for this node, we need to assign the ActionNull permission otherwise //the node will inherit from it's parent. - InsertPermissions(nodeIDs, ActionNull.Instance); + ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( + _user.Id, new[] { ActionNull.Instance.Letter }, allNodes.ToArray()); } + } /// @@ -108,7 +104,7 @@ namespace umbraco.cms.presentation.user if (path != "") { //get the user and their permissions - string permissions = m_user.GetPermissions(path); + string permissions = _user.GetPermissions(path); return umbraco.BusinessLogic.Actions.Action.FromString(permissions); } return null; @@ -149,11 +145,5 @@ namespace umbraco.cms.presentation.user } return nodeIds; } - - private void InsertPermissions(IEnumerable nodeIDs, IAction permission) - { - Permission.MakeNew(m_user, nodeIDs.Select(x => new CMSNode(x)), permission.Letter, true); - } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs index 7f79097615..4c2f721bd2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs @@ -115,7 +115,7 @@ namespace umbraco.presentation.webservices { var xd = new XmlDocument(); xd.LoadXml(""); - foreach (var cr in CacheRefreshersResolver.Current.CacheResolvers) + foreach (var cr in CacheRefreshersResolver.Current.CacheRefreshers) { var n = xmlHelper.addTextNode(xd, "cacheRefresher", cr.Name); n.Attributes.Append(xmlHelper.addAttribute(xd, "uniqueIdentifier", cr.UniqueIdentifier.ToString())); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs index bf2ea8502f..2d6beced38 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs @@ -26,8 +26,8 @@ namespace umbraco.presentation.webservices { if (!AuthorizeRequest()) return null; - var check = new global::umbraco.presentation.org.umbraco.update.CheckForUpgrade(); - org.umbraco.update.UpgradeResult result = check.CheckUpgrade(UmbracoVersion.Current.Major, + var check = new global::Umbraco.Web.org.umbraco.update.CheckForUpgrade(); + var result = check.CheckUpgrade(UmbracoVersion.Current.Major, UmbracoVersion.Current.Minor, UmbracoVersion.Current.Build, UmbracoVersion.CurrentComment); @@ -74,7 +74,7 @@ namespace umbraco.presentation.webservices if (!String.IsNullOrEmpty(global::Umbraco.Core.Configuration.GlobalSettings.ConfigurationStatus)) dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString(); - org.umbraco.update.CheckForUpgrade check = new global::umbraco.presentation.org.umbraco.update.CheckForUpgrade(); + var check = new global::Umbraco.Web.org.umbraco.update.CheckForUpgrade(); check.Install(installId, isUpgrade, isCompleted, diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs index 237c31a708..2a5d23bee3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs @@ -16,6 +16,7 @@ using umbraco.BusinessLogic; using umbraco.businesslogic.Exceptions; using umbraco.cms.businesslogic.media; using Umbraco.Core; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.webservices { @@ -224,7 +225,7 @@ namespace umbraco.presentation.umbraco.webservices if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { - var mp = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; + var mp = MembershipProviderExtensions.GetUsersMembershipProvider(); if (mp != null && mp.ValidateUser(username, password)) { var user = new User(username); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs index a6e2d7e402..1dfa3362da 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs @@ -7,8 +7,12 @@ using System.Linq; using System.Web.Script.Services; using System.Web.Services; using System.Xml; +using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Web; using Umbraco.Web.WebServices; using umbraco.BasePages; using umbraco.BusinessLogic; @@ -160,12 +164,16 @@ namespace umbraco.presentation.webservices if (parentNode != null) content.SortNodes(ref parentNode); + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification(contentService.GetById(parentId), ActionSort.Instance, UmbracoContext, ApplicationContext); + } catch (Exception ex) { LogHelper.Error("Could not update content sort order", ex); } } + } [Serializable] diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj index db7b95bd7a..0e163249ba 100644 --- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj +++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj @@ -1,4 +1,4 @@ - + Debug @@ -40,11 +40,11 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll - + False ..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll @@ -88,6 +88,7 @@ + diff --git a/src/UmbracoExamine.Azure/app.config b/src/UmbracoExamine.Azure/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/UmbracoExamine.Azure/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UmbracoExamine.Azure/packages.config b/src/UmbracoExamine.Azure/packages.config index 54eb3830da..4dce0f43bc 100644 --- a/src/UmbracoExamine.Azure/packages.config +++ b/src/UmbracoExamine.Azure/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj index d17a4c4921..56d666f9b2 100644 --- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj +++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj @@ -1,4 +1,4 @@ - + Debug @@ -40,11 +40,11 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll - + False ..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll @@ -89,6 +89,7 @@ + diff --git a/src/UmbracoExamine.PDF.Azure/app.config b/src/UmbracoExamine.PDF.Azure/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/UmbracoExamine.PDF.Azure/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UmbracoExamine.PDF.Azure/packages.config b/src/UmbracoExamine.PDF.Azure/packages.config index 54eb3830da..4dce0f43bc 100644 --- a/src/UmbracoExamine.PDF.Azure/packages.config +++ b/src/UmbracoExamine.PDF.Azure/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj index 6bdc3d3666..217c735bc5 100644 --- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj +++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj @@ -1,4 +1,4 @@ - + Debug @@ -46,9 +46,9 @@ false - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll @@ -78,6 +78,7 @@ + diff --git a/src/UmbracoExamine.PDF/app.config b/src/UmbracoExamine.PDF/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/UmbracoExamine.PDF/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UmbracoExamine.PDF/packages.config b/src/UmbracoExamine.PDF/packages.config index 1d29bc3cc6..1169a8ab9c 100644 --- a/src/UmbracoExamine.PDF/packages.config +++ b/src/UmbracoExamine.PDF/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/UmbracoExamine/DataServices/UmbracoContentService.cs b/src/UmbracoExamine/DataServices/UmbracoContentService.cs index fe55a9963d..93029091cc 100644 --- a/src/UmbracoExamine/DataServices/UmbracoContentService.cs +++ b/src/UmbracoExamine/DataServices/UmbracoContentService.cs @@ -96,7 +96,8 @@ namespace UmbracoExamine.DataServices [SecuritySafeCritical] private static XmlNode GetPage(int documentId) { - var x = Access.AccessXml.SelectSingleNode("/access/page [@id=" + documentId.ToString() + "]"); + var xDoc = Access.GetXmlDocumentCopy(); + var x = xDoc.SelectSingleNode("/access/page [@id=" + documentId.ToString() + "]"); return x; } diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index c180189a64..5a0df8800a 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -1,4 +1,4 @@ - + Debug @@ -82,9 +82,9 @@ ..\Solution Items\TheFARM-Public.snk - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False @@ -171,6 +171,7 @@ + diff --git a/src/UmbracoExamine/app.config b/src/UmbracoExamine/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/UmbracoExamine/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UmbracoExamine/packages.config b/src/UmbracoExamine/packages.config index 31026832a5..e923fba924 100644 --- a/src/UmbracoExamine/packages.config +++ b/src/UmbracoExamine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs index c748605264..d841f9ce3a 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs @@ -32,6 +32,15 @@ namespace umbraco.MacroEngines } public DynamicBackingItem(int Id) { + if (Id == -1) + { + // passing in -1 needs to return a real node, the "root" node, which has no + // properties (defaults apply) but can be used to access descendants, children, etc. + + this.content = new NodeFactory.Node(Id); + return; + } + var n = CompatibilityHelper.ConvertToNode(UmbracoContext.Current.ContentCache.GetById(Id)); this.content = n; @@ -57,7 +66,18 @@ namespace umbraco.MacroEngines } else { - this.content = CompatibilityHelper.ConvertToNode(UmbracoContext.Current.ContentCache.GetById(Id)); + if (Id == -1) + { + // passing in -1 needs to return a real node, the "root" node, which has no + // properties (defaults apply) but can be used to access descendants, children, etc. + + this.content = new NodeFactory.Node(Id); + } + else + { + this.content = CompatibilityHelper.ConvertToNode(UmbracoContext.Current.ContentCache.GetById(Id)); + } + this.Type = Type; } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index b46187ef94..050c8cf224 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -657,36 +657,8 @@ namespace umbraco.MacroEngines private object GetReflectedProperty(string alias) { - Func> getMember = - memberAlias => - { - try - { - return Attempt.Succeed( - n.GetType().InvokeMember(memberAlias, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public, - null, - n, - null)); - } - catch (MissingMethodException ex) - { - return Attempt.Fail(ex); - } - }; - - //try with the current casing - var attempt = getMember(alias); - if (!attempt.Success) - { - //if we cannot get with the current alias, try changing it's case - attempt = alias[0].IsUpperCase() - ? getMember(alias.ToCleanString(CleanStringType.Ascii | CleanStringType.Alias | CleanStringType.CamelCase)) - : getMember(alias.ToCleanString(CleanStringType.Ascii | CleanStringType.Alias | CleanStringType.PascalCase)); - } - + var attempt = n.GetType().GetMemberIgnoreCase(n, alias); + return attempt.Success ? attempt.Result : null; } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index 6ab1bad3d8..d9d93a458e 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -33,13 +33,13 @@ namespace umbraco.MacroEngines.Library public dynamic NodeById(int Id) { var node = new DynamicNode(Id); - if (node != null && node.Id == 0) return new DynamicNull(); + if (node.Id == 0) return new DynamicNull(); return node; } public dynamic NodeById(string Id) { var node = new DynamicNode(Id); - if (node != null && node.Id == 0) return new DynamicNull(); + if (node.Id == 0) return new DynamicNull(); return node; } public dynamic NodeById(DynamicNull Id) @@ -53,7 +53,7 @@ namespace umbraco.MacroEngines.Library return new DynamicNull(); } var node = new DynamicNode(Id); - if (node != null && node.Id == 0) return new DynamicNull(); + if (node.Id == 0) return new DynamicNull(); return node; } public dynamic NodesById(List Ids) diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index 992451e496..17608e428b 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,6 +1,6 @@  - + @@ -14,6 +14,6 @@ - + \ No newline at end of file diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index 6dec8e9ee7..cf88d26017 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -1,4 +1,4 @@ - + Debug @@ -45,9 +45,9 @@ false - + False - ..\packages\Examine.0.1.52.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False @@ -65,9 +65,9 @@ True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - + False - ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll diff --git a/src/umbraco.businesslogic/ApplicationTree.cs b/src/umbraco.businesslogic/ApplicationTree.cs index 6458885edd..2c015c9b05 100644 --- a/src/umbraco.businesslogic/ApplicationTree.cs +++ b/src/umbraco.businesslogic/ApplicationTree.cs @@ -84,6 +84,17 @@ namespace umbraco.BusinessLogic /// The type. public string Type { get; set; } + private Type _runtimeType; + + /// + /// Returns the CLR type based on it's assembly name stored in the config + /// + /// + internal Type GetRuntimeType() + { + return _runtimeType ?? (_runtimeType = System.Type.GetType(Type)); + } + /// /// Gets or sets the default tree action. /// diff --git a/src/umbraco.businesslogic/StateHelper.cs b/src/umbraco.businesslogic/StateHelper.cs index 10e81dd614..e84d3aa129 100644 --- a/src/umbraco.businesslogic/StateHelper.cs +++ b/src/umbraco.businesslogic/StateHelper.cs @@ -349,7 +349,7 @@ namespace umbraco.BusinessLogic * we currently reproduce this by configuring each cookie with a 30d expires, but does * that actually make sense? shouldn't some cookie have _no_ expires? */ - static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, 30d); // was "PreviewSet" + static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, TimeSpan.Zero); // was "PreviewSet" static readonly Cookie _userContext = new Cookie(Constants.Web.AuthCookieName, 30d); // was "UserContext" static readonly Cookie _member = new Cookie("UMB_MEMBER", 30d); // was "umbracoMember" @@ -431,7 +431,7 @@ namespace umbraco.BusinessLogic } public void SetValue(string value) { - SetValueWithDate(value, DateTime.Now + _expires); + SetValueWithDate(value, _expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + _expires); } public void SetValue(string value, double days) @@ -441,7 +441,7 @@ namespace umbraco.BusinessLogic public void SetValue(string value, TimeSpan expires) { - SetValue(value, DateTime.Now + expires); + SetValue(value, expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + expires); } public void SetValue(string value, DateTime expires) @@ -451,7 +451,7 @@ namespace umbraco.BusinessLogic private void SetValueWithDate(string value, DateTime expires) { - HttpCookie cookie = new HttpCookie(_key, value); + var cookie = new HttpCookie(_key, value); if (GlobalSettings.UseSSL) cookie.Secure = true; @@ -459,7 +459,12 @@ namespace umbraco.BusinessLogic //ensure http only, this should only be able to be accessed via the server cookie.HttpOnly = true; - cookie.Expires = expires; + //set an expiry date if not min value, otherwise leave it as a session cookie. + if (expires != DateTime.MinValue) + { + cookie.Expires = expires; + } + ResponseCookie = cookie; // original Umbraco code also does this @@ -473,7 +478,7 @@ namespace umbraco.BusinessLogic { if (RequestCookie != null || ResponseCookie != null) { - HttpCookie cookie = new HttpCookie(_key); + var cookie = new HttpCookie(_key); cookie.Expires = DateTime.Now.AddDays(-1); ResponseCookie = cookie; } diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index 6c84ef6fd8..ffa6c790e0 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -5,6 +5,9 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Querying; using umbraco.DataLayer; using System.Collections.Generic; using System.Linq; @@ -15,23 +18,17 @@ namespace umbraco.BusinessLogic /// /// represents a Umbraco back end user /// + [Obsolete("Use the UserService instead")] public class User { - private int _id; - private bool _isInitialized; - private string _name; - private string _loginname; - private int _startnodeid; - private int _startmediaid; - private string _email; - private string _language = ""; - private UserType _usertype; - private bool _userNoConsole; - private bool _userDisabled; + private IUser _user; + private int? _lazyId; + private bool? _defaultToLiveEditing; - private Hashtable _notifications = new Hashtable(); + private readonly Hashtable _notifications = new Hashtable(); private bool _notificationsInitialized = false; + [Obsolete("Obsolete, For querying the database use the new UmbracoDatabase object ApplicationContext.Current.DatabaseContext.Database", false)] private static ISqlHelper SqlHelper { get { return Application.SqlHelper; } @@ -39,19 +36,7 @@ namespace umbraco.BusinessLogic internal User(IUser user) { - _id = (int)user.Id; - _userNoConsole = user.NoConsole; - _userDisabled = user.IsLockedOut; - _name = user.Name; - _loginname = user.Username; - _email = user.Email; - _language = user.Language; - _startnodeid = user.StartContentId; - _startmediaid = user.StartMediaId; - //this is cached, so should be 'ok' - _usertype = UserType.GetUserType(user.UserType.Id); - - _isInitialized = true; + _user = user; } /// @@ -60,7 +45,7 @@ namespace umbraco.BusinessLogic /// The ID. public User(int ID) { - setupUser(ID); + SetupUser(ID); } /// @@ -70,7 +55,7 @@ namespace umbraco.BusinessLogic /// if set to true [no setup]. public User(int ID, bool noSetup) { - _id = ID; + _lazyId = ID; } /// @@ -80,7 +65,7 @@ namespace umbraco.BusinessLogic /// The password. public User(string Login, string Password) { - setupUser(getUserId(Login, Password)); + SetupUser(getUserId(Login, Password)); } /// @@ -89,43 +74,27 @@ namespace umbraco.BusinessLogic /// The login. public User(string Login) { - setupUser(getUserId(Login)); + SetupUser(getUserId(Login)); } - private void setupUser(int ID) + private void SetupUser(int ID) { - _id = ID; - - using (IRecordsReader dr = SqlHelper.ExecuteReader( - "Select userNoConsole, userDisabled, userType,startStructureID, startMediaId, userName,userLogin,userEmail, userLanguage from umbracoUser where id = @id", - SqlHelper.CreateParameter("@id", ID))) + _user = ApplicationContext.Current.Services.UserService.GetUserById(ID); + if (_user == null) { - if (dr.Read()) - { - _userNoConsole = dr.GetBoolean("usernoconsole"); - _userDisabled = dr.GetBoolean("userDisabled"); - _name = dr.GetString("userName"); - _loginname = dr.GetString("userLogin"); - _email = dr.GetString("userEmail"); - _language = dr.GetString("userLanguage"); - _startnodeid = dr.GetInt("startStructureID"); - if (!dr.IsNull("startMediaId")) - _startmediaid = dr.GetInt("startMediaID"); - _usertype = UserType.GetUserType(dr.GetShort("UserType")); - } - else - { - throw new ArgumentException("No User exists with ID " + ID.ToString()); - } + throw new ArgumentException("No User exists with ID " + ID); } - _isInitialized = true; } /// - /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility + /// Used to persist object changes to the database. /// public void Save() { + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + + ApplicationContext.Current.Services.UserService.Save(_user); + OnSaving(EventArgs.Empty); } @@ -137,15 +106,13 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _name; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.Name; } set { - _name = value; - SqlHelper.ExecuteNonQuery("Update umbracoUser set UserName = @userName where id = @id", SqlHelper.CreateParameter("@userName", value), SqlHelper.CreateParameter("@id", Id)); - FlushFromCache(); + _user.Name = value; + } } @@ -157,15 +124,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _email; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.Email; } set { - _email = value; - SqlHelper.ExecuteNonQuery("Update umbracoUser set UserEmail = @email where id = @id", SqlHelper.CreateParameter("@id", this.Id), SqlHelper.CreateParameter("@email", value)); - FlushFromCache(); + _user.Email = value; } } @@ -177,15 +141,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _language; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.Language; } set { - _language = value; - SqlHelper.ExecuteNonQuery("Update umbracoUser set userLanguage = @language where id = @id", SqlHelper.CreateParameter("@language", value), SqlHelper.CreateParameter("@id", Id)); - FlushFromCache(); + _user.Language = value; } } @@ -201,8 +162,7 @@ namespace umbraco.BusinessLogic } set { - SqlHelper.ExecuteNonQuery("Update umbracoUser set UserPassword = @pw where id = @id", SqlHelper.CreateParameter("@pw", value), SqlHelper.CreateParameter("@id", Id)); - FlushFromCache(); + _user.RawPasswordValue = value; } } @@ -212,9 +172,8 @@ namespace umbraco.BusinessLogic /// public string GetPassword() { - return - SqlHelper.ExecuteScalar("select UserPassword from umbracoUser where id = @id", - SqlHelper.CreateParameter("@id", this.Id)); + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.RawPasswordValue; } /// @@ -228,13 +187,13 @@ namespace umbraco.BusinessLogic return UserType.Alias == "admin"; } + [Obsolete("Do not use this method to validate credentials, use the user's membership provider to do authentication. This method will not work if the password format is 'Encrypted'")] public bool ValidatePassword(string password) { - string userLogin = - SqlHelper.ExecuteScalar("select userLogin from umbracoUser where userLogin = @login and UserPassword = @pw", - SqlHelper.CreateParameter("@pw", password), - SqlHelper.CreateParameter("@login", LoginName) - ); + var userLogin = ApplicationContext.Current.DatabaseContext.Database.ExecuteScalar( + "SELECT userLogin FROM umbracoUser WHERE userLogin = @login AND UserPasword = @password", + new {login = LoginName, password = password}); + return userLogin == this.LoginName; } @@ -267,20 +226,18 @@ namespace umbraco.BusinessLogic /// public List GetApplications() { - if (!_isInitialized) - setupUser(_id); + if (_lazyId.HasValue) SetupUser(_lazyId.Value); var allApps = Application.getAll(); var apps = new List(); - using (IRecordsReader appIcons = SqlHelper.ExecuteReader("select app from umbracoUser2app where [user] = @userID", SqlHelper.CreateParameter("@userID", this.Id))) + var sections = _user.AllowedSections; + + foreach (var s in sections) { - while (appIcons.Read()) - { - var app = allApps.SingleOrDefault(x => x.alias == appIcons.GetString("app")); - if(app != null) - apps.Add(app); - } + var app = allApps.SingleOrDefault(x => x.alias == s); + if (app != null) + apps.Add(app); } return apps; @@ -294,21 +251,19 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _loginname; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.Username; } set { - if (!ensureUniqueLoginName(value, this)) + if (EnsureUniqueLoginName(value, this) == false) throw new Exception(String.Format("A user with the login '{0}' already exists", value)); - _loginname = value; - SqlHelper.ExecuteNonQuery("Update umbracoUser set UserLogin = @login where id = @id", SqlHelper.CreateParameter("@login", value), SqlHelper.CreateParameter("@id", Id)); - FlushFromCache(); + + _user.Username = value; } } - private static bool ensureUniqueLoginName(string loginName, User currentUser) + private static bool EnsureUniqueLoginName(string loginName, User currentUser) { User[] u = User.getAllByLoginName(loginName); if (u.Length != 0) @@ -326,6 +281,7 @@ namespace umbraco.BusinessLogic /// The login name. /// The password. /// + [Obsolete("Do not use this method to validate credentials, use the user's membership provider to do authentication. This method will not work if the password format is 'Encrypted'")] public static bool validateCredentials(string lname, string passw) { return validateCredentials(lname, passw, true); @@ -338,6 +294,7 @@ namespace umbraco.BusinessLogic /// The password. /// if set to true [check for umbraco console access]. /// + [Obsolete("Do not use this method to validate credentials, use the user's membership provider to do authentication. This method will not work if the password format is 'Encrypted'")] public static bool validateCredentials(string lname, string passw, bool checkForUmbracoConsoleAccess) { string consoleCheckSql = ""; @@ -345,7 +302,9 @@ namespace umbraco.BusinessLogic consoleCheckSql = "and userNoConsole = 0 "; object tmp = SqlHelper.ExecuteScalar( - "select id from umbracoUser where userDisabled = 0 " + consoleCheckSql + " and userLogin = @login and userPassword = @pw", SqlHelper.CreateParameter("@login", lname), SqlHelper.CreateParameter("@pw", passw) + "select id from umbracoUser where userDisabled = 0 " + consoleCheckSql + " and userLogin = @login and userPassword = @pw", + SqlHelper.CreateParameter("@login", lname), + SqlHelper.CreateParameter("@pw", passw) ); // Logging @@ -365,18 +324,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _usertype; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return new UserType(_user.UserType); } set { - _usertype = value; - SqlHelper.ExecuteNonQuery( - @"Update umbracoUser set userType = @type where id = @id", - SqlHelper.CreateParameter("@type", value.Id), - SqlHelper.CreateParameter("@id", Id)); - FlushFromCache(); + _user.UserType = value.UserTypeItem; } } @@ -387,19 +340,13 @@ namespace umbraco.BusinessLogic /// public static User[] getAll() { + int totalRecs; + var users = ApplicationContext.Current.Services.UserService.GetAll( + 0, int.MaxValue, out totalRecs); - IRecordsReader dr; - dr = SqlHelper.ExecuteReader("Select id from umbracoUser"); - - List users = new List(); - - while (dr.Read()) - { - users.Add(User.GetUser(dr.GetInt("id"))); - } - dr.Close(); - - return users.OrderBy(x => x.Name).ToArray(); + return users.Select(x => new User(x)) + .OrderBy(x => x.Name) + .ToArray(); } @@ -411,8 +358,8 @@ namespace umbraco.BusinessLogic { try { - if (umbraco.BasePages.BasePage.umbracoUserContextID != "") - return BusinessLogic.User.GetUser(umbraco.BasePages.BasePage.GetUserId(umbraco.BasePages.BasePage.umbracoUserContextID)); + if (BasePages.BasePage.umbracoUserContextID != "") + return GetUser(BasePages.BasePage.GetUserId(BasePages.BasePage.umbracoUserContextID)); else return null; } @@ -440,27 +387,21 @@ namespace umbraco.BusinessLogic /// public static User[] getAllByEmail(string email, bool useExactMatch) { - List retVal = new List(); - System.Collections.ArrayList tmpContainer = new System.Collections.ArrayList(); - - IRecordsReader dr; - - if (useExactMatch == true) - { - dr = SqlHelper.ExecuteReader("Select id from umbracoUser where userEmail = @email", SqlHelper.CreateParameter("@email", email)); - } - else - { - dr = SqlHelper.ExecuteReader("Select id from umbracoUser where userEmail LIKE {0} @email", SqlHelper.CreateParameter("@email", String.Format("%{0}%", email))); - } - - while (dr.Read()) + int totalRecs; + if (useExactMatch) { - retVal.Add(BusinessLogic.User.GetUser(dr.GetInt("id"))); + return ApplicationContext.Current.Services.UserService.FindByEmail( + email, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact) + .Select(x => new User(x)) + .ToArray(); + } + else + { + return ApplicationContext.Current.Services.UserService.FindByEmail( + string.Format("%{0}%", email), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Wildcard) + .Select(x => new User(x)) + .ToArray(); } - dr.Close(); - - return retVal.ToArray(); } /// @@ -477,7 +418,7 @@ namespace umbraco.BusinessLogic /// Gets all users by login name. /// /// The login. - /// whether to use a partial match + /// whether to use a partial match /// public static User[] getAllByLoginName(string login, bool partialMatch) { @@ -486,36 +427,21 @@ namespace umbraco.BusinessLogic public static IEnumerable GetAllByLoginName(string login, bool partialMatch) { - - var users = new List(); - + int totalRecs; if (partialMatch) { - using (var dr = SqlHelper.ExecuteReader( - "Select id from umbracoUser where userLogin LIKE @login", SqlHelper.CreateParameter("@login", String.Format("%{0}%", login)))) - { - while (dr.Read()) - { - users.Add(BusinessLogic.User.GetUser(dr.GetInt("id"))); - } - } - + return ApplicationContext.Current.Services.UserService.FindByUsername( + string.Format("%{0}%", login), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Wildcard) + .Select(x => new User(x)) + .ToArray(); } else { - using (var dr = SqlHelper.ExecuteReader( - "Select id from umbracoUser where userLogin=@login", SqlHelper.CreateParameter("@login", login))) - { - while (dr.Read()) - { - users.Add(BusinessLogic.User.GetUser(dr.GetInt("id"))); - } - } + return ApplicationContext.Current.Services.UserService.FindByUsername( + login, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact) + .Select(x => new User(x)) + .ToArray(); } - - return users; - - } /// @@ -525,21 +451,12 @@ namespace umbraco.BusinessLogic /// The login name. /// The password. /// The user type. - [MethodImpl(MethodImplOptions.Synchronized)] public static User MakeNew(string name, string lname, string passw, UserType ut) { + var user = new Umbraco.Core.Models.Membership.User(name, "", lname, passw, ut.UserTypeItem); + ApplicationContext.Current.Services.UserService.Save(user); - SqlHelper.ExecuteNonQuery(@" - insert into umbracoUser - (UserType,startStructureId,startMediaId, UserName, userLogin, userPassword, userEmail,userLanguage) - values (@type,-1,-1,@name,@lname,@pw,'',@lang)", - SqlHelper.CreateParameter("@lang", GlobalSettings.DefaultUILanguage), - SqlHelper.CreateParameter("@name", name), - SqlHelper.CreateParameter("@lname", lname), - SqlHelper.CreateParameter("@type", ut.Id), - SqlHelper.CreateParameter("@pw", passw)); - - var u = new User(lname); + var u = new User(user); u.OnNew(EventArgs.Empty); return u; @@ -553,22 +470,13 @@ namespace umbraco.BusinessLogic /// The lname. /// The passw. /// The email. - /// The ut. - [MethodImpl(MethodImplOptions.Synchronized)] + /// The ut. public static User MakeNew(string name, string lname, string passw, string email, UserType ut) { - SqlHelper.ExecuteNonQuery(@" - insert into umbracoUser - (UserType,startStructureId,startMediaId, UserName, userLogin, userPassword, userEmail,userLanguage) - values (@type,-1,-1,@name,@lname,@pw,@email,@lang)", - SqlHelper.CreateParameter("@lang", GlobalSettings.DefaultUILanguage), - SqlHelper.CreateParameter("@name", name), - SqlHelper.CreateParameter("@lname", lname), - SqlHelper.CreateParameter("@email", email), - SqlHelper.CreateParameter("@type", ut.Id), - SqlHelper.CreateParameter("@pw", passw)); + var user = new Umbraco.Core.Models.Membership.User(name, email, lname, passw, ut.UserTypeItem); + ApplicationContext.Current.Services.UserService.Save(user); - var u = new User(lname); + var u = new User(user); u.OnNew(EventArgs.Empty); return u; @@ -585,18 +493,52 @@ namespace umbraco.BusinessLogic /// The ut. public static void Update(int id, string name, string lname, string email, UserType ut) { - if (!ensureUniqueLoginName(lname, User.GetUser(id))) + if (EnsureUniqueLoginName(lname, GetUser(id)) == false) throw new Exception(String.Format("A user with the login '{0}' already exists", lname)); - - SqlHelper.ExecuteNonQuery(@"Update umbracoUser set userName=@name, userLogin=@lname, userEmail=@email, UserType=@type where id = @id", - SqlHelper.CreateParameter("@name", name), - SqlHelper.CreateParameter("@lname", lname), - SqlHelper.CreateParameter("@email", email), - SqlHelper.CreateParameter("@type", ut.Id), - SqlHelper.CreateParameter("@id", id)); + var found = ApplicationContext.Current.Services.UserService.GetUserById(id); + if (found == null) return; + found.Name = name; + found.Username = lname; + found.Email = email; + found.UserType = ut.UserTypeItem; + ApplicationContext.Current.Services.UserService.Save(found); } + public static void Update(int id, string name, string lname, string email, bool disabled, bool noConsole, UserType ut) + { + if (EnsureUniqueLoginName(lname, GetUser(id)) == false) + throw new Exception(String.Format("A user with the login '{0}' already exists", lname)); + + var found = ApplicationContext.Current.Services.UserService.GetUserById(id); + if (found == null) return; + found.Name = name; + found.Username = lname; + found.Email = email; + found.UserType = ut.UserTypeItem; + found.IsApproved = disabled == false; + found.IsLockedOut = noConsole; + ApplicationContext.Current.Services.UserService.Save(found); + } + + /// + /// Updates the membership provider properties + /// + /// The id. + /// + /// + /// + public static void Update(int id, string email, bool disabled, bool noConsole) + { + var found = ApplicationContext.Current.Services.UserService.GetUserById(id); + if (found == null) return; + + found.Email = email; + found.IsApproved = disabled == false; + found.IsLockedOut = noConsole; + ApplicationContext.Current.Services.UserService.Save(found); + } + /// /// Gets the ID from the user with the specified login name and password /// @@ -605,9 +547,8 @@ namespace umbraco.BusinessLogic /// a user ID public static int getUserId(string lname, string passw) { - return getUserId("select id from umbracoUser where userDisabled = 0 and userNoConsole = 0 and userLogin = @login and userPassword = @pw", - SqlHelper.CreateParameter("@login", lname), - SqlHelper.CreateParameter("@pw", passw)); + var found = ApplicationContext.Current.Services.UserService.GetByUsername(lname); + return found.RawPasswordValue == passw ? found.Id : -1; } /// @@ -617,16 +558,10 @@ namespace umbraco.BusinessLogic /// a user ID public static int getUserId(string lname) { - return getUserId("select id from umbracoUser where userLogin = @login", - SqlHelper.CreateParameter("@login", lname)); + var found = ApplicationContext.Current.Services.UserService.GetByUsername(lname); + return found == null ? -1 : found.Id; } - - private static int getUserId(string query, params IParameter[] parameterValues) - { - object userId = SqlHelper.ExecuteScalar(query, parameterValues); - return (userId != null && userId != DBNull.Value) ? int.Parse(userId.ToString()) : -1; - } - + /// /// Deletes this instance. /// @@ -639,18 +574,8 @@ namespace umbraco.BusinessLogic OnDeleting(EventArgs.Empty); - //would be better in the notifications class but since we can't reference the cms project (poorly architected) we need to use raw sql - SqlHelper.ExecuteNonQuery("delete from umbracoUser2NodeNotify where userId = @userId", SqlHelper.CreateParameter("@userId", Id)); + ApplicationContext.Current.Services.UserService.Delete(_user, true); - //would be better in the permissions class but since we can't reference the cms project (poorly architected) we need to use raw sql - SqlHelper.ExecuteNonQuery("delete from umbracoUser2NodePermission where userId = @userId", SqlHelper.CreateParameter("@userId", Id)); - - //delete the assigned applications - clearApplications(); - - SqlHelper.ExecuteNonQuery("delete from umbracoUserLogins where userID = @id", SqlHelper.CreateParameter("@id", Id)); - - SqlHelper.ExecuteNonQuery("delete from umbracoUser where id = @id", SqlHelper.CreateParameter("@id", Id)); FlushFromCache(); } @@ -660,17 +585,9 @@ namespace umbraco.BusinessLogic public void disable() { OnDisabling(EventArgs.Empty); - //change disabled and userLogin (prefix with yyyyMMdd_ ) - this.Disabled = true; - //MUST clear out the umbraco logins otherwise if they are still logged in they can still do stuff: - //http://issues.umbraco.org/issue/U4-2042 - SqlHelper.ExecuteNonQuery("delete from umbracoUserLogins where userID = @id", SqlHelper.CreateParameter("@id", Id)); - //can't rename if it's going to take up too many chars - if (this.LoginName.Length + 9 <= 125) - { - this.LoginName = DateTime.Now.ToString("yyyyMMdd") + "_" + this.LoginName; - } - this.Save(); + + //delete without the true overload will perform the disable operation + ApplicationContext.Current.Services.UserService.Delete(_user); } /// @@ -680,58 +597,24 @@ namespace umbraco.BusinessLogic /// public string GetPermissions(string Path) { - if (!_isInitialized) - setupUser(_id); - - // NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been - // set for the current node - var nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal)+1)) : int.Parse(Path); - return GetPermissions(nodeId); - } + if (_lazyId.HasValue) SetupUser(_lazyId.Value); - [Obsolete("Do not use this, implement something in the service layer!! And make sure we can mock/test it")] - internal string GetPermissions(int nodeId) - { - if (!_isInitialized) - setupUser(_id); + var defaultPermissions = UserType.DefaultPermissions; - string defaultPermissions = UserType.DefaultPermissions; - - //get the cached permissions for the user - var cachedPermissions = ApplicationContext.Current.ApplicationCache.GetCacheItem( - string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, _id), - //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average - CacheItemPriority.BelowNormal, - null, - //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, - // then it will refresh from the database. - new TimeSpan(0, 20, 0), - () => - { - var cruds = new Hashtable(); - using (var dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id))) - { - while (dr.Read()) - { - if (!cruds.ContainsKey(dr.GetInt("nodeId"))) - { - cruds.Add(dr.GetInt("nodeId"), string.Empty); - } - cruds[dr.GetInt("nodeId")] += dr.GetString("permission"); - } - } - return cruds; - }); + var cachedPermissions = ApplicationContext.Current.Services.UserService.GetPermissions(_user) + .ToArray(); // NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been // set for the current node - if (cachedPermissions.ContainsKey(nodeId)) + var nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal) + 1)) : int.Parse(Path); + if (cachedPermissions.Any(x => x.EntityId == nodeId)) { - return cachedPermissions[nodeId].ToString(); + var found = cachedPermissions.First(x => x.EntityId == nodeId); + return string.Join("", found.AssignedPermissions); } // exception to everything. If default cruds is empty and we're on root node; allow browse of root node - if (string.IsNullOrEmpty(defaultPermissions) && nodeId == -1) + if (string.IsNullOrEmpty(defaultPermissions) && Path == "-1") defaultPermissions = "F"; // else return default user type cruds @@ -755,7 +638,7 @@ namespace umbraco.BusinessLogic { string notifications = ""; - if (!_notificationsInitialized) + if (_notificationsInitialized == false) initNotifications(); foreach (string nodeId in Path.Split(',')) @@ -781,20 +664,19 @@ namespace umbraco.BusinessLogic /// public void initNotifications() { - if (!_isInitialized) - setupUser(_id); + if (_lazyId.HasValue) SetupUser(_lazyId.Value); - using (IRecordsReader dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodeNotify where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id))) + var notifications = ApplicationContext.Current.Services.NotificationService.GetUserNotifications(_user); + foreach (var n in notifications.OrderBy(x => x.EntityId)) { - while (dr.Read()) + int nodeId = n.EntityId; + if (_notifications.ContainsKey(nodeId) == false) { - int nodeId = dr.GetInt("nodeId"); - if (!_notifications.ContainsKey(nodeId)) - _notifications.Add(nodeId, String.Empty); - - _notifications[nodeId] += dr.GetString("action"); + _notifications.Add(nodeId, string.Empty); } + _notifications[nodeId] += n.Action; } + _notificationsInitialized = true; } @@ -804,7 +686,7 @@ namespace umbraco.BusinessLogic /// The id. public int Id { - get { return _id; } + get { return _user.Id; } } /// @@ -812,7 +694,14 @@ namespace umbraco.BusinessLogic /// public void clearApplications() { - SqlHelper.ExecuteNonQuery("delete from umbracoUser2app where [user] = @id", SqlHelper.CreateParameter("@id", this.Id)); + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + + foreach (var s in _user.AllowedSections.ToArray()) + { + _user.RemoveAllowedSection(s); + } + + ApplicationContext.Current.Services.UserService.Save(_user); } /// @@ -821,7 +710,11 @@ namespace umbraco.BusinessLogic /// The app alias. public void addApplication(string AppAlias) { - SqlHelper.ExecuteNonQuery("insert into umbracoUser2app ([user],app) values (@id, @app)", SqlHelper.CreateParameter("@id", this.Id), SqlHelper.CreateParameter("@app", AppAlias)); + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + + _user.AddAllowedSection(AppAlias); + + ApplicationContext.Current.Services.UserService.Save(_user); } /// @@ -832,15 +725,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _userNoConsole; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.IsLockedOut; } set { - _userNoConsole = value; - SqlHelper.ExecuteNonQuery("update umbracoUser set userNoConsole = @userNoConsole where id = @id", SqlHelper.CreateParameter("@id", this.Id), SqlHelper.CreateParameter("@userNoConsole", _userNoConsole)); - FlushFromCache(); + _user.IsLockedOut = value; } } @@ -852,18 +742,16 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _userDisabled; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.IsApproved == false; } set { - _userDisabled = value; - SqlHelper.ExecuteNonQuery("update umbracoUser set userDisabled = @userDisabled where id = @id", SqlHelper.CreateParameter("@id", this.Id), SqlHelper.CreateParameter("@userDisabled", _userDisabled)); - FlushFromCache(); + _user.IsApproved = value == false; } } + /// /// /// Gets or sets the start content node id. /// @@ -872,16 +760,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _startnodeid; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.StartContentId; } set { - - _startnodeid = value; - SqlHelper.ExecuteNonQuery("update umbracoUser set startStructureId = @start where id = @id", SqlHelper.CreateParameter("@start", value), SqlHelper.CreateParameter("@id", this.Id)); - FlushFromCache(); + _user.StartContentId = value; } } @@ -893,16 +777,12 @@ namespace umbraco.BusinessLogic { get { - if (!_isInitialized) - setupUser(_id); - return _startmediaid; + if (_lazyId.HasValue) SetupUser(_lazyId.Value); + return _user.StartMediaId; } set { - - _startmediaid = value; - SqlHelper.ExecuteNonQuery("update umbracoUser set startMediaId = @start where id = @id", SqlHelper.CreateParameter("@start", value), SqlHelper.CreateParameter("@id", this.Id)); - FlushFromCache(); + _user.StartMediaId = value; } } @@ -913,7 +793,7 @@ namespace umbraco.BusinessLogic public void FlushFromCache() { OnFlushingFromCache(EventArgs.Empty); - ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, Id.ToString())); + RuntimeCacheProvider.Current.Clear(typeof (IUser)); } /// @@ -921,22 +801,15 @@ namespace umbraco.BusinessLogic /// /// The id. /// - [Obsolete("The legacy user object should no longer be used, use the WebSecurity class to access the current user or the UserService to retreive a user by id")] + [Obsolete("The legacy user object should no longer be used, use the WebSecurity class to access the current user or the UserService to retrieve a user by id")] public static User GetUser(int id) { - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - string.Format("{0}{1}", CacheKeys.UserCacheKey, id.ToString()), () => - { - try - { - return new User(id); - } - catch (ArgumentException) - { - //no user was found - return null; - } - }); + var result = ApplicationContext.Current.Services.UserService.GetUserById(id); + if (result == null) + { + throw new ArgumentException("No user found with id " + id); + } + return new User(result); } diff --git a/src/umbraco.businesslogic/UserType.cs b/src/umbraco.businesslogic/UserType.cs index e049a4023a..31604c3ce9 100644 --- a/src/umbraco.businesslogic/UserType.cs +++ b/src/umbraco.businesslogic/UserType.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Data; +using System.Globalization; +using System.Linq; using System.Runtime.CompilerServices; using System.Web; using System.Web.Caching; @@ -14,13 +16,24 @@ namespace umbraco.BusinessLogic /// /// Represents a umbraco Usertype /// + [Obsolete("Use the UserService instead")] public class UserType { + internal Umbraco.Core.Models.Membership.IUserType UserTypeItem; + /// /// Creates a new empty instance of a UserType /// - public UserType() { } + public UserType() + { + UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); + } + + internal UserType(Umbraco.Core.Models.Membership.IUserType userType) + { + UserTypeItem = userType; + } /// /// Creates a new instance of a UserType and attempts to @@ -43,8 +56,9 @@ namespace umbraco.BusinessLogic /// The name. public UserType(int id, string name) { - _id = id; - _name = name; + UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); + UserTypeItem.Id = id; + UserTypeItem.Name = name; } /// @@ -56,18 +70,13 @@ namespace umbraco.BusinessLogic /// public UserType(int id, string name, string defaultPermissions, string alias) { - _name = name; - _id = id; - _defaultPermissions = defaultPermissions; - _alias = alias; + UserTypeItem = new Umbraco.Core.Models.Membership.UserType(); + UserTypeItem.Id = id; + UserTypeItem.Name = name; + UserTypeItem.Alias = alias; + UserTypeItem.Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); } - - private int _id; - private string _name; - private string _defaultPermissions; - private string _alias; - /// /// The cache storage for all user types /// @@ -75,40 +84,20 @@ namespace umbraco.BusinessLogic { get { - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - CacheKeys.UserTypeCacheKey, - () => - { - var tmp = new List(); - using (var dr = SqlHelper.ExecuteReader("select id, userTypeName, userTypeAlias, userTypeDefaultPermissions from umbracoUserType")) - { - while (dr.Read()) - { - tmp.Add(new UserType( - dr.GetShort("id"), - dr.GetString("userTypeName"), - dr.GetString("userTypeDefaultPermissions"), - dr.GetString("userTypeAlias"))); - } - } - return tmp; - }); + return ApplicationContext.Current.Services.UserService.GetAllUserTypes() + .Select(x => new UserType(x)) + .ToList(); } } - - private static ISqlHelper SqlHelper - { - get { return Application.SqlHelper; } - } - + #region Public Properties /// /// Gets or sets the user type alias. /// public string Alias { - get { return _alias; } - set { _alias = value; } + get { return UserTypeItem.Alias; } + set { UserTypeItem.Alias = value; } } /// @@ -116,8 +105,8 @@ namespace umbraco.BusinessLogic /// public string Name { - get { return _name; } - set { _name = value; } + get { return UserTypeItem.Name; } + set { UserTypeItem.Name = value; } } /// @@ -125,7 +114,7 @@ namespace umbraco.BusinessLogic /// public int Id { - get { return _id; } + get { return UserTypeItem.Id; } } /// @@ -133,8 +122,8 @@ namespace umbraco.BusinessLogic /// public string DefaultPermissions { - get { return _defaultPermissions; } - set { _defaultPermissions = value; } + get { return string.Join("", UserTypeItem.Permissions); } + set { UserTypeItem.Permissions = value.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)); } } /// @@ -153,19 +142,11 @@ namespace umbraco.BusinessLogic public void Save() { //ensure that this object has an ID specified (it exists in the database) - if (_id <= 0) + if (UserTypeItem.HasIdentity == false) throw new Exception("The current UserType object does not exist in the database. New UserTypes should be created with the MakeNew method"); - SqlHelper.ExecuteNonQuery(@" - update umbracoUserType set - userTypeAlias=@alias,userTypeName=@name,userTypeDefaultPermissions=@permissions - where id=@id", - SqlHelper.CreateParameter("@alias", _alias), - SqlHelper.CreateParameter("@name", _name), - SqlHelper.CreateParameter("@permissions", _defaultPermissions), - SqlHelper.CreateParameter("@id", _id) - ); - + ApplicationContext.Current.Services.UserService.SaveUserType(UserTypeItem); + //raise event OnUpdated(this, new EventArgs()); } @@ -176,10 +157,10 @@ namespace umbraco.BusinessLogic public void Delete() { //ensure that this object has an ID specified (it exists in the database) - if (_id <= 0) + if (UserTypeItem.HasIdentity == false) throw new Exception("The current UserType object does not exist in the database. New UserTypes should be created with the MakeNew method"); - SqlHelper.ExecuteNonQuery(@"delete from umbracoUserType where id=@id", SqlHelper.CreateParameter("@id", _id)); + ApplicationContext.Current.Services.UserService.DeleteUserType(UserTypeItem); //raise event OnDeleted(this, new EventArgs()); @@ -193,16 +174,8 @@ namespace umbraco.BusinessLogic /// and the data was loaded, false if it wasn't public bool LoadByPrimaryKey(int id) { - var userType = GetUserType(id); - if (userType == null) - return false; - - _id = userType.Id; - _alias = userType.Alias; - _defaultPermissions = userType.DefaultPermissions; - _name = userType.Name; - - return true; + UserTypeItem = ApplicationContext.Current.Services.UserService.GetUserTypeById(id); + return UserTypeItem != null; } /// @@ -221,37 +194,20 @@ namespace umbraco.BusinessLogic if (existing != null) throw new Exception("The UserType alias specified already exists"); - SqlHelper.ExecuteNonQuery(@" - insert into umbracoUserType - (userTypeAlias,userTypeName,userTypeDefaultPermissions) - values (@alias,@name,@permissions)", - SqlHelper.CreateParameter("@alias", alias), - SqlHelper.CreateParameter("@name", name), - SqlHelper.CreateParameter("@permissions", defaultPermissions)); - - //get it's id - var newId = SqlHelper.ExecuteScalar("SELECT MAX(id) FROM umbracoUserType WHERE userTypeAlias=@alias", SqlHelper.CreateParameter("@alias", alias)); - - //load the instance and return it - using (var dr = SqlHelper.ExecuteReader( - "select id, userTypeName, userTypeAlias, userTypeDefaultPermissions from umbracoUserType where id=@id", - SqlHelper.CreateParameter("@id", newId))) + var userType = new Umbraco.Core.Models.Membership.UserType { - if (dr.Read()) - { - var ut = new UserType( - dr.GetShort("id"), - dr.GetString("userTypeName"), - dr.GetString("userTypeDefaultPermissions"), - dr.GetString("userTypeAlias")); + Alias = alias, + Name = name, + Permissions = defaultPermissions.ToCharArray().Select(x => x.ToString(CultureInfo.InvariantCulture)) + }; + ApplicationContext.Current.Services.UserService.SaveUserType(userType); - //raise event - OnNew(ut, new EventArgs()); + var legacy = new UserType(userType); + + //raise event + OnNew(legacy, new EventArgs()); - return ut; - } - throw new InvalidOperationException("Could not read the new User Type with id of " + newId); - } + return legacy; } /// diff --git a/src/umbraco.cms/Actions/ActionChangeDocType.cs b/src/umbraco.cms/Actions/ActionChangeDocType.cs index 3016f2ef6d..aff2b45697 100644 --- a/src/umbraco.cms/Actions/ActionChangeDocType.cs +++ b/src/umbraco.cms/Actions/ActionChangeDocType.cs @@ -69,7 +69,7 @@ namespace umbraco.BusinessLogic.Actions get { - return ".sprChangeDocType"; + return "axis-rotation-2"; } } diff --git a/src/umbraco.cms/app.config b/src/umbraco.cms/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/umbraco.cms/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/CMSNode.cs b/src/umbraco.cms/businesslogic/CMSNode.cs index bd997bab54..9b0712df0e 100644 --- a/src/umbraco.cms/businesslogic/CMSNode.cs +++ b/src/umbraco.cms/businesslogic/CMSNode.cs @@ -22,6 +22,7 @@ using umbraco.cms.businesslogic.Tags; using File = System.IO.File; using Media = umbraco.cms.businesslogic.media.Media; using Tag = umbraco.cms.businesslogic.Tags.Tag; +using Notification = umbraco.cms.businesslogic.workflow.Notification; using Task = umbraco.cms.businesslogic.task.Task; namespace umbraco.cms.businesslogic @@ -377,6 +378,19 @@ namespace umbraco.cms.businesslogic setupNode(); } + /// + /// This is purely for a hackity hack hack hack in order to make the new Document(id, version) constructor work because + /// the Version property needs to be set on the object before setupNode is called, otherwise it never works! this allows + /// inheritors to set default data before setupNode() is called. + /// + /// + /// + internal CMSNode(int id, object[] ctorArgs) + { + _id = id; + PreSetupNode(ctorArgs); + } + /// /// Initializes a new instance of the class. /// @@ -420,6 +434,11 @@ namespace umbraco.cms.businesslogic _entity = entity; } + protected internal CMSNode(IEntity entity) + { + _id = entity.Id; + } + #endregion #region Public Methods @@ -1011,6 +1030,18 @@ order by level,sortOrder"; _entity.Name = txt; } + /// + /// This is purely for a hackity hack hack hack in order to make the new Document(id, version) constructor work because + /// the Version property needs to be set on the object before setupNode is called, otherwise it never works! + /// + /// + internal virtual void PreSetupNode(params object[] ctorArgs) + { + //if people want to override then awesome but then we call setupNode so they need to ensure + // to call base.PreSetupNode + setupNode(); + } + /// /// Sets up the internal data of the CMSNode, used by the various constructors /// @@ -1138,6 +1169,13 @@ order by level,sortOrder"; _entity = content; } + internal protected void PopulateCMSNodeFromUmbracoEntity(IAggregateRoot content, Guid objectType) + { + _uniqueID = content.Key; + _nodeObjectType = objectType; + _createDate = content.CreateDate; + } + #endregion #region Private Methods diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index 2db24bd5f9..f81ecce499 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -40,13 +40,18 @@ namespace umbraco.cms.businesslogic private bool _versionDateInitialized; private string _contentTypeIcon; private ContentType _contentType; - private Properties m_LoadedProperties = null; + private Properties _loadedProperties = null; protected internal IContentBase ContentBase; #endregion #region Constructors - + + protected internal Content(int id, Guid version) + : base(id, new object[] { version }) + { + } + public Content(int id) : base(id) { } protected Content(int id, bool noSetup) : base(id, noSetup) { } @@ -224,7 +229,7 @@ namespace umbraco.cms.businesslogic get { EnsureProperties(); - return m_LoadedProperties; + return _loadedProperties; } } @@ -237,7 +242,7 @@ namespace umbraco.cms.businesslogic get { EnsureProperties(); - return m_LoadedProperties.ToArray(); + return _loadedProperties.ToArray(); } } @@ -289,7 +294,7 @@ namespace umbraco.cms.businesslogic { EnsureProperties(); - return m_LoadedProperties.SingleOrDefault(x => x.PropertyType.Alias == alias); + return _loadedProperties.SingleOrDefault(x => x.PropertyType.Alias == alias); } /// @@ -301,7 +306,7 @@ namespace umbraco.cms.businesslogic { EnsureProperties(); - return m_LoadedProperties.SingleOrDefault(x => x.PropertyType.Id == pt.Id); + return _loadedProperties.SingleOrDefault(x => x.PropertyType.Id == pt.Id); } /// @@ -398,7 +403,7 @@ namespace umbraco.cms.businesslogic { var props = this.GenericProperties; foreach (property.Property p in props) - if (p != null) + if (p != null && p.Value != null && string.IsNullOrEmpty(p.Value.ToString()) == false) x.AppendChild(p.ToXml(xd)); // attributes @@ -463,6 +468,21 @@ namespace umbraco.cms.businesslogic #region Protected Methods + /// + /// This is purely for a hackity hack hack hack in order to make the new Document(id, version) constructor work because + /// the Version property needs to be set on the object before setupNode is called, otherwise it never works! + /// + /// + internal override void PreSetupNode(params object[] ctorArgs) + { + //we know that there is one ctor arg and it is a GUID since we are only calling the base + // ctor with this overload for one purpose. + var version = (Guid) ctorArgs[0]; + _version = version; + + base.PreSetupNode(ctorArgs); + } + /// /// Sets up the ContentType property for this content item and sets the addition content properties manually. /// If the ContentType property is not already set, then this will get the ContentType from Cache. @@ -617,7 +637,7 @@ namespace umbraco.cms.businesslogic /// private void ClearLoadedProperties() { - m_LoadedProperties = null; + _loadedProperties = null; } /// @@ -625,7 +645,7 @@ namespace umbraco.cms.businesslogic /// private void EnsureProperties() { - if (m_LoadedProperties == null) + if (_loadedProperties == null) { InitializeProperties(); } @@ -644,13 +664,13 @@ namespace umbraco.cms.businesslogic /// private void InitializeProperties() { - m_LoadedProperties = new Properties(); + _loadedProperties = new Properties(); if (ContentBase != null) { //NOTE: we will not load any properties where HasIdentity = false - this is because if properties are // added to the property collection that aren't persisted we'll get ysods - m_LoadedProperties.AddRange(ContentBase.Properties.Where(x => x.HasIdentity).Select(x => new Property(x))); + _loadedProperties.AddRange(ContentBase.Properties.Where(x => x.HasIdentity).Select(x => new Property(x))); return; } @@ -659,7 +679,7 @@ namespace umbraco.cms.businesslogic //Create anonymous typed list with 2 props, Id and PropertyTypeId of type Int. //This will still be an empty list since the props list is empty. - var propData = m_LoadedProperties.Select(x => new { Id = 0, PropertyTypeId = 0 }).ToList(); + var propData = _loadedProperties.Select(x => new { Id = 0, PropertyTypeId = 0 }).ToList(); string sql = @"select id, propertyTypeId from cmsPropertyData where versionId=@versionId"; @@ -698,7 +718,7 @@ namespace umbraco.cms.businesslogic continue; //this remains from old code... not sure why we would do this? } - m_LoadedProperties.Add(p); + _loadedProperties.Add(p); } } diff --git a/src/umbraco.cms/businesslogic/ContentType.cs b/src/umbraco.cms/businesslogic/ContentType.cs index 71d676f54d..58dbde48a3 100644 --- a/src/umbraco.cms/businesslogic/ContentType.cs +++ b/src/umbraco.cms/businesslogic/ContentType.cs @@ -30,7 +30,7 @@ namespace umbraco.cms.businesslogic /// Besides data definition, the ContentType also defines the sorting and grouping (in tabs) of Properties/Datafields /// on the Content and which Content (by ContentType) can be created as child to the Content of the ContentType. /// - [Obsolete("Obsolete, Use Umbraco.Core.Models.ContentType or Umbraco.Core.Models.MediaType", false)] + [Obsolete("Obsolete, Use Umbraco.Core.Models.ContentType or Umbraco.Core.Models.MediaType or or Umbraco.Core.Models.MemberType", false)] public class ContentType : CMSNode { #region Constructors @@ -59,6 +59,7 @@ namespace umbraco.cms.businesslogic /// /// /// + /// /// /// This is like creating a ContentType node using optimized mode but this lets you set /// all of the properties that are initialized normally from the database. @@ -175,7 +176,7 @@ namespace umbraco.cms.businesslogic public static ContentType GetContentType(int id) { return ApplicationContext.Current.ApplicationCache.GetCacheItem - (string.Format("UmbracoContentType{0}", id), + (string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, id), TimeSpan.FromMinutes(30), () => new ContentType(id)); } @@ -199,10 +200,17 @@ namespace umbraco.cms.businesslogic /// The Alias of the ContentType /// The Iconurl of Contents of this ContentType protected static void Create(int NodeId, string Alias, string IconUrl) + { + Create(NodeId, Alias, IconUrl, true); + } + + internal static void Create(int nodeId, string alias, string iconUrl, bool formatAlias) { SqlHelper.ExecuteNonQuery( - "Insert into cmsContentType (nodeId,alias,icon) values (" + NodeId + ",'" + helpers.Casing.SafeAliasWithForcingCheck(Alias) + - "','" + IconUrl + "')"); + "Insert into cmsContentType (nodeId,alias,icon) values (" + + nodeId + ",'" + + (formatAlias ? helpers.Casing.SafeAliasWithForcingCheck(alias) : alias) + + "','" + iconUrl + "')"); } /// @@ -1142,6 +1150,15 @@ namespace umbraco.cms.businesslogic return; } } + else if (nodeObjectType == new Guid(Constants.ObjectTypes.MemberType)) + { + var memberType = ApplicationContext.Current.Services.MemberTypeService.Get(Id); + if (memberType != null) + { + PopulateContentTypeFromContentTypeBase(memberType); + return; + } + } // TODO: Load master content types using (var dr = SqlHelper.ExecuteReader("Select allowAtRoot, isContainer, Alias,icon,thumbnail,description from cmsContentType where nodeid=" + Id) @@ -1176,9 +1193,8 @@ namespace umbraco.cms.businesslogic RemoveFromDataTypeCache(ct.Alias); // clear anything that uses this as master content type - //TODO: Update to load all content types - //Should this include "ct.nodeObjectType == media.MediaType._objectType" ? - if (ct.nodeObjectType == DocumentType._objectType) + if (ct.nodeObjectType == DocumentType._objectType + || ct.nodeObjectType == media.MediaType._objectType) { //NOTE Changed from "DocumentType.GetAllAsList().FindAll(dt => dt.MasterContentType == id)" to loading master contenttypes directly from the db. //Related to http://issues.umbraco.org/issue/U4-1714 diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs index e524f90429..cd32162fa7 100644 --- a/src/umbraco.cms/businesslogic/Dictionary.cs +++ b/src/umbraco.cms/businesslogic/Dictionary.cs @@ -532,5 +532,24 @@ namespace umbraco.cms.businesslogic } #endregion } + + // zb023 - utility method + public static string ReplaceKey(string text) + { + if (text.StartsWith("#") == false) + return text; + + var lang = Language.GetByCultureCode(Thread.CurrentThread.CurrentCulture.Name); + + if (lang == null) + return "[" + text + "]"; + + if (DictionaryItem.hasKey(text.Substring(1, text.Length - 1)) == false) + return "[" + text + "]"; + + var di = new DictionaryItem(text.Substring(1, text.Length - 1)); + return di.Value(lang.id); + } + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index ef9b77e16b..10e6176877 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -298,51 +298,27 @@ namespace umbraco.cms.businesslogic.packager foreach (var dataTypeDefinition in dataTypeDefinitions) { insPack.Data.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; } } - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//DataType")) - { - cms.businesslogic.datatype.DataTypeDefinition newDtd = cms.businesslogic.datatype.DataTypeDefinition.Import(n); - - if (newDtd != null) - { - insPack.Data.DataTypes.Add(newDtd.Id.ToString()); - saveNeeded = true; - } - }*/ - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } #endregion #region Languages - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//Language")) + var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); + if (languageItemsElement != null) { - language.Language newLang = language.Language.Import(n); - - if (newLang != null) - { - insPack.Data.Languages.Add(newLang.id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } + var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); + insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString())); } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } + #endregion #region Dictionary items - foreach (XmlNode n in Config.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) + var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); + if (dictionaryItemsElement != null) { - Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); - - if (newDi != null) - { - insPack.Data.DictionaryItems.Add(newDi.id.ToString()); - //saveNeeded = true; - } + var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); + insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString())); } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } #endregion #region Macros @@ -368,56 +344,8 @@ namespace umbraco.cms.businesslogic.packager foreach (var template in templates) { insPack.Data.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; } } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) - { - var t = Template.Import(n, currentUser); - - insPack.Data.Templates.Add(t.Id.ToString()); - - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - - //NOTE: SD: I'm pretty sure the only thing the below script does is ensure that the Master template Id is set - // in the database, but this is also duplicating the saving of the design content since the above Template.Import - // already does this. I've left this for now because I'm not sure the reprocussions of removing it but seems there - // is a lot of excess database calls happening here. - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) - { - string master = XmlHelper.GetNodeValue(n.SelectSingleNode("Master")); - Template t = Template.GetByAlias(XmlHelper.GetNodeValue(n.SelectSingleNode("Alias"))); - if (master.Trim() != "") - { - var masterTemplate = Template.GetByAlias(master); - if (masterTemplate != null) - { - t.MasterTemplate = Template.GetByAlias(master).Id; - //SD: This appears to always just save an empty template because the design isn't set yet - // this fixes an issue now that we have MVC because if there is an empty template and MVC is - // the default, it will create a View not a master page and then the system will try to route via - // MVC which means that the package will not work anymore. - // The code below that imports the templates should suffice because it's actually importing - // template data not just blank data. - - //if (UmbracoConfiguration.Current.UmbracoSettings.Templates.UseAspNetMasterPages) - // t.SaveMasterPageFile(t.Design); - } - } - // Master templates can only be generated when their master is known - if (UmbracoConfiguration.Current.UmbracoSettings.Templates.UseAspNetMasterPages) - { - t.ImportDesign(XmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); - t.SaveMasterPageFile(t.Design); - } - }*/ #endregion #region DocumentTypes @@ -436,44 +364,6 @@ namespace umbraco.cms.businesslogic.packager //saveNeeded = true; } } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) - { - ImportDocumentType(n, currentUser, false); - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - - // Add documenttype structure - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) - { - DocumentType dt = DocumentType.GetByAlias(XmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias"))); - if (dt != null) - { - ArrayList allowed = new ArrayList(); - foreach (XmlNode structure in n.SelectNodes("Structure/DocumentType")) - { - DocumentType dtt = DocumentType.GetByAlias(XmlHelper.GetNodeValue(structure)); - if (dtt != null) - allowed.Add(dtt.Id); - } - - int[] adt = new int[allowed.Count]; - for (int i = 0; i < allowed.Count; i++) - adt[i] = (int)allowed[i]; - dt.AllowedChildContentTypeIDs = adt; - dt.Save(); - //PPH we log the document type install here. - insPack.Data.Documenttypes.Add(dt.Id.ToString()); - saveNeeded = true; - } - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; }*/ #endregion #region Stylesheets @@ -496,10 +386,6 @@ namespace umbraco.cms.businesslogic.packager var firstContentItem = content.First(); insPack.Data.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); } - /*foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) - { - insPack.Data.ContentNodeId = cms.businesslogic.web.Document.Import(-1, currentUser, n).ToString(); - }*/ #endregion #region Package Actions diff --git a/src/umbraco.cms/businesslogic/Packager/data.cs b/src/umbraco.cms/businesslogic/Packager/data.cs index 6f0df33b4d..a950212c47 100644 --- a/src/umbraco.cms/businesslogic/Packager/data.cs +++ b/src/umbraco.cms/businesslogic/Packager/data.cs @@ -116,8 +116,8 @@ namespace umbraco.cms.businesslogic.packager instance.Attributes.Append(xmlHelper.addAttribute(Source, "skinRepoGuid", "")); XmlElement license = Source.CreateElement("license"); - license.InnerText = "MIT license"; - license.Attributes.Append(xmlHelper.addAttribute(Source, "url", "http://www.opensource.org/licenses/mit-license.php")); + license.InnerText = "MIT License"; + license.Attributes.Append(xmlHelper.addAttribute(Source, "url", "http://opensource.org/licenses/MIT")); instance.AppendChild(license); XmlElement author = Source.CreateElement("author"); diff --git a/src/umbraco.cms/businesslogic/Permission.cs b/src/umbraco.cms/businesslogic/Permission.cs index 29a2f7ecc4..ba0ad56b14 100644 --- a/src/umbraco.cms/businesslogic/Permission.cs +++ b/src/umbraco.cms/businesslogic/Permission.cs @@ -2,8 +2,10 @@ using System; using System.Collections; using System.Collections.Specialized; using System.Data; +using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; +using Umbraco.Core; using Umbraco.Core.Events; using umbraco.DataLayer; using umbraco.cms.businesslogic; @@ -12,6 +14,9 @@ using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs; namespace umbraco.BusinessLogic { + + //TODO: Wrap this in the new services/repo layer! + /// /// Summary description for Permission. /// @@ -167,7 +172,7 @@ namespace umbraco.BusinessLogic } private static string Converter(int from) { - return from.ToString(); + return from.ToString(CultureInfo.InvariantCulture); } /// @@ -186,16 +191,11 @@ namespace umbraco.BusinessLogic [MethodImpl(MethodImplOptions.Synchronized)] public static void UpdateCruds(User user, CMSNode node, string permissions) { - // delete all settings on the node for this user - //false = do not raise events - DeletePermissions(user, node, false); + ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( + user.Id, + permissions.ToCharArray(), + node.Id); - // Loop through the permissions and create them - foreach (char c in permissions) - { - //false = don't raise events since we'll raise a custom event after - MakeNew(user, node, c, false); - } OnUpdated(new UserPermission(user, node, permissions.ToCharArray()), new SaveEventArgs()); } diff --git a/src/umbraco.cms/businesslogic/Property/Property.cs b/src/umbraco.cms/businesslogic/Property/Property.cs index 8adac0d7e3..1159db94a6 100644 --- a/src/umbraco.cms/businesslogic/Property/Property.cs +++ b/src/umbraco.cms/businesslogic/Property/Property.cs @@ -1,5 +1,7 @@ using System; +using System.Reflection.Emit; using System.Runtime.CompilerServices; +using System.Web.UI; using System.Xml; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -39,6 +41,8 @@ namespace umbraco.cms.businesslogic.property _pt = pt; _id = Id; + if (_pt.DataTypeDefinition.DataType == null) + throw new Exception(string.Format("Could not load datatype '{0}'", _pt.DataTypeDefinition.Text)); _data = _pt.DataTypeDefinition.DataType.Data; _data.PropertyId = Id; } @@ -49,6 +53,8 @@ namespace umbraco.cms.businesslogic.property _pt = PropertyType.GetPropertyType( SqlHelper.ExecuteScalar("select propertytypeid from cmsPropertyData where id = @id", SqlHelper.CreateParameter("@id", Id))); + if (_pt.DataTypeDefinition.DataType == null) + throw new Exception(string.Format("Could not load datatype '{0}'", _pt.DataTypeDefinition.Text)); _data = _pt.DataTypeDefinition.DataType.Data; _data.PropertyId = Id; } @@ -61,7 +67,19 @@ namespace umbraco.cms.businesslogic.property //Just to ensure that there is a PropertyType available _pt = PropertyType.GetPropertyType(property.PropertyTypeId); - _data = _pt.DataTypeDefinition.DataType.Data; + + //ensure we have data property editor set + if (_pt.DataTypeDefinition.DataType != null) + { + _data = _pt.DataTypeDefinition.DataType.Data; + } + else + { + //send back null we will handle it in ContentControl AddControlNew + //and display to use message from the dictionary errors section + _data= new DefaultData(null); + } + _data.PropertyId = Id; //set the value so it doesn't need to go to the database @@ -152,4 +170,4 @@ namespace umbraco.cms.businesslogic.property } } -} \ No newline at end of file +} diff --git a/src/umbraco.cms/businesslogic/cache/Cache.cs b/src/umbraco.cms/businesslogic/cache/Cache.cs index 6669d369bf..dc6946a6b3 100644 --- a/src/umbraco.cms/businesslogic/cache/Cache.cs +++ b/src/umbraco.cms/businesslogic/cache/Cache.cs @@ -6,7 +6,7 @@ using Umbraco.Core; namespace umbraco.cms.businesslogic.cache { /// - /// Used to easily store and retreive items from the cache. + /// Used to easily store and retrieve items from the cache. /// /// /// This whole class will become obsolete, however one of the methods is still used that is not ported over to the new CacheHelper diff --git a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs index bcb8c91fa3..4e0a48ad93 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs @@ -1,9 +1,11 @@ using System; using System.Collections; using System.Globalization; +using System.Data; using System.Linq; using Umbraco.Core.Cache; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; using umbraco.DataLayer; using System.Xml; @@ -32,6 +34,7 @@ namespace umbraco.cms.businesslogic.datatype private string _propertyEditorAlias; private static readonly Guid ObjectType = new Guid(Constants.ObjectTypes.DataType); + private string _text1; #endregion @@ -52,6 +55,13 @@ namespace umbraco.cms.businesslogic.datatype #endregion #region Public Properties + + public override string Text + { + get { return _text1 ?? (_text1 = base.Text); } + set { _text1 = value; } + } + /// /// The associated datatype, which delivers the methods for editing data, editing prevalues see: umbraco.interfaces.IDataType /// @@ -146,6 +156,23 @@ namespace umbraco.cms.businesslogic.datatype /// public override void Save() { + //Cannot change to a duplicate alias + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType +INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id +WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @"= @name +AND umbracoNode.id <> @id", + new { id = this.Id, name = this.Text }); + if (exists > 0) + { + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem( + string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, this.Id)); + + throw new DuplicateNameException("A data type with the name " + this.Text + " already exists"); + } + + //this actually does the persisting. + base.Text = _text1; + OnSaving(EventArgs.Empty); } @@ -265,6 +292,14 @@ namespace umbraco.cms.businesslogic.datatype /// public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text, Guid UniqueId) { + //Cannot add a duplicate data type + var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType +INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id +WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + "= @name", new { name = Text }); + if (exists > 0) + { + throw new DuplicateNameException("A data type with the name " + Text + " already exists"); + } var newId = MakeNew(-1, ObjectType, u.Id, 1, Text, UniqueId).Id; diff --git a/src/umbraco.cms/businesslogic/datatype/DefaultData.cs b/src/umbraco.cms/businesslogic/datatype/DefaultData.cs index 8b07258d32..dc78b7985c 100644 --- a/src/umbraco.cms/businesslogic/datatype/DefaultData.cs +++ b/src/umbraco.cms/businesslogic/datatype/DefaultData.cs @@ -82,7 +82,10 @@ namespace umbraco.cms.businesslogic.datatype //instead of making it query for itself. This is a peformance optimization enhancement. var dbType = BaseDataType.GetDBType(strDbType); var fieldName = BaseDataType.GetDataFieldName(dbType); - _dataType.SetDataTypeProperties(fieldName, dbType); + + //if misconfigured (datatype created in the tree, but save button never clicked), the datatype will be null + if(_dataType != null) + _dataType.SetDataTypeProperties(fieldName, dbType); //ensures that it doesn't go back to the db _valueLoaded = true; @@ -103,7 +106,7 @@ namespace umbraco.cms.businesslogic.datatype .Where(x => x.Id == _propertyId); var dto = Database.Fetch(sql).FirstOrDefault(); - if (dto != null) + if (dto != null && _dataType != null) { //the type stored in the cmsDataType table var strDbType = dto.PropertyTypeDto.DataTypeDto.DbType; diff --git a/src/umbraco.cms/businesslogic/datatype/DefaultPreValueEditor.cs b/src/umbraco.cms/businesslogic/datatype/DefaultPreValueEditor.cs index b235ec0610..23a1763293 100644 --- a/src/umbraco.cms/businesslogic/datatype/DefaultPreValueEditor.cs +++ b/src/umbraco.cms/businesslogic/datatype/DefaultPreValueEditor.cs @@ -1,7 +1,7 @@ using System; using System.Web.UI; using System.Web.UI.WebControls; - +using Umbraco.Core; using umbraco.interfaces; using umbraco.BusinessLogic; using umbraco.DataLayer; @@ -177,7 +177,7 @@ namespace umbraco.cms.businesslogic.datatype foreach (KeyValuePair k in dtSettings) { var result = k.Value.Validate(); - Label lbl = FindControlRecursive