diff --git a/build/Build.bat b/build/Build.bat index cf9c5ace51..9ad1fd214d 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -1,6 +1,6 @@ @ECHO OFF SET release=6.2.0 -SET comment= +SET comment=RC SET version=%release% IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%) diff --git a/build/Build.proj b/build/Build.proj index 1ef2f6915a..3f1f49c348 100644 --- a/build/Build.proj +++ b/build/Build.proj @@ -299,6 +299,10 @@ + + $(BUILD_RELEASE) + $(BUILD_RELEASE)-$(BUILD_COMMENT) + + \ No newline at end of file diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index a747183c1c..56bf97a8c8 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -4,7 +4,7 @@ UmbracoCms.Core 6.2.0 Umbraco Cms Core Binaries - Morten Christensen + Umbraco HQ Umbraco HQ http://umbraco.codeplex.com/license http://umbraco.com/ @@ -15,17 +15,20 @@ en-US umbraco - - + + + + - - + + + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index babaf5f845..8199f67c6f 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -4,7 +4,7 @@ UmbracoCms 6.1.2 Umbraco Cms - Morten Christensen + Umbraco HQ Umbraco HQ http://umbraco.codeplex.com/license http://umbraco.com/ @@ -19,11 +19,9 @@ - - - + @@ -32,15 +30,15 @@ + - - - + + + - diff --git a/build/NuSpecs/build/net40/UmbracoCms.props b/build/NuSpecs/build/UmbracoCms.props similarity index 100% rename from build/NuSpecs/build/net40/UmbracoCms.props rename to build/NuSpecs/build/UmbracoCms.props diff --git a/build/NuSpecs/build/UmbracoCms.targets b/build/NuSpecs/build/UmbracoCms.targets new file mode 100644 index 0000000000..04a60d1d44 --- /dev/null +++ b/build/NuSpecs/build/UmbracoCms.targets @@ -0,0 +1,50 @@ + + + + 6.2.0-RC + + + + ..\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 d95c158818..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 5e11945a9d..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 d95c158818..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 5e11945a9d..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 d95c158818..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 19af134c2a..94f4feafc1 100644 --- a/build/NuSpecs/tools/install.ps1 +++ b/build/NuSpecs/tools/install.ps1 @@ -1,27 +1,37 @@ 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 - } - + $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 - - # 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 + $webConfigSource = Join-Path $projectDestinationPath "Web.config" + Copy-Item $webConfigSource $backupPath -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/Umbraco.Core/CacheRefreshersResolver.cs b/src/Umbraco.Core/CacheRefreshersResolver.cs index e01dfba01a..e8df98fc1b 100644 --- a/src/Umbraco.Core/CacheRefreshersResolver.cs +++ b/src/Umbraco.Core/CacheRefreshersResolver.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core /// /// Gets the implementations. /// - public IEnumerable CacheResolvers + public IEnumerable CacheRefreshers { get { diff --git a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs index 580dae7481..acd691308a 100644 --- a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs +++ b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Configuration } /// - /// Increases the version number in ClientDependency.config by 1 + /// Changes the version number in ClientDependency.config to a random value to avoid stale caches /// internal bool IncreaseVersionNumber() { diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index d3c1fcb2ae..f7688575f3 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -5,10 +5,13 @@ using System.Linq; using System.Web; using System.Web.Configuration; using System.Web.Routing; +using System.Web.Security; using System.Xml; using System.Xml.Linq; +using System.Xml.XPath; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Security; namespace Umbraco.Core.Configuration { @@ -255,7 +258,38 @@ namespace Umbraco.Core.Configuration SaveSetting("umbracoConfigurationStatus", value); } } - + + /// + /// Gets or sets the Umbraco members membership providers' useLegacyEncoding state. This will return a boolean + /// + /// The useLegacyEncoding status. + public static bool UmbracoMembershipProviderLegacyEncoding + { + get + { + return IsConfiguredMembershipProviderUsingLegacyEncoding(Constants.Conventions.Member.UmbracoMemberProviderName); + } + set + { + SetMembershipProvidersLegacyEncoding(Constants.Conventions.Member.UmbracoMemberProviderName, value); + } + } + + /// + /// Gets or sets the Umbraco users membership providers' useLegacyEncoding state. This will return a boolean + /// + /// The useLegacyEncoding status. + public static bool UmbracoUsersMembershipProviderLegacyEncoding + { + get + { + return IsConfiguredMembershipProviderUsingLegacyEncoding(UmbracoSettings.DefaultBackofficeProvider); + } + set + { + SetMembershipProvidersLegacyEncoding(UmbracoSettings.DefaultBackofficeProvider, value); + } + } /// /// Saves a setting into the configuration file. @@ -315,6 +349,38 @@ namespace Umbraco.Core.Configuration return fileName; } + private static void SetMembershipProvidersLegacyEncoding(string providerName, bool useLegacyEncoding) + { + var webConfigFilename = GetFullWebConfigFileName(); + var webConfigXml = XDocument.Load(webConfigFilename, LoadOptions.PreserveWhitespace); + + var membershipConfigs = webConfigXml.XPathSelectElements("configuration/system.web/membership/providers/add").ToList(); + + if (membershipConfigs.Any() == false) + return; + + var provider = membershipConfigs.SingleOrDefault(c => c.Attribute("name") != null && c.Attribute("name").Value == providerName); + + if (provider == null) + return; + + provider.SetAttributeValue("useLegacyEncoding", useLegacyEncoding); + + webConfigXml.Save(webConfigFilename, SaveOptions.DisableFormatting); + } + + private static bool IsConfiguredMembershipProviderUsingLegacyEncoding(string providerName) + { + //check if this can even be configured. + var membershipProvider = Membership.Providers[providerName] as MembershipProviderBase; + if (membershipProvider == null) + { + return false; + } + + return membershipProvider.UseLegacyEncoding; + } + /// /// Gets the full path to root. /// diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index 0bdb0cd5c9..9f6b19fc00 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -658,7 +658,7 @@ namespace Umbraco.Core.Configuration { string defaultProvider = GetKey("/settings/providers/users/DefaultBackofficeProvider"); if (String.IsNullOrEmpty(defaultProvider)) - defaultProvider = "UsersMembershipProvider"; + defaultProvider = "UsersMembershipProvider"; return defaultProvider; } diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index f1b8d39765..9823d31f6b 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return ""; } } + public static string CurrentComment { get { return "RC"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 8ef9f1ca8f..b4c2cab2a7 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -85,7 +85,7 @@ namespace Umbraco.Core /// /// MediaType alias for a folder. - /// + /// public const string Folder = "Folder"; /// @@ -93,10 +93,10 @@ namespace Umbraco.Core /// public const string Image = "Image"; } - + /// /// Constants for Umbraco Member property aliases. - /// + /// public static class Member { public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider"; @@ -106,63 +106,63 @@ namespace Umbraco.Core /// /// Property alias for a Members Password Question /// - public const string PasswordQuestion = "umbracoPasswordRetrievalQuestionPropertyTypeAlias"; + public const string PasswordQuestion = "umbracoMemberPasswordRetrievalQuestion"; public const string PasswordQuestionLabel = "Password Question"; /// /// Property alias for Members Password Answer /// - public const string PasswordAnswer = "umbracoPasswordRetrievalAnswerPropertyTypeAlias"; + public const string PasswordAnswer = "umbracoMemberPasswordRetrievalAnswer"; public const string PasswordAnswerLabel = "Password Answer"; /// /// Property alias for the Comments on a Member /// - public const string Comments = "umbracoCommentPropertyTypeAlias"; + public const string Comments = "umbracoMemberComments"; - public const string CommentsLabel = "Comments"; + public const string CommentsLabel = "Comments"; /// /// Property alias for the Approved boolean of a Member /// - public const string IsApproved = "umbracoApprovePropertyTypeAlias"; + public const string IsApproved = "umbracoMemberApproved"; - public const string IsApprovedLabel = "Is Approved"; + public const string IsApprovedLabel = "Is Approved"; /// /// Property alias for the Locked out boolean of a Member /// - public const string IsLockedOut = "umbracoLockPropertyTypeAlias"; + public const string IsLockedOut = "umbracoMemberLockedOut"; - public const string IsLockedOutLabel = "Is Locked Out"; + public const string IsLockedOutLabel = "Is Locked Out"; /// /// Property alias for the last date the Member logged in /// - public const string LastLoginDate = "umbracoLastLoginPropertyTypeAlias"; + public const string LastLoginDate = "umbracoMemberLastLogin"; - public const string LastLoginDateLabel = "Last Login Date"; + public const string LastLoginDateLabel = "Last Login Date"; /// /// Property alias for the last date a Member changed its password /// - public const string LastPasswordChangeDate = "umbracoMemberLastPasswordChange"; + public const string LastPasswordChangeDate = "umbracoMemberLastPasswordChangeDate"; public const string LastPasswordChangeDateLabel = "Last Password Change Date"; /// /// Property alias for the last date a Member was locked out /// - public const string LastLockoutDate = "umbracoMemberLastLockout"; + public const string LastLockoutDate = "umbracoMemberLastLockoutDate"; public const string LastLockoutDateLabel = "Last Lockout Date"; /// /// Property alias for the number of failed login attemps /// - public const string FailedPasswordAttempts = "umbracoFailedPasswordAttemptsPropertyTypeAlias"; + public const string FailedPasswordAttempts = "umbracoMemberFailedPasswordAttempts"; public const string FailedPasswordAttemptsLabel = "Failed Password Attempts"; @@ -177,7 +177,7 @@ namespace Umbraco.Core { { Comments, - new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext) + new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext, true) { Alias = Comments, Name = CommentsLabel @@ -185,7 +185,7 @@ namespace Umbraco.Core }, { FailedPasswordAttempts, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Integer, true) { Alias = FailedPasswordAttempts, Name = FailedPasswordAttemptsLabel @@ -193,7 +193,7 @@ namespace Umbraco.Core }, { IsApproved, - new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true) { Alias = IsApproved, Name = IsApprovedLabel @@ -201,7 +201,7 @@ namespace Umbraco.Core }, { IsLockedOut, - new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true) { Alias = IsLockedOut, Name = IsLockedOutLabel @@ -209,7 +209,7 @@ namespace Umbraco.Core }, { LastLockoutDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastLockoutDate, Name = LastLockoutDateLabel @@ -217,7 +217,7 @@ namespace Umbraco.Core }, { LastLoginDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastLoginDate, Name = LastLoginDateLabel @@ -225,7 +225,7 @@ namespace Umbraco.Core }, { LastPasswordChangeDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastPasswordChangeDate, Name = LastPasswordChangeDateLabel @@ -233,7 +233,7 @@ namespace Umbraco.Core }, { PasswordAnswer, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true) { Alias = PasswordAnswer, Name = PasswordAnswerLabel @@ -241,7 +241,7 @@ namespace Umbraco.Core }, { PasswordQuestion, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true) { Alias = PasswordQuestion, Name = PasswordQuestionLabel diff --git a/src/Umbraco.Core/Constants-Packaging.cs b/src/Umbraco.Core/Constants-Packaging.cs new file mode 100644 index 0000000000..a73adf10de --- /dev/null +++ b/src/Umbraco.Core/Constants-Packaging.cs @@ -0,0 +1,51 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the constants used for Umbraco packages in the package.config xml + /// + public static class Packaging + { + public const string UmbPackageNodeName = "umbPackage"; + public const string DataTypesNodeName = "DataTypes"; + public const string PackageXmlFileName = "package.xml"; + public const string UmbracoPackageExtention = ".umb"; + public const string DataTypeNodeName = "DataType"; + public const string LanguagesNodeName = "Languages"; + public const string FilesNodeName = "Files"; + public const string StylesheetsNodeName = "Stylesheets"; + public const string TemplatesNodeName = "Templates"; + public const string OrgnameNodeName = "orgName"; + public const string NameNodeName = "Name"; + public const string TemplateNodeName = "Template"; + public const string AliasNodeName = "Alias"; + public const string DictionaryitemsNodeName = "DictionaryItems"; + public const string MacrosNodeName = "macros"; + public const string DocumentSetNodeName = "DocumentSet"; + public const string DocumentTypesNodeName = "DocumentTypes"; + public const string DocumentTypeNodeName = "DocumentType"; + public const string FileNodeName = "file"; + public const string OrgPathNodeName = "orgPath"; + public const string GuidNodeName = "guid"; + public const string StylesheetNodeName = "styleSheet"; + public const string MacroNodeName = "macro"; + public const string InfoNodeName = "info"; + public const string PackageRequirementsMajorXpath = "/package/requirements/major"; + public const string PackageRequirementsMinorXpath = "/package/requirements/minor"; + public const string PackageRequirementsPatchXpath = "/package/requirements/patch"; + public const string PackageNameXpath = "/package/name"; + public const string PackageVersionXpath = "/package/version"; + public const string PackageUrlXpath = "/package/url"; + public const string PackageLicenseXpath = "/package/license"; + public const string AuthorNameXpath = "/author/name"; + public const string AuthorWebsiteXpath = "/author/website"; + public const string ReadmeXpath = "/readme"; + public const string ControlNodeName = "control"; + public const string ActionNodeName = "Action"; + public const string ActionsNodeName = "Actions"; + public const string UndoNodeAttribute = "undo"; + public const string RunatNodeAttribute = "runat"; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index add9b65fce..ad559f8814 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -275,7 +275,7 @@ namespace Umbraco.Core //the database migration objects MigrationResolver.Current = new MigrationResolver( - () => PluginManager.Current.ResolveMigrationTypes()); + () => PluginManager.Current.ResolveTypes()); // todo: remove once we drop IPropertyEditorValueConverter support. PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver( diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index e074297eb8..2bc46688f2 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -3,6 +3,7 @@ using System.Configuration; using System.Data.SqlServerCe; using System.IO; using System.Linq; +using System.Web; using System.Web.Configuration; using System.Xml.Linq; using Umbraco.Core.Configuration; @@ -426,6 +427,13 @@ namespace Umbraco.Core if (_result == null) { + + if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted + && ProviderName == "MySql.Data.MySqlClient") + { + throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration"); + } + var database = new UmbracoDatabase(_connectionString, ProviderName); var dbSchema = new DatabaseSchemaCreation(database); _result = dbSchema.ValidateSchema(); @@ -434,15 +442,15 @@ namespace Umbraco.Core } internal Result CreateDatabaseSchemaAndData() - { - var readyForInstall = CheckReadyForInstall(); - if (readyForInstall.Success == false) - { - return readyForInstall.Result; - } - + { try { + var readyForInstall = CheckReadyForInstall(); + if (readyForInstall.Success == false) + { + return readyForInstall.Result; + } + LogHelper.Info("Database configuration status: Started"); string message; @@ -500,14 +508,15 @@ namespace Umbraco.Core /// internal Result UpgradeSchemaAndData() { - var readyForInstall = CheckReadyForInstall(); - if (readyForInstall.Success == false) - { - return readyForInstall.Result; - } - try { + + var readyForInstall = CheckReadyForInstall(); + if (readyForInstall.Success == false) + { + return readyForInstall.Result; + } + LogHelper.Info("Database upgrade started"); var database = new UmbracoDatabase(_connectionString, ProviderName); @@ -567,6 +576,12 @@ namespace Umbraco.Core private Attempt CheckReadyForInstall() { + if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted + && ProviderName == "MySql.Data.MySqlClient") + { + throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration"); + } + if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName))) { return Attempt.Fail(new Result diff --git a/src/Umbraco.Core/DateTimeExtensions.cs b/src/Umbraco.Core/DateTimeExtensions.cs index 74460814b3..1ff996170f 100644 --- a/src/Umbraco.Core/DateTimeExtensions.cs +++ b/src/Umbraco.Core/DateTimeExtensions.cs @@ -18,5 +18,30 @@ namespace Umbraco.Core return dt.ToString("yyyy-MM-dd HH:mm:ss"); } + public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo) + { + if (truncateTo == DateTruncate.Year) + return new DateTime(dt.Year, 0, 0); + if (truncateTo == DateTruncate.Month) + return new DateTime(dt.Year, dt.Month, 0); + if (truncateTo == DateTruncate.Day) + return new DateTime(dt.Year, dt.Month, dt.Day); + if (truncateTo == DateTruncate.Hour) + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0); + if (truncateTo == DateTruncate.Minute) + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0); + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); + } + + public enum DateTruncate + { + Year, + Month, + Day, + Hour, + Minute, + Second + } + } } diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index 349c374bb7..525f75e709 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; +using Umbraco.Core.Logging; namespace Umbraco.Core { @@ -14,7 +15,7 @@ namespace Umbraco.Core public static IEnumerable> InGroupsOf(this IEnumerable source, int groupSize) { if (source == null) - throw new NullReferenceException("source"); + throw new NullReferenceException("source"); // enumerate the source only once! return new InGroupsEnumerator(source, groupSize).Groups(); @@ -125,23 +126,13 @@ namespace Umbraco.Core } /// The flatten list. - /// The items. - /// The select child. - /// Item type + /// The items. + /// The select child. + /// Item type /// list of TItem - [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")] - public static IEnumerable FlattenList(this IEnumerable items, Func> selectChild) + public static IEnumerable FlattenList(this IEnumerable e, Func> f) { - IEnumerable children = items != null && items.Any() - ? items.SelectMany(selectChild).FlattenList(selectChild) - : Enumerable.Empty(); - - if (items != null) - { - return items.Concat(children); - } - - return null; + return e.SelectMany(c => f(c).FlattenList(f)).Concat(e); } /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 8b3440d435..e89cb6e2af 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -25,6 +25,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 /// diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs index f461c4007c..6daf99a58d 100644 --- a/src/Umbraco.Core/Models/EntityExtensions.cs +++ b/src/Umbraco.Core/Models/EntityExtensions.cs @@ -23,5 +23,5 @@ namespace Umbraco.Core.Models var dirty = (IRememberBeingDirty)entity; return dirty.WasPropertyDirty("Id"); } - } + } } diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index 99bf3827fe..ca87fa11c7 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -15,62 +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; - } - - //TODO: Should we just get rid of this one? no reason to have a level set. - internal 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, IMemberType contentType) - : this(name, email, username, password, -1, contentType) + /// + /// 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; } - //public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType) - // : base(name, parent, contentType, new PropertyCollection()) - //{ - // Mandate.ParameterNotNull(contentType, "contentType"); - - // _contentType = contentType; - // _email = email; - // _username = username; - // _password = password; - //} - - 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); @@ -109,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); } } @@ -161,14 +203,16 @@ namespace Umbraco.Core.Models } /// - /// Gets or sets the Password Answer + /// Gets or sets the raw password answer value /// /// + /// For security reasons this value should be encrypted, the encryption process is handled by the memberhip provider + /// /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias /// Part of the standard properties collection. /// [IgnoreDataMember] - public string PasswordAnswer + public string RawPasswordAnswerValue { get { @@ -230,7 +274,9 @@ namespace Umbraco.Core.Models { get { - var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, "IsApproved", true); + 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; var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo(); @@ -238,7 +284,8 @@ namespace Umbraco.Core.Models { return tryConvert.Result; } - return default(bool); + //if the property exists but it cannot be converted, we will assume true + return true; } set { @@ -270,7 +317,7 @@ namespace Umbraco.Core.Models { return tryConvert.Result; } - return default(bool); + return false; } set { @@ -418,14 +465,6 @@ namespace Umbraco.Core.Models public virtual string ContentTypeAlias { get { return _contentTypeAlias; } - internal set - { - SetPropertyValueAndDetectChanges(o => - { - _contentTypeAlias = value; - return _contentTypeAlias; - }, _contentTypeAlias, DefaultContentTypeAliasSelector); - } } /// diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index e324b78447..aea28e20c5 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -44,7 +44,9 @@ namespace Umbraco.Core.Models SetPropertyValueAndDetectChanges(o => { - _alias = value == "_umbracoSystemDefaultProtectType" ? value : value.ToSafeAlias(); + _alias = value == "_umbracoSystemDefaultProtectType" + ? value + : (value == null ? string.Empty : value.ToSafeAlias() ); return _alias; }, _alias, AliasSelector); } diff --git a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs index d6e3294c72..6f92982961 100644 --- a/src/Umbraco.Core/Models/Membership/IMembershipUser.cs +++ b/src/Umbraco.Core/Models/Membership/IMembershipUser.cs @@ -9,9 +9,19 @@ namespace Umbraco.Core.Models.Membership 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 IsLockedOut { get; set; } diff --git a/src/Umbraco.Core/Services/MemberCountType.cs b/src/Umbraco.Core/Models/Membership/MemberCountType.cs similarity index 80% rename from src/Umbraco.Core/Services/MemberCountType.cs rename to src/Umbraco.Core/Models/Membership/MemberCountType.cs index 882d1515e2..89b428a827 100644 --- a/src/Umbraco.Core/Services/MemberCountType.cs +++ b/src/Umbraco.Core/Models/Membership/MemberCountType.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Core.Services +namespace Umbraco.Core.Models.Membership { /// /// The types of members to count diff --git a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs similarity index 92% rename from src/Umbraco.Core/Models/Membership/MembershipExtensions.cs rename to src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs index 6be0afe441..2f1cc78001 100644 --- a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/MembershipUserExtensions.cs @@ -3,7 +3,7 @@ using System.Web.Security; namespace Umbraco.Core.Models.Membership { - internal static class MembershipExtensions + internal static class MembershipUserExtensions { internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName) { diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 11ee83d069..2e9a73ba50 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -27,6 +27,7 @@ namespace Umbraco.Core.Models.Membership if (userType == null) throw new ArgumentNullException("userType"); _userType = userType; + _defaultPermissions = _userType.Permissions; //Groups = new List { userType }; SessionTimeout = 60; _sectionCollection = new ObservableCollection(); @@ -38,15 +39,17 @@ namespace Umbraco.Core.Models.Membership _isLockedOut = false; _startContentId = -1; _startMediaId = -1; + //cannot be null + _rawPasswordValue = ""; } - public User(string name, string email, string username, string password, IUserType userType) + public User(string name, string email, string username, string rawPasswordValue, IUserType userType) : this(userType) { _name = name; _email = email; _username = username; - _password = password; + _rawPasswordValue = rawPasswordValue; _isApproved = true; _isLockedOut = false; _startContentId = -1; @@ -65,7 +68,7 @@ namespace Umbraco.Core.Models.Membership private string _username; private string _email; - private string _password; + private string _rawPasswordValue; private bool _isApproved; private bool _isLockedOut; private string _language; @@ -81,7 +84,7 @@ namespace Umbraco.Core.Models.Membership 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 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); @@ -157,16 +160,16 @@ namespace Umbraco.Core.Models.Membership } } [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); } } @@ -204,7 +207,7 @@ namespace Umbraco.Core.Models.Membership [IgnoreDataMember] public string PasswordQuestion { get; set; } [IgnoreDataMember] - public string PasswordAnswer { get; set; } + public string RawPasswordAnswerValue { get; set; } [IgnoreDataMember] public string Comments { get; set; } [IgnoreDataMember] diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index f177ff209c..165074dd99 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -43,7 +43,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 Alias of the PropertyType, which this Property is based on /// @@ -59,9 +59,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 04f9ca4d80..cdc1990766 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -14,6 +14,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; @@ -29,16 +30,28 @@ namespace Umbraco.Core.Models public PropertyType(IDataTypeDefinition dataTypeDefinition) { if(dataTypeDefinition.HasIdentity) - DataTypeDefinitionId = dataTypeDefinition.Id; + _dataTypeDefinitionId = dataTypeDefinition.Id; - DataTypeId = dataTypeDefinition.ControlId; - DataTypeDatabaseType = dataTypeDefinition.DatabaseType; + _dataTypeId = dataTypeDefinition.ControlId; + _dataTypeDatabaseType = dataTypeDefinition.DatabaseType; } internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType) + : this(dataTypeControlId, dataTypeDatabaseType, false) { - DataTypeId = dataTypeControlId; - 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(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType) + { + _isExplicitDbType = isExplicitDbType; + _dataTypeId = dataTypeControlId; + _dataTypeDatabaseType = dataTypeDatabaseType; } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -149,6 +162,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; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index eb53215896..453e8b0862 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/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedItemType.cs index a98a3c2a75..681fec8854 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/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 dfe84659de..5477628222 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/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index 1fd33bd6a8..e0c13053e3 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -57,7 +57,8 @@ namespace Umbraco.Core.Models Trashed = trashed; } - public UmbracoEntity(int trashed) + // for MySql + public UmbracoEntity(UInt64 trashed) { AdditionalData = new Dictionary(); UmbracoProperties = new List(); diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index d3b1184940..65ae278b48 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -237,12 +237,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)) { @@ -311,7 +313,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)) { @@ -323,6 +326,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 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/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 03c9616485..d6ea1b3967 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/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/Factories/MemberFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs index 1aa8f70bb8..efa92566a6 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/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index dd7d8d9277..d2b3d62e85 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -19,7 +19,7 @@ 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, @@ -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 7fe64a3f33..b6b3425425 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -11,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, @@ -32,13 +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); + 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. - var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + //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; @@ -55,10 +56,10 @@ namespace Umbraco.Core.Persistence.Factories return memberType; } - private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType) + private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { - var propertyGroups = new PropertyGroupCollection(); - var standardProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var propertyGroups = new PropertyGroupCollection(); + foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) { var group = new PropertyGroup(); @@ -91,10 +92,19 @@ namespace Umbraco.Core.Persistence.Factories var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.ControlId, - //ensures that any built-in membership properties have their correct dbtype assigned no matter - //what the underlying data type is - MemberTypeRepository.GetDbTypeForProperty(typeDto.Alias, typeDto.DbType.EnumParse(true), standardProps)) + //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.ControlId, + 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, @@ -123,34 +133,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 MemberTypePropertyProfileAccess(propertyType.ViewOnProfile, propertyType.CanEdit)); - //PropertyType Collection - propertyTypes.Add(new PropertyType(propertyType.ControlId, - 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.ControlId, + 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 57cc79bc32..b3382df1e3 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 = (guid, dbType, alias) => new PropertyType(guid, 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.ControlId, - 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.ControlId, + 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/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index 12c303beb4..080776e7fe 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Factories Key = guidId, StartContentId = dto.ContentStartId, StartMediaId = dto.MediaStartId.HasValue ? dto.MediaStartId.Value : -1, - Password = dto.Password, + RawPasswordValue = dto.Password, Username = dto.Login, Name = dto.UserName, IsLockedOut = dto.NoConsole, @@ -61,7 +61,7 @@ namespace Umbraco.Core.Persistence.Factories Email = entity.Email, Login = entity.Username, NoConsole = entity.IsLockedOut, - Password = entity.Password, + 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/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs index d895e1c127..3a318d7136 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/UserMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs index bdb411fc5a..15825cca84 100644 --- a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs @@ -31,7 +31,7 @@ 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); CacheMap(src => src.DefaultPermissions, dto => dto.DefaultPermissions); CacheMap(src => src.StartMediaId, dto => dto.MediaStartId); 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/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index 53f1938c00..ee10f20730 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -125,67 +125,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); } @@ -194,40 +187,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 c70eb820b8..3fa8cd4ab0 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,36 +44,42 @@ 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 is for this IX_umbracoNodeTrashed which is added in 6.2, then it must mean we are on 6.1 + if (Errors.Any(x => x.Item1.Equals("Index") && (x.Item2.InvariantEquals("IX_umbracoNodeTrashed")))) + { + return new Version(6, 1, 0); + } + return UmbracoVersion.Current; } @@ -106,6 +117,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..f49bea5ce0 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) { 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..9bc9cd8e9c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -3,10 +3,11 @@ 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. /// @@ -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(); + //SD: Why do we want this? if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _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, _configuredVersion, _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,75 @@ 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; + } 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 > _configuredVersion && + migrationAttribute.TargetVersion <= _targetVersion && + migrationAttribute.ProductName == _productName + orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending + select migration).Distinct(); return migrations; } 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 > _configuredVersion && + migrationAttribute.TargetVersion <= _targetVersion && + migrationAttribute.ProductName == _productName + 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/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/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 1bd6b45307..a022c4894c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("6.2.0", 1, 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 a5ae8658bc..cecce85d42 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs @@ -1,37 +1,64 @@ 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("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..a4fae3ae83 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs @@ -0,0 +1,97 @@ +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("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("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) == 0) + { + Create.PrimaryKey("PK_cmsTagRelationship") + .OnTable("cmsTagRelationship") + .Columns(new[] { "nodeId", "tagId" }); + } + + //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..1a5b6cd61e --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs @@ -0,0 +1,21 @@ +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero +{ + [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..f64c4e380c --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs @@ -0,0 +1,88 @@ +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("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} + }; + + 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/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 08a08e49d3..57f076064b 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs @@ -9,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"; diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 6ab3fcc592..e3ff272cee 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -227,41 +227,6 @@ namespace Umbraco.Core.Persistence.Querying } - private 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"); - } - } - protected virtual string VisitMethodCall(MethodCallExpression m) { List args = this.VisitExpressionList(m.Arguments); diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs index ecd5bb0087..9a27cc8183 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs @@ -253,12 +253,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, RemoveQuote(args[0].ToString().ToUpper())); case "EndsWith": - return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper()); case "Contains": - return string.Format("upper({0}) like '%{1}%'", r, 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/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 24048a0e76..517c004e0f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -132,6 +132,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", @@ -278,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories from p in perm.AssignedPermissions select new Tuple(perm.UserId, p)).ToList(); - permissionsRepo.AssignEntityPermissions(entity, userPermissions); + 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; @@ -531,15 +532,15 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Assigns one permission to an entity for multiple users + /// Assigns a single permission to the current content item for the specified user ids /// /// /// - /// - public void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds) + /// + public void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds) { var repo = new PermissionRepository(UnitOfWork, _cacheHelper); - repo.AssignEntityPermissions(entity, permission, userIds); + repo.AssignEntityPermission(entity, permission, userIds); } public IEnumerable GetPermissionsForEntity(int entityId) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index fb700e4f30..da259c1594 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Linq; using System.Threading.Tasks; @@ -11,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 @@ -56,8 +58,24 @@ namespace Umbraco.Core.Persistence.Repositories } } + protected virtual PropertyType CreatePropertyType(Guid dataTypeId, DataTypeDatabaseType dbType, string propertyTypeAlias) + { + return new PropertyType(dataTypeId, 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; @@ -145,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 ControlId if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int)) { - var datatype = Database.FirstOrDefault("WHERE controlId = @Id", new { Id = propertyType.DataTypeId }); - propertyType.DataTypeDefinitionId = datatype.DataTypeId; + AssignDataTypeFromPropertyEditor(propertyType); } var propertyTypeDto = propertyFactory.BuildPropertyTypeDto(tabId, propertyType); int typePrimaryKey = Convert.ToInt32(Database.Insert(propertyTypeDto)); @@ -161,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; @@ -299,24 +329,11 @@ namespace Umbraco.Core.Persistence.Repositories foreach (var propertyType in entity.PropertyTypes) { var tabId = propertyType.PropertyGroupId != null ? propertyType.PropertyGroupId.Value : default(int); + //If the Id of the DataType is not set, we resolve it from the db by its ControlId if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int)) { - //we cannot try to assign a data type of it's an empty guid - if (propertyType.DataTypeId != Guid.Empty) - { - var sql = new Sql() - .Select("*") - .From() - .Where("controlId = @Id", new { Id = propertyType.DataTypeId }) - .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; - } - } + AssignDataTypeFromPropertyEditor(propertyType); } //validate the alias! @@ -358,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); } @@ -375,25 +392,23 @@ 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.ControlId, - 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.ControlId, 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)); @@ -433,5 +448,32 @@ namespace Umbraco.Core.Persistence.Repositories 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 an empty guid + if (propertyType.DataTypeId != Guid.Empty) + { + var sql = new Sql() + .Select("*") + .From() + .Where("controlId = @Id", new { Id = propertyType.DataTypeId }) + .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.DataTypeId); + } + } + } } } \ No newline at end of file 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/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 16044a313d..3a343febae 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds); + void AssignEntityPermission(IContent entity, char permission, IEnumerable userIds); /// /// Gets the list of permissions for the content item diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index 2dd7a7f83b..9534aead90 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -50,5 +50,9 @@ namespace Umbraco.Core.Persistence.Repositories /// 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/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index f966a4d26f..538548831a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -38,8 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); - - + /// /// Gets the user permissions for the specified entities /// @@ -49,11 +48,11 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds); /// - /// Assigns the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single user to any number of entities /// /// /// /// - void AssignUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); + void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 241b5ad799..47f202e34e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -124,6 +124,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", diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 5b46e73658..c3da14da23 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -167,6 +167,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", @@ -310,7 +311,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"); @@ -320,7 +324,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"); } @@ -555,7 +559,6 @@ namespace Umbraco.Core.Persistence.Repositories resultQuery = sql; } - //get the referenced column name var expressionMember = ExpressionHelper.GetMemberInfo(orderBy); //now find the mapped column name @@ -567,8 +570,19 @@ namespace Umbraco.Core.Persistence.Repositories } //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()); + } - var pagedResult = Database.Page(pageIndex + 1, pageSize, resultQuery); + 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); @@ -577,10 +591,7 @@ namespace Umbraco.Core.Persistence.Repositories { return Enumerable.Empty(); } - var result = GetAll(pagedResult.Items.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()); + return GetAll(resolveIds(pagedResult.Items)).ToArray(); } private IMember BuildFromDto(List dtos) diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 8ecc194d29..ea95936d32 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -18,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) { @@ -40,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) @@ -99,11 +99,11 @@ 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", - "cmsDataType.controlId", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId", + "cmsDataType.controlId", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId", "cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.parentGroupId", "cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId") .From() @@ -166,8 +166,10 @@ 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 entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName); var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); @@ -179,10 +181,10 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); - EnsureCorrectDbTypeForBuiltInProperties(entity); + EnsureExplicitDataTypeForBuiltInProperties(entity); PersistNewBaseContentType(dto, entity); - + //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); foreach (var memberTypeDto in memberTypeDtos) @@ -216,12 +218,12 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); - EnsureCorrectDbTypeForBuiltInProperties(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); @@ -236,19 +238,40 @@ namespace Umbraco.Core.Persistence.Repositories #endregion /// - /// Ensure that all the built-in membership provider properties have their correct db types - /// and property editors assigned. + /// Override so we can specify explicit db type's on any property types that are built-in. + /// + /// + /// + /// + /// + protected override PropertyType CreatePropertyType(Guid dataTypeId, 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(dataTypeId, 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 EnsureCorrectDbTypeForBuiltInProperties(IContentTypeBase memberType) + private static void EnsureExplicitDataTypeForBuiltInProperties(IContentTypeBase memberType) { var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); foreach (var propertyType in memberType.PropertyTypes) { - propertyType.DataTypeDatabaseType = GetDbTypeForProperty(propertyType.Alias, propertyType.DataTypeDatabaseType, stdProps); - //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 = GetPropertyEditorForProperty(propertyType.Alias, propertyType.DataTypeId, stdProps); + 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; + } } } @@ -273,9 +296,11 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - /// - internal static DataTypeDatabaseType GetDbTypeForProperty( - string propAlias, + /// + /// Successful attempt if it was a built in property + /// + internal static Attempt GetDbTypeForBuiltInProperty( + string propAlias, DataTypeDatabaseType dbType, Dictionary standardProps) { @@ -285,14 +310,23 @@ namespace Umbraco.Core.Persistence.Repositories if (aliases.Contains(propAlias)) { //return the pre-determined db type for this property - return standardProps.Single(x => x.Key == propAlias).Value.DataTypeDatabaseType; + return Attempt.Succeed(standardProps.Single(x => x.Key == propAlias).Value.DataTypeDatabaseType); } - return dbType; + return Attempt.Fail(dbType); } - internal static Guid GetPropertyEditorForProperty( - string propAlias, + /// + /// + /// + /// + /// + /// + /// + /// Successful attempt if it was a built in property + /// + internal static Attempt GetPropertyEditorForBuiltInProperty( + string propAlias, Guid propertyEditor, Dictionary standardProps) { @@ -302,10 +336,10 @@ namespace Umbraco.Core.Persistence.Repositories if (aliases.Contains(propAlias)) { //return the pre-determined db type for this property - return standardProps.Single(x => x.Key == propAlias).Value.DataTypeId; + return Attempt.Succeed(standardProps.Single(x => x.Key == propAlias).Value.DataTypeId); } - return propertyEditor; + return Attempt.Fail(propertyEditor); } } } \ 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 fd96dc7a54..3081be7aff 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -119,7 +119,10 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - public void AssignUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + /// + /// 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()) @@ -157,15 +160,18 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - /// - /// This will first clear the permissions for this entity then re-create them - /// - public void AssignEntityPermissions(TEntity entity, char permission, IEnumerable userIds) + 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", new {nodeId = entity.Id}); + 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 { @@ -194,7 +200,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// This will first clear the permissions for this entity then re-create them /// - public void AssignEntityPermissions(TEntity entity, IEnumerable> userPermissions) + public void ReplaceEntityPermissions(TEntity entity, IEnumerable> userPermissions) { var db = _unitOfWork.Database; using (var trans = db.GetTransaction()) @@ -208,7 +214,9 @@ namespace Umbraco.Core.Persistence.Repositories UserId = p.Item1 }).ToArray(); - _unitOfWork.Database.BulkInsertRecords(actions); + _unitOfWork.Database.BulkInsertRecords(actions, trans); + + trans.Complete(); //Raise the event AssignedPermissions.RaiseEvent( diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 05b8c84fd1..737ed7daef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -220,8 +220,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; + } + } /// @@ -230,8 +241,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/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 63a2d4917e..8fac8d8c11 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -131,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", @@ -325,15 +327,15 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Assigns the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single user to any number of entities /// /// /// /// - public void AssignUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) { var repo = new PermissionRepository(UnitOfWork, _cacheHelper); - repo.AssignUserPermissions(userId, permissions, entityIds); + repo.ReplaceUserPermissions(userId, permissions, entityIds); } #endregion diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index c62fa1a923..b4c0020305 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -59,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/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index 0b8d80a21f..ba6f4dcc7e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -36,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", @@ -55,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", @@ -78,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( @@ -97,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( @@ -115,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 " + @@ -177,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) diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index d9e9599dab..170b10cb5e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -226,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/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 4046a7575d..ca9e88fb1e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -151,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/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 004aabfc70..1ceeddafb7 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -212,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/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index f6b5cf7f91..3928a0c1cc 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -529,16 +529,7 @@ namespace Umbraco.Core { return ResolveTypesWithAttribute(); } - - /// - /// Returns all available IMigrations in application - /// - /// - internal IEnumerable ResolveMigrationTypes() - { - return ResolveTypes(); - } - + /// /// Returns all SqlSyntaxProviders with the SqlSyntaxProviderAttribute /// diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index ed64f179b7..3e8fa1a568 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -1,15 +1,93 @@ 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[UmbracoSettings.DefaultBackofficeProvider] == null) + { + throw new InvalidOperationException("No membership provider found with name " + UmbracoSettings.DefaultBackofficeProvider); + } + return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; + } + + 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 /// diff --git a/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs b/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs index ce56c2708e..fc07abc896 100644 --- a/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/UmbracoMembersMembershipProviderBase.cs @@ -6,7 +6,7 @@ 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 IUmbracoContentTypeMembershipProvider + public interface IUmbracoMemberTypeMembershipProvider { string LockPropertyTypeAlias { get; } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 607538decc..c90c0b5029 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -65,12 +65,12 @@ namespace Umbraco.Core.Services /// /// /// - public void AssignContentPermissions(IContent entity, char permission, IEnumerable userIds) + 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); } } @@ -176,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)) { @@ -191,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); @@ -216,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)) { @@ -231,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); @@ -1984,6 +2004,7 @@ namespace Umbraco.Core.Services /// /// Occurs before Create /// + [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; /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 672973a3ca..f26ec77b53 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Services /// /// /// - void AssignContentPermissions(IContent entity, char permission, IEnumerable userIds); + void AssignContentPermission(IContent entity, char permission, IEnumerable userIds); /// /// Gets the list of permissions for the content item diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index 6238cf05a8..f7918b2d8d 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -5,11 +5,27 @@ using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Services { + /// /// Defines the MemberService, which is an easy access to operations involving (umbraco) members. /// public interface IMemberService : IMembershipMemberService { + IMember CreateMember(string username, string email, string name, string memberTypeAlias); + IMember CreateMember(string username, string email, string name, IMemberType memberType); + IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias); + IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType); + + /// + /// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method + /// + /// The member to save the password for + /// + /// + /// This method exists so that Umbraco developers can use one entry point to create/update members if they choose to. + /// + void SavePassword(IMember member, string password); + /// /// Checks if a member with the id exists /// diff --git a/src/Umbraco.Core/Services/IMembershipMemberService.cs b/src/Umbraco.Core/Services/IMembershipMemberService.cs index 807b250ea7..d4d865614a 100644 --- a/src/Umbraco.Core/Services/IMembershipMemberService.cs +++ b/src/Umbraco.Core/Services/IMembershipMemberService.cs @@ -13,8 +13,8 @@ namespace Umbraco.Core.Services /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. /// public interface IMembershipMemberService : IMembershipMemberService, IMembershipRoleService - { - IMember CreateMemberWithIdentity(string username, string email, string password, IMemberType memberType, bool raiseEvents = true); + { + IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType); } /// @@ -44,11 +44,12 @@ namespace Umbraco.Core.Services /// /// /// - /// + /// + /// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database + /// /// - /// /// - T CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true); + T CreateWithIdentity(string username, string email, string rawPasswordValue, string memberTypeAlias); /// /// Gets the member by the provider key @@ -67,20 +68,20 @@ namespace Umbraco.Core.Services T GetByUsername(string login); void Delete(T membershipUser); + + void Save(T entity, bool raiseEvents = true); - void Save(T membershipUser, bool raiseEvents = true); + void Save(IEnumerable entities, bool raiseEvents = true); - void Save(IEnumerable members, bool raiseEvents = true); + IEnumerable FindByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); - IEnumerable FindMembersByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); - - IEnumerable FindMembersByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + IEnumerable FindByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); /// /// Gets the total number of members based on the count type /// /// - int GetMemberCount(MemberCountType countType); + int GetCount(MemberCountType countType); /// /// Gets a list of paged member data @@ -89,6 +90,6 @@ namespace Umbraco.Core.Services /// /// /// - IEnumerable GetAllMembers(int pageIndex, int pageSize, out int totalRecords); + IEnumerable GetAll(int pageIndex, int pageSize, out int totalRecords); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMembershipUserService.cs b/src/Umbraco.Core/Services/IMembershipUserService.cs index 57be495f47..5739d6940c 100644 --- a/src/Umbraco.Core/Services/IMembershipUserService.cs +++ b/src/Umbraco.Core/Services/IMembershipUserService.cs @@ -10,8 +10,6 @@ namespace Umbraco.Core.Services /// public interface IMembershipUserService : IMembershipMemberService { - - IUser CreateMemberWithIdentity(string username, string email, string password, IUserType userType, bool raiseEvents = true); - + IUser CreateUserWithIdentity(string username, string email, IUserType userType); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 772d399dc9..191f9085f6 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -9,6 +9,16 @@ namespace Umbraco.Core.Services /// public interface IUserService : IMembershipUserService { + /// + /// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method + /// + /// The user to save the password for + /// + /// + /// This method exists so that Umbraco developers can use one entry point to create/update users if they choose to. + /// + void SavePassword(IUser user, string password); + /// /// To permanently delete the user pass in true, otherwise they will just be disabled /// @@ -54,12 +64,12 @@ namespace Umbraco.Core.Services IEnumerable GetPermissions(IUser user, params int[] nodeIds); /// - /// Assigns the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single user to any number of entities /// /// /// /// - void AssignUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); + void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); #region User types diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 7ee4fc6eab..dfeb271936 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -122,12 +122,23 @@ namespace Umbraco.Core.Services { var mediaType = FindMediaTypeByAlias(mediaTypeAlias); var media = new Models.Media(name, parentId, mediaType); + + //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(media, mediaTypeAlias, parentId), this)) { media.WasCancelled = true; return media; } + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(media), this)) + { + media.WasCancelled = true; + return media; + } + + //TODO: Once we fix up the transaction logic, these write locks should be replaced with + // an outter transaction instead. using (new WriteLock(Locker)) { var uow = _uowProvider.GetUnitOfWork(); @@ -142,6 +153,8 @@ namespace Umbraco.Core.Services } } + Saved.RaiseEvent(new SaveEventArgs(media, false), this); + Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parentId), this); Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id); @@ -166,12 +179,21 @@ namespace Umbraco.Core.Services { var mediaType = FindMediaTypeByAlias(mediaTypeAlias); var media = new Models.Media(name, parent, mediaType); + + //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(media, mediaTypeAlias, parent), this)) { media.WasCancelled = true; return media; } + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(media), this)) + { + media.WasCancelled = true; + return media; + } + using (new WriteLock(Locker)) { var uow = _uowProvider.GetUnitOfWork(); @@ -186,6 +208,8 @@ namespace Umbraco.Core.Services } } + Saved.RaiseEvent(new SaveEventArgs(media, false), this); + Created.RaiseEvent(new NewEventArgs(media, false, mediaTypeAlias, parent), this); Audit.Add(AuditTypes.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id); @@ -1042,6 +1066,7 @@ namespace Umbraco.Core.Services /// /// Occurs before Create /// + [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; /// diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index f7cccd798e..33b7721556 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -4,8 +4,11 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Web.Security; using System.Xml.Linq; +using Umbraco.Core.Auditing; using Umbraco.Core.Events; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; @@ -13,6 +16,7 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using System.Linq; +using Umbraco.Core.Security; namespace Umbraco.Core.Services { @@ -86,6 +90,37 @@ namespace Umbraco.Core.Services } } + /// + /// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method + /// + /// The member to save the password for + /// + /// + /// This method exists so that Umbraco developers can use one entry point to create/update members if they choose to. + /// + public void SavePassword(IMember member, string password) + { + if (member == null) throw new ArgumentNullException("member"); + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) + { + provider.ChangePassword(member.Username, "", password); + } + + //go re-fetch the member and update the properties that may have changed + var result = GetByUsername(member.Username); + if (result != null) + { + //should never be null but it could have been deleted by another thread. + member.RawPasswordValue = result.RawPasswordValue; + member.LastPasswordChangeDate = result.LastPasswordChangeDate; + member.UpdateDate = member.UpdateDate; + } + + throw new NotSupportedException("When using a non-Umbraco membership provider you must change the member password by using the MembershipProvider.ChangePassword method"); + } + /// /// Checks if a member with the id exists /// @@ -216,30 +251,39 @@ namespace Umbraco.Core.Services var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - var query = new Query(); - + var sql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == new Guid(Constants.ObjectTypes.Member)); + switch (matchType) { case StringPropertyMatchType.Exact: - query.Where(member => member.Name.Equals(displayNameToMatch)); + sql.Where(dto => dto.Text.Equals(displayNameToMatch)); break; case StringPropertyMatchType.Contains: - query.Where(member => member.Name.Contains(displayNameToMatch)); + sql.Where(dto => dto.Text.Contains(displayNameToMatch)); break; case StringPropertyMatchType.StartsWith: - query.Where(member => member.Name.StartsWith(displayNameToMatch)); + sql.Where(dto => dto.Text.StartsWith(displayNameToMatch)); break; case StringPropertyMatchType.EndsWith: - query.Where(member => member.Name.EndsWith(displayNameToMatch)); + sql.Where(dto => dto.Text.EndsWith(displayNameToMatch)); break; case StringPropertyMatchType.Wildcard: - query.Where(member => member.Name.SqlWildcard(displayNameToMatch, TextColumnType.NVarchar)); + sql.Where(dto => dto.Text.SqlWildcard(displayNameToMatch, TextColumnType.NVarchar)); break; default: throw new ArgumentOutOfRangeException("matchType"); } - return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Name); + sql.OrderBy(dto => dto.Text); + + var result = repository.GetPagedResultsByQuery(sql, pageIndex, pageSize, out totalRecords, + dtos => dtos.Select(x => x.NodeId).ToArray()); + + //ensure this result is sorted correct just in case + return result.OrderBy(x => x.Name); } } @@ -252,7 +296,7 @@ namespace Umbraco.Core.Services /// /// /// - public IEnumerable FindMembersByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + public IEnumerable FindByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) @@ -284,7 +328,7 @@ namespace Umbraco.Core.Services } } - public IEnumerable FindMembersByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + public IEnumerable FindByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) @@ -525,7 +569,7 @@ namespace Umbraco.Core.Services /// that have their last active date within the Membership.UserIsOnlineTimeWindow (which is in minutes). It isn't exact science /// but that is how MS have made theirs so we'll follow that principal. /// - public int GetMemberCount(MemberCountType countType) + public int GetCount(MemberCountType countType) { using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork())) { @@ -565,7 +609,7 @@ namespace Umbraco.Core.Services } - public IEnumerable GetAllMembers(int pageIndex, int pageSize, out int totalRecords) + public IEnumerable GetAll(int pageIndex, int pageSize, out int totalRecords) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) @@ -574,19 +618,100 @@ namespace Umbraco.Core.Services } } - public IMember CreateMemberWithIdentity(string username, string email, string password, IMemberType memberType, bool raiseEvents = true) + /// + /// Creates a member object + /// + /// + /// + /// + /// + /// + public IMember CreateMember(string username, string email, string name, string memberTypeAlias) + { + var memberType = FindMemberTypeByAlias(memberTypeAlias); + return CreateMember(username, email, name, memberType); + } + + /// + /// Creates a new member object + /// + /// + /// + /// + /// + /// + public IMember CreateMember(string username, string email, string name, IMemberType memberType) + { + var member = new Member(name, email.ToLower().Trim(), username, memberType); + + Created.RaiseEvent(new NewEventArgs(member, false, memberType.Alias, -1), this); + + return member; + } + + /// + /// Creates a member with an Id + /// + /// + /// + /// + /// + /// + public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias) + { + var memberType = FindMemberTypeByAlias(memberTypeAlias); + return CreateMemberWithIdentity(username, email, name, memberType); + } + + /// + /// Creates a member with an Id, the username will be used as their name + /// + /// + /// + /// + /// + public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType) + { + return CreateMemberWithIdentity(username, email, username, memberType); + } + + /// + /// Creates a member with an Id + /// + /// + /// + /// + /// + /// + public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType) + { + return CreateMemberWithIdentity(username, email, name, "", memberType); + } + + /// + /// Creates and persists a new Member + /// + /// + /// + /// + /// + /// + IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string rawPasswordValue, string memberTypeAlias) + { + var memberType = FindMemberTypeByAlias(memberTypeAlias); + return CreateMemberWithIdentity(username, email, memberType); + } + + private IMember CreateMemberWithIdentity(string username, string email, string name, string rawPasswordValue, IMemberType memberType) { if (memberType == null) throw new ArgumentNullException("memberType"); - var member = new Member(username, email.ToLower().Trim(), username, password, -1, memberType); + var member = new Member(name, email.ToLower().Trim(), username, rawPasswordValue, memberType); - if (raiseEvents) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this)) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this)) - { - member.WasCancelled = true; - return member; - } + member.WasCancelled = true; + return member; } var uow = _uowProvider.GetUnitOfWork(); @@ -600,40 +725,12 @@ namespace Umbraco.Core.Services CreateAndSaveMemberXml(xml, member.Id, uow.Database); } - if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(member, false), this); + Saved.RaiseEvent(new SaveEventArgs(member, false), this); + Created.RaiseEvent(new NewEventArgs(member, false, memberType.Alias, -1), this); return member; } - /// - /// Creates and persists a new Member - /// - /// - /// - /// - /// - /// - /// - public IMember CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true) - { - var uow = _uowProvider.GetUnitOfWork(); - IMemberType memberType; - - using (var repository = _repositoryFactory.CreateMemberTypeRepository(uow)) - { - var query = Query.Builder.Where(x => x.Alias == memberTypeAlias); - memberType = repository.GetByQuery(query).FirstOrDefault(); - } - - if (memberType == null) - { - throw new ArgumentException(string.Format("No MemberType matching the passed in Alias: '{0}' was found", memberTypeAlias)); - } - - return CreateMemberWithIdentity(username, email, password, memberType, raiseEvents); - } - /// /// Gets a Member by its Id /// @@ -708,42 +805,42 @@ namespace Umbraco.Core.Services Deleted.RaiseEvent(new DeleteEventArgs(member, false), this); } - + /// /// Saves an updated Member /// - /// + /// /// - public void Save(IMember member, bool raiseEvents = true) + public void Save(IMember entity, bool raiseEvents = true) { if (raiseEvents) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(entity), this)) { return; } - + } var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - repository.AddOrUpdate(member); + repository.AddOrUpdate(entity); uow.Commit(); - var xml = member.ToXml(); - CreateAndSaveMemberXml(xml, member.Id, uow.Database); + var xml = entity.ToXml(); + CreateAndSaveMemberXml(xml, entity.Id, uow.Database); } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(member, false), this); + Saved.RaiseEvent(new SaveEventArgs(entity, false), this); } - public void Save(IEnumerable members, bool raiseEvents = true) + public void Save(IEnumerable entities, bool raiseEvents = true) { if (raiseEvents) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(members), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(entities), this)) return; } using (new WriteLock(Locker)) @@ -751,7 +848,7 @@ namespace Umbraco.Core.Services var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - foreach (var member in members) + foreach (var member in entities) { repository.AddOrUpdate(member); } @@ -759,14 +856,14 @@ namespace Umbraco.Core.Services //commit the whole lot in one go uow.Commit(); - foreach (var member in members) + foreach (var member in entities) { CreateAndSaveMemberXml(member.ToXml(), member.Id, uow.Database); } } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(members, false), this); + Saved.RaiseEvent(new SaveEventArgs(entities, false), this); } } @@ -818,7 +915,7 @@ namespace Umbraco.Core.Services var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - return repository.GetByMemberGroup(roleName); + return repository.GetByMemberGroup(roleName); } } @@ -852,8 +949,8 @@ namespace Umbraco.Core.Services foreach (var memberGroup in found) { - _memberGroupService.Delete(memberGroup); - } + _memberGroupService.Delete(memberGroup); + } return found.Any(); } } @@ -893,9 +990,34 @@ namespace Umbraco.Core.Services { repository.DissociateRoles(memberIds, roleNames); } - } + } + + + #endregion + private IMemberType FindMemberTypeByAlias(string memberTypeAlias) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Alias == memberTypeAlias); + var types = repository.GetByQuery(query); + + if (types.Any() == false) + throw new Exception( + string.Format("No MemberType matching the passed in Alias: '{0}' was found", + memberTypeAlias)); + + var contentType = types.First(); + + if (contentType == null) + throw new Exception(string.Format("MemberType matching the passed in Alias: '{0}' was null", + memberTypeAlias)); + + return contentType; + } + } + /// /// Rebuilds all xml content in the cmsContentXml table for all media /// @@ -973,7 +1095,7 @@ namespace Umbraco.Core.Services //bulk insert it into the database uow.Database.BulkInsertRecords(xmlItems, tr); - tr.Complete(); + tr.Complete(); } } } @@ -986,7 +1108,7 @@ namespace Umbraco.Core.Services } #region Event Handlers - + /// /// Occurs before Delete /// @@ -1001,12 +1123,21 @@ namespace Umbraco.Core.Services /// Occurs before Save /// public static event TypedEventHandler> Saving; + + /// + /// Occurs after Create + /// + /// + /// Please note that the Member object has been created, but might not have been saved + /// so it does not have an identity yet (meaning no Id has been set). + /// + public static event TypedEventHandler> Created; /// /// Occurs after Save /// public static event TypedEventHandler> Saved; - + #endregion ///// @@ -1070,6 +1201,6 @@ namespace Umbraco.Core.Services // return new Member(name, email, username, password, -1, memType); //} - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index aa345266be..ba42548bac 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -58,7 +58,7 @@ namespace Umbraco.Core.Services IContent[] allVersions = null; int totalUsers; - var allUsers = _userService.GetAllMembers(0, int.MaxValue, out totalUsers); + var allUsers = _userService.GetAll(0, int.MaxValue, out totalUsers); foreach (var u in allUsers) { if (u.IsApproved == false) continue; diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 816865d807..64c03a6426 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -12,6 +12,8 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Packaging; +using Umbraco.Core.Packaging.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -341,7 +343,9 @@ namespace Umbraco.Core.Services foreach (var propertyType in contentType.PropertyTypes) { var definition = _dataTypeService.GetDataTypeDefinitionById(propertyType.DataTypeDefinitionId); - var propertyGroup = contentType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value); + var propertyGroup = propertyType.PropertyGroupId == null + ? null + : contentType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value); var genericProperty = new XElement("GenericProperty", new XElement("Name", propertyType.Name), new XElement("Alias", propertyType.Alias), @@ -698,7 +702,7 @@ namespace Umbraco.Core.Services { var prevalue = new XElement("PreValue"); prevalue.Add(new XAttribute("Id", pv.Value.Id)); - prevalue.Add(new XAttribute("Value", pv.Value.Value)); + prevalue.Add(new XAttribute("Value", pv.Value.Value == null ? "" : pv.Value.Value)); prevalue.Add(new XAttribute("Alias", pv.Key)); prevalue.Add(new XAttribute("SortOrder", sort)); prevalues.Add(prevalue); @@ -731,7 +735,7 @@ namespace Umbraco.Core.Services var dataTypes = new Dictionary(); var dataTypeElements = name.Equals("DataTypes") ? (from doc in element.Elements("DataType") select doc).ToList() - : new List { element.Element("DataType") }; + : new List { element }; foreach (var dataTypeElement in dataTypeElements) { @@ -1198,5 +1202,21 @@ namespace Umbraco.Core.Services #region Stylesheets #endregion + + #region Installation + + internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0) + { + //TODO Add events ? + //NOTE The PackageInstallation class should be passed as IPackageInstallation through the + //constructor (probably as an overload to avoid breaking stuff), so that its extendable. + var installer = new PackageInstallation(this, new PackageExtraction()); + return installer.InstallPackage(packageFilePath, userId); + } + + #endregion + + #region Package Building + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 98c6448f12..d2a58b6ad8 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Security; namespace Umbraco.Core.Services { @@ -75,7 +76,23 @@ namespace Umbraco.Core.Services } } - public IUser CreateMemberWithIdentity(string username, string email, string password, IUserType userType, bool raiseEvents = true) + public IUser CreateUserWithIdentity(string username, string email, IUserType userType) + { + return CreateUserWithIdentity(username, email, "", userType); + } + + IUser IMembershipMemberService.CreateWithIdentity(string username, string email, string rawPasswordValue, string memberTypeAlias) + { + var userType = GetUserTypeByAlias(memberTypeAlias); + if (userType == null) + { + throw new ArgumentException("The user type " + memberTypeAlias + " could not be resolved"); + } + + return CreateUserWithIdentity(username, email, rawPasswordValue, userType); + } + + private IUser CreateUserWithIdentity(string username, string email, string rawPasswordValue, IUserType userType) { if (userType == null) throw new ArgumentNullException("userType"); @@ -94,8 +111,7 @@ namespace Umbraco.Core.Services Email = email, Language = Configuration.GlobalSettings.DefaultUILanguage, Name = username, - Password = password, - DefaultPermissions = userType.Permissions, + RawPasswordValue = rawPasswordValue, Username = username, StartContentId = -1, StartMediaId = -1, @@ -103,33 +119,18 @@ namespace Umbraco.Core.Services IsApproved = true }; - if (raiseEvents) - { - if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(user), this)) - return user; - } + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(user), this)) + return user; repository.AddOrUpdate(user); uow.Commit(); - if (raiseEvents) - SavedUser.RaiseEvent(new SaveEventArgs(user, false), this); + SavedUser.RaiseEvent(new SaveEventArgs(user, false), this); return user; } } - public IUser CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true) - { - var userType = GetUserTypeByAlias(memberTypeAlias); - if (userType == null) - { - throw new ArgumentException("The user type " + memberTypeAlias + " could not be resolved"); - } - - return CreateMemberWithIdentity(username, email, password, userType); - } - public IUser GetById(int id) { using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) @@ -191,6 +192,37 @@ namespace Umbraco.Core.Services uow.Database.Execute("delete from umbracoUserLogins where userID = @id", new {id = membershipUser.Id}); } + /// + /// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method + /// + /// The user to save the password for + /// + /// + /// This method exists so that Umbraco developers can use one entry point to create/update users if they choose to. + /// + public void SavePassword(IUser user, string password) + { + if (user == null) throw new ArgumentNullException("user"); + + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) + { + provider.ChangePassword(user.Username, "", password); + } + + //go re-fetch the member and update the properties that may have changed + var result = GetByUsername(user.Username); + if (result != null) + { + //should never be null but it could have been deleted by another thread. + user.RawPasswordValue = result.RawPasswordValue; + user.LastPasswordChangeDate = result.LastPasswordChangeDate; + user.UpdateDate = user.UpdateDate; + } + + throw new NotSupportedException("When using a non-Umbraco membership provider you must change the user password by using the MembershipProvider.ChangePassword method"); + } + /// /// To permanently delete the user pass in true, otherwise they will just be disabled /// @@ -218,37 +250,37 @@ namespace Umbraco.Core.Services } } - public void Save(IUser membershipUser, bool raiseEvents = true) + public void Save(IUser entity, bool raiseEvents = true) { if (raiseEvents) { - if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(membershipUser), this)) + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(entity), this)) return; } var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) { - repository.AddOrUpdate(membershipUser); + repository.AddOrUpdate(entity); uow.Commit(); } if (raiseEvents) - SavedUser.RaiseEvent(new SaveEventArgs(membershipUser, false), this); + SavedUser.RaiseEvent(new SaveEventArgs(entity, false), this); } - public void Save(IEnumerable members, bool raiseEvents = true) + public void Save(IEnumerable entities, bool raiseEvents = true) { if (raiseEvents) { - if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(members), this)) + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(entities), this)) return; } var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) { - foreach (var member in members) + foreach (var member in entities) { repository.AddOrUpdate(member); } @@ -257,10 +289,10 @@ namespace Umbraco.Core.Services } if (raiseEvents) - SavedUser.RaiseEvent(new SaveEventArgs(members, false), this); + SavedUser.RaiseEvent(new SaveEventArgs(entities, false), this); } - public IEnumerable FindMembersByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + public IEnumerable FindByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) @@ -292,7 +324,7 @@ namespace Umbraco.Core.Services } } - public IEnumerable FindMembersByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + public IEnumerable FindByUsername(string login, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) @@ -324,7 +356,7 @@ namespace Umbraco.Core.Services } } - public int GetMemberCount(MemberCountType countType) + public int GetCount(MemberCountType countType) { using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) { @@ -360,7 +392,7 @@ namespace Umbraco.Core.Services } } - public IEnumerable GetAllMembers(int pageIndex, int pageSize, out int totalRecords) + public IEnumerable GetAll(int pageIndex, int pageSize, out int totalRecords) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) @@ -399,17 +431,17 @@ namespace Umbraco.Core.Services } /// - /// Assigns the same permission set for a single user to any number of entities + /// Replaces the same permission set for a single user to any number of entities /// /// /// /// - public void AssignUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) + public void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) { - repository.AssignUserPermissions(userId, permissions, entityIds); + repository.ReplaceUserPermissions(userId, permissions, entityIds); } } diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index d14dbc7c6b..ac5e8b0ec6 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -477,7 +477,7 @@ namespace Umbraco.Core public static bool InvariantContains(this IEnumerable compare, string compareTo) { - return compare.Contains(compareTo, new DelegateEqualityComparer((source, dest) => source.Equals(dest, StringComparison.InvariantCultureIgnoreCase), x => x.GetHashCode())); + return compare.Contains(compareTo, StringComparer.InvariantCultureIgnoreCase); } /// diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 07f262c272..508e356f71 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -115,6 +115,11 @@ namespace Umbraco.Core.Strings return InvalidFileNameChars.Contains(c) == false; } + public static string CutMaxLength(string text, int length) + { + return text.Length <= length ? text : text.Substring(0, length); + } + #endregion #region Configuration @@ -168,6 +173,7 @@ namespace Umbraco.Core.Strings return WithConfig(CleanStringType.UrlSegment, new Config { PreFilter = ApplyUrlReplaceCharacters, + PostFilter = x => CutMaxLength(x, 240), IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore StringType = (UrlReplacingToAscii ? CleanStringType.Ascii : CleanStringType.Utf8) | CleanStringType.LowerCase, BreakTermsOnUpper = false, @@ -202,6 +208,7 @@ namespace Umbraco.Core.Strings { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged; PreFilter = null; + PostFilter = null; IsTerm = (c, leading) => leading ? char.IsLetter(c) : char.IsLetterOrDigit(c); BreakTermsOnUpper = false; CutAcronymOnNonUpper = false; @@ -214,6 +221,7 @@ namespace Umbraco.Core.Strings return new Config { PreFilter = PreFilter, + PostFilter = PostFilter, IsTerm = IsTerm, StringType = StringType, BreakTermsOnUpper = BreakTermsOnUpper, @@ -224,6 +232,7 @@ namespace Umbraco.Core.Strings } public Func PreFilter { get; set; } + public Func PostFilter { get; set; } public Func IsTerm { get; set; } public CleanStringType StringType { get; set; } @@ -554,6 +563,10 @@ function validateSafeAlias(id, value, immediate, callback) {{ // clean text = CleanCodeString(text, stringType, separator.Value, culture, config); + // apply post-filter + if (config.PostFilter != null) + text = config.PostFilter(text); + return text; } diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs index 281901504f..d61ad60144 100644 --- a/src/Umbraco.Core/TypeExtensions.cs +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -7,11 +7,59 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using Umbraco.Core.Strings; namespace Umbraco.Core { public static class TypeExtensions { + /// + /// Tries to return a value based on a property name for an object but ignores case sensitivity + /// + /// + /// + /// + /// + /// + /// Currenty this will only work for ProperCase and camelCase properties, see the TODO below to enable complete case insensitivity + /// + internal static Attempt GetMemberIgnoreCase(this Type type, object target, string memberName) + { + Func> getMember = + memberAlias => + { + try + { + return Attempt.Succeed( + type.InvokeMember(memberAlias, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public, + null, + target, + null)); + } + catch (MissingMethodException ex) + { + return Attempt.Fail(ex); + } + }; + + //try with the current casing + var attempt = getMember(memberName); + if (attempt.Success == false) + { + //if we cannot get with the current alias, try changing it's case + attempt = memberName[0].IsUpperCase() + ? getMember(memberName.ToCleanString(CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.CamelCase)) + : getMember(memberName.ToCleanString(CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.PascalCase)); + + //TODO: If this still fails then we should get a list of properties from the object and then compare - doing the above without listing + // all properties will surely be faster than using reflection to get ALL properties first and then query against them. + } + + return attempt; + } public static object GetDefaultValue(this Type t) { diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index 36a5deff75..14795e146d 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -290,7 +290,15 @@ namespace Umbraco.Core "HtmlAgilityPack.", "TidyNet.", "ICSharpCode.", - "CookComputing." + "CookComputing.", + "AutoMapper,", + "HtmlAgilityPack,", + "itextsharp,", + "MiniProfiler,", + "Moq,", + "nunit.framework,", + "TidyNet,", + "WebDriver," }; /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 62f5ecfb73..20a60f7b2a 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -214,8 +214,20 @@ + + + + + + + + + + + + @@ -269,7 +281,7 @@ - + @@ -796,7 +808,7 @@ - + @@ -852,6 +864,9 @@ Constants.cs + + Constants.cs + Constants.cs @@ -914,9 +929,7 @@ - - - + - + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - - - - + + + + - - + + + @@ -29,12 +29,12 @@ 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 fd834a87d8..c7a494b36a 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 c34fd48031..b55b5324ba 100644 --- a/src/Umbraco.Web.UI/config/feedProxy.config +++ b/src/Umbraco.Web.UI/config/feedProxy.config @@ -1,9 +1,9 @@  - - + + - - - + + + 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/tinyMceConfig.config b/src/Umbraco.Web.UI/config/tinyMceConfig.config index 2c3f2484f6..579dc77ba1 100644 --- a/src/Umbraco.Web.UI/config/tinyMceConfig.config +++ b/src/Umbraco.Web.UI/config/tinyMceConfig.config @@ -2,228 +2,222 @@ - - - code - images/editor/code.gif - code - 1 - - - removeformat - images/editor/removeformat.gif - removeformat - 2 - + + + code + images/editor/code.gif + code + 1 + + + removeformat + images/editor/removeformat.gif + removeformat + 2 + - - Undo - images/editor/undo.gif - undo - 11 - - - Redo - images/editor/redo.gif - redo - 12 - - - Cut - images/editor/cut.gif - cut - 13 - - - Copy - images/editor/copy.gif - copy - 14 - - - mcePasteWord - images/editor/paste.gif - pasteword - 15 - + + Undo + images/editor/undo.gif + undo + 11 + + + Redo + images/editor/redo.gif + redo + 12 + + + Cut + images/editor/cut.gif + cut + 13 + + + Copy + images/editor/copy.gif + copy + 14 + + + mcePasteWord + images/editor/paste.gif + pasteword + 15 + - - stylePicker - images/editor/showStyles.png - umbracocss - 20 - - - bold - images/editor/bold.gif - bold - 21 - - - italic - images/editor/italic.gif - italic - 22 - - - Underline - images/editor/underline.gif - underline - 23 - - - Strikethrough - images/editor/strikethrough.gif - strikethrough - 24 - + + stylePicker + images/editor/showStyles.png + umbracocss + 20 + + + bold + images/editor/bold.gif + bold + 21 + + + italic + images/editor/italic.gif + italic + 22 + + + Underline + images/editor/underline.gif + underline + 23 + + + Strikethrough + images/editor/strikethrough.gif + strikethrough + 24 + - - JustifyLeft - images/editor/justifyleft.gif - justifyleft - 31 - - - JustifyCenter - images/editor/justifycenter.gif - justifycenter - 32 - - - JustifyRight - images/editor/justifyright.gif - justifyright - 33 - - - JustifyFull - images/editor/justifyfull.gif - justifyfull - 34 - + + JustifyLeft + images/editor/justifyleft.gif + justifyleft + 31 + + + JustifyCenter + images/editor/justifycenter.gif + justifycenter + 32 + + + JustifyRight + images/editor/justifyright.gif + justifyright + 33 + + + JustifyFull + images/editor/justifyfull.gif + justifyfull + 34 + - - bullist - images/editor/bullist.gif - bullist - 41 - - - numlist - images/editor/numlist.gif - numlist - 42 - - - Outdent - images/editor/outdent.gif - outdent - 43 - - - Indent - images/editor/indent.gif - indent - 44 - + + bullist + images/editor/bullist.gif + bullist + 41 + + + numlist + images/editor/numlist.gif + numlist + 42 + + + Outdent + images/editor/outdent.gif + outdent + 43 + + + Indent + images/editor/indent.gif + indent + 44 + - - mceLink - images/editor/link.gif - link - 51 - - - unlink - images/editor/unLink.gif - unlink - 52 - - - mceInsertAnchor - images/editor/anchor.gif - anchor - 53 - + + mceLink + images/editor/link.gif + link + 51 + + + unlink + images/editor/unLink.gif + unlink + 52 + + + mceInsertAnchor + images/editor/anchor.gif + anchor + 53 + - - mceImage - images/editor/image.gif - image - 61 - + + mceImage + images/editor/image.gif + image + 61 + + + umbracomacro + images/editor/insMacro.gif + umbracomacro + 62 + + + mceInsertTable + images/editor/table.gif + table + 63 + + + umbracoembed + images/editor/media.gif + umbracoembed + 66 + + + inserthorizontalrule + images/editor/hr.gif + hr + 71 + + + subscript + images/editor/sub.gif + sub + 72 + + + superscript + images/editor/sup.gif + sup + 73 + + + mceCharMap + images/editor/charmap.gif + charmap + 74 + + + mceSpellCheck + images/editor/spellchecker.gif + spellchecker + 75 + - - umbracomacro - images/editor/insMacro.gif - umbracomacro - 62 - - - mceInsertTable - images/editor/table.gif - table - 63 - - - umbracoembed - images/editor/media.gif - umbracoembed - 66 - - - inserthorizontalrule - images/editor/hr.gif - hr - 71 - - - subscript - images/editor/sub.gif - sub - 72 - - - - superscript - images/editor/sup.gif - sup - 73 - - - - mceCharMap - images/editor/charmap.gif - charmap - 74 - - - - mceSpellCheck - images/editor/spellchecker.gif - spellchecker - 75 - - - - paste - inlinepopups - noneditable - table - lists - umbracomacro - - advlink - umbracocss - umbracoembed - - umbracoimg - spellchecker - umbracoshortcut - - - + + paste + inlinepopups + noneditable + table + lists + umbracomacro + umbracoimg + advlink + umbracocss + umbracoembed + spellchecker + + + - - font + + font - - - - raw - GoogleSpellChecker.ashx - + + + + raw + GoogleSpellChecker.ashx + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 19d72fd252..6834440077 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -1,14 +1,14 @@  - - + + - - + + - - + + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 1758488f54..0dc21ae2fc 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -26,7 +26,7 @@ + should be set to false if the application pool's user account hasn't got readrights of the driveroot up to the /media directory --> True diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 6cced72c73..a089b97608 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -1,4 +1,4 @@ - + @@ -34,15 +34,11 @@ 1 200 --> - - 1047 - 1047 - 1047 - + - robot@umbraco.dk + your@email.here @@ -51,8 +47,15 @@ False - - Raw + + UTF8 + + + false + + + + true True @@ -61,7 +64,7 @@ True - True + False False @@ -69,24 +72,27 @@ text - false - - true - - true + + false + 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 - HideFileDuplicates - + ShowDuplicates + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd @@ -122,14 +128,18 @@ false - false + true - + + + + plus star @@ -137,15 +147,29 @@ ae oe aa + ae + oe + ue + ss + ae + oe + - + + + + true - true + + + Mvc + @@ -154,6 +178,29 @@ + + + + + p + div + ul + span + + + + + + + + - falses - 86400 - 1440 + + + @@ -183,22 +230,23 @@ 0 - - umb1.dev - localhost + + + - + - umbraco - umbraco + your-username + your-username + your-username + your-username + your-username + css,xslt - umbraco - umbraco - umbraco diff --git a/src/Umbraco.Web.UI/config/xsltExtensions.config b/src/Umbraco.Web.UI/config/xsltExtensions.config index 91012fe61f..dfd43863e0 100644 --- a/src/Umbraco.Web.UI/config/xsltExtensions.config +++ b/src/Umbraco.Web.UI/config/xsltExtensions.config @@ -1,12 +1,12 @@ - + - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/install/Default.aspx.cs b/src/Umbraco.Web.UI/install/Default.aspx.cs index 5ce92070a8..af37f0c76c 100644 --- a/src/Umbraco.Web.UI/install/Default.aspx.cs +++ b/src/Umbraco.Web.UI/install/Default.aspx.cs @@ -60,8 +60,9 @@ namespace Umbraco.Web.UI.Install _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) + // It's not considered an upgrade if the ConfigurationStatus is missing or empty or if the db is not configured (initially). + if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus) == false + && (IsPostBack == false && global::Umbraco.Core.ApplicationContext.Current.DatabaseContext.IsDatabaseConfigured)) { var result = Security.ValidateCurrentUser(new HttpContextWrapper(Context)); diff --git a/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs b/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs index 57772cfbdd..ce81ca3bce 100644 --- a/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs +++ b/src/Umbraco.Web.UI/install/steps/DefaultUser.ascx.cs @@ -19,15 +19,7 @@ namespace Umbraco.Web.UI.Install.Steps protected MembershipProvider CurrentProvider { - get - { - var provider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; - if (provider == null) - { - throw new InvalidOperationException("No MembershipProvider found with name " + UmbracoSettings.DefaultBackofficeProvider); - } - return provider; - } + get { return MembershipProviderExtensions.GetUsersMembershipProvider(); } } protected void ChangePasswordClick(object sender, EventArgs e) diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 2316496b38..8d99805c71 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -1,8 +1,8 @@  - + - + diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index c115fc3001..3f063c134a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -344,7 +344,7 @@
Macro
- /create/simple.ascx + /Create/PartialView.ascx @@ -354,8 +354,8 @@
Macro
/Create/PartialViewMacro.ascx - - + +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index de825d2d38..d7ce1437b2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -5,7 +5,7 @@ http://umbraco.org - Manage hostnames + Culture and Hostnames Audit Trail Browse Node Change Document Type @@ -24,6 +24,7 @@ Notifications Public access Publish + Unpublish Reload nodes Republish entire site Permissions @@ -36,19 +37,31 @@ Update + Permission denied. Add new Domain - Invalid hostname + remove + Invalid node. + Invalid domain format. + Domain has already been assigned. Domain + Language New domain '%0%' has been created Domain '%0%' is deleted Domain '%0%' has already been assigned - - https://www.example.com/, example.com/en, etc. Use * to match
- any domain and just set the culture.]]> +
One-level paths in domains are supported, eg. "example.com/en". However, they + should be avoided. Better use the culture setting above.]]>
Domain '%0%' has been updated Edit Current Domains + Inherit + Culture + + or inherit culture from parent nodes. Will also apply
+ to the current node, unless a domain below applies too.]]> +
+ Domains Viewing for @@ -83,9 +96,11 @@ To change the document type for the selected content, first select from the list of valid types for this location. Then confirm and/or amend the mapping of properties from the current type to the new, and click Save. + The content has been re-published. Current Property Current type The document type cannot be changed, as there are no alternatives valid for this location. + Document Type Changed Map Properties Map to Property New Template @@ -93,7 +108,8 @@ none Content Select New Document Type - The document type of the selected content has been successfully changed and the properties mapped. + The document type of the selected content has been successfully changed to [new type] and the following properties mapped: + to Could not complete property mapping as one or more properties have more than one mapping defined. Only alternate types valid for the current location are displayed. @@ -263,6 +279,7 @@ You cannot split a cell that hasn't been merged. Error in XSLT source The XSLT has not been saved, because it contained error(s) + There is a configuration error with the data type used for this property, please check the data type About @@ -271,7 +288,7 @@ Alias Are you sure? Border - or + by Cancel Cell margin Choose @@ -647,6 +664,7 @@ To manage your website, simply open the umbraco back office and start adding con Upgrade instructions There's an upgrade available for this package. You can download it directly from the umbraco package repository. Package version + Package version history View package website @@ -754,6 +772,7 @@ To manage your website, simply open the umbraco back office and start adding con Master Content Type enabled This Content Type uses as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself + No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. Sorting complete. @@ -809,6 +828,7 @@ To manage your website, simply open the umbraco back office and start adding con XSLT could not be saved, check file permissions XSLT saved No errors in XSLT + Content unpublished Partial view saved Partial view saved without any errors! Partial view not saved diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx index 7fc9bbf44c..3bb2fad469 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx @@ -49,7 +49,8 @@ - + + @@ -105,10 +106,26 @@ }); - jQuery(document).ready(function () { + $(document).ready(function () { + + $("table.tabs-table tr.propertyContent input.sort-order").keydown(function(e) { + // Allow: backspace, delete, tab, escape, enter and . + if ($.inArray(e.keyCode, [46, 8, 9, 27, 13, 190]) !== -1 || + // Allow: Ctrl+A + (e.keyCode == 65 && e.ctrlKey === true) || + // Allow: home, end, left, right + (e.keyCode >= 35 && e.keyCode <= 39)) { + // let it happen, don't do anything + return; + } + // Ensure that it is a number and stop the keypress + if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) { + e.preventDefault(); + } + }); // Make each tr of the tabs table sortable (prevent dragging of header row, and set up a callback for when row dragged) - jQuery("table.tabs-table tbody").sortable({ + $("table.tabs-table tbody").sortable({ containment: 'parent', cancel: '.propertyHeader, input', tolerance: 'pointer', @@ -119,8 +136,8 @@ // Fired after row dragged; go through each tr and save position to the hidden sort order field function saveOrder() { - jQuery("table.tabs-table tbody tr.propertyContent").each(function (index) { - jQuery("input.sort-order", this).val(index + 1); + $("table.tabs-table tbody tr.propertyContent").each(function (index) { + $("input.sort-order", this).val(index + 1); }); } diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs index c3459b752e..28250d9fa4 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using System.Web.UI.WebControls; namespace Umbraco.Web.UI.Umbraco.Controls { diff --git a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs index 582c67837d..c17c27becf 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs +++ b/src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.designer.cs @@ -11,5 +11,7 @@ namespace Umbraco.Web.UI.Umbraco.Controls { public partial class ContentTypeControlNew { + + } } diff --git a/src/Umbraco.Web.UI/umbraco/create/User.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/User.ascx.cs index ff69a0b44c..9e7af18387 100644 --- a/src/Umbraco.Web.UI/umbraco/create/User.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/User.ascx.cs @@ -28,7 +28,9 @@ namespace Umbraco.Web.UI.Umbraco.Create /// protected void LoginExistsCheck(object sender, ServerValidateEventArgs e) { - var user = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].GetUser(Login.Text.Replace(" ", "").ToLower(), false); + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); + + var user = provider.GetUser(Login.Text.Replace(" ", "").ToLower(), false); if (Login.Text != "" && user != null) e.IsValid = false; @@ -44,9 +46,11 @@ namespace Umbraco.Web.UI.Umbraco.Create /// protected void EmailExistsCheck(object sender, ServerValidateEventArgs e) { - var found = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].GetUserNameByEmail(Email.Text.ToLower()); + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); - if (Email.Text != "" && found.IsNullOrWhiteSpace() == false && Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].RequiresUniqueEmail) + var found = provider.GetUserNameByEmail(Email.Text.ToLower()); + + if (Email.Text != "" && found.IsNullOrWhiteSpace() == false && provider.RequiresUniqueEmail) e.IsValid = false; else e.IsValid = true; diff --git a/src/Umbraco.Web.UI/umbraco/create/simple.ascx b/src/Umbraco.Web.UI/umbraco/create/simple.ascx index a885ca9008..04f63ecaff 100644 --- a/src/Umbraco.Web.UI/umbraco/create/simple.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/simple.ascx @@ -1,11 +1,12 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="simple.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.simple" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> +<%@ Control Language="c#" AutoEventWireup="True" Inherits="umbraco.cms.presentation.create.controls.simple" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<%=umbraco.ui.Text("name")%>:*
+
diff --git a/src/Umbraco.Web.UI/umbraco/developer/DataTypes/editDatatype.aspx b/src/Umbraco.Web.UI/umbraco/developer/DataTypes/editDatatype.aspx index 86066a5c63..48d035061d 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/DataTypes/editDatatype.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/DataTypes/editDatatype.aspx @@ -1,4 +1,5 @@ <%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" Title="Edit data type" + ValidateRequest="false" CodeBehind="editDatatype.aspx.cs" AutoEventWireup="True" Inherits="umbraco.cms.presentation.developer.editDatatype" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> @@ -7,6 +8,7 @@ + diff --git a/src/Umbraco.Web.UI/umbraco/plugins/uGoLive/Dashboard.ascx b/src/Umbraco.Web.UI/umbraco/plugins/uGoLive/Dashboard.ascx index a5ee2d2ba5..2f55eda0ce 100644 --- a/src/Umbraco.Web.UI/umbraco/plugins/uGoLive/Dashboard.ascx +++ b/src/Umbraco.Web.UI/umbraco/plugins/uGoLive/Dashboard.ascx @@ -5,7 +5,6 @@ - diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml index b767d94901..f45160420d 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/EmptyTemplate.cshtml @@ -11,7 +11,5 @@ @Library.NodeById(1233) *@ - - @* The fun starts here *@ diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml index ce0aa17d38..db32eed518 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByDate.cshtml @@ -2,8 +2,7 @@
    - @*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 Model.Children.Where("Visible").OrderBy("CreateDate desc")) {
  • @page.Name
  • diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml index 57fd127205..c565234b4a 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByName.cshtml @@ -1,8 +1,7 @@ @inherits umbraco.MacroEngines.DynamicNodeContext -
      - @*OrderBy() takes the property to sort by*@ + @* OrderBy() takes the property to sort by *@ @foreach (var page in Model.Children.Where("Visible").OrderBy("Name")) {
    • @page.Name
    • diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml index 8383d9077e..46846b2d3f 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml @@ -5,7 +5,6 @@ Show:True Alias:propertyAlias Name:Property Alias Type:Textstring *@ - @{ @* Get the property alias we want to filter on from the macro parameter *@ @@ -13,7 +12,6 @@ var selection = Model.Children.Where("Visible").OrderBy(propertyAlias); } -
        @foreach (var page in selection) { diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml index 7bf279768b..d82a0823e0 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml @@ -7,9 +7,8 @@ Settings section). *@ - @{ - @*Build a query and return the visible items *@ + @* Build a query and return the visible items *@ var selection= Model.Textpages.Where("Visible"); @* @@ -18,8 +17,7 @@ *@ } - -@*Determine if there are any nodes in the selection, then render list *@ +@* Determine if there are any nodes in the selection, then render list *@ @if(selection.Any()){
          diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml index 7f63b3a98a..b27e6d5636 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml @@ -1,5 +1,3 @@ - - @inherits umbraco.MacroEngines.DynamicNodeContext @* @@ -7,7 +5,6 @@ Macro Parameters To Create, for this macro to work: Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent *@ - @if (Parameter.mediaId != null) { @* Get the media folder as a dynamic node *@ diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml index 7e2d252473..3c1d149fbb 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml @@ -12,7 +12,6 @@ var selection = Model.PropertyWithPicker; } - @* Lists each selected value from the picker as a link *@
            @foreach(var id in selection){ diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml index dd8be57d9b..0c10425cd9 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml @@ -6,9 +6,8 @@ the css class "current". *@ - @{ - @*Get the root of the website *@ + @* Get the root of the website *@ var root = Model.AncestorOrSelf(1); } diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml index d9d2ec35df..987011c08c 100644 --- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml @@ -1,38 +1,34 @@ @inherits umbraco.MacroEngines.DynamicNodeContext -@{ - @* Walk up the tree from the current page to get the root node *@ - var rootNode = Model.AncestorOrself(1); -} - -@*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(@Model.AncestorOrSelf())
            +@* Helper method to travers through all descendants *@ +@helper traverse(dynamic node) +{ - -@*Helper method to travers through all descendants*@ -@helper traverse(dynamic node){ - -@*If a MaxLevelForSitemap parameter is passed to the macro, otherwise default to 4 levels*@ -var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap); + @* If a MaxLevelForSitemap parameter is passed to the macro, otherwise default to 4 levels *@ + var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap); -@*Select visible children *@ -var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap); + @* 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 + @* 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) + @*Run the traverse helper again *@ + @traverse(item)
              • } -
              +
            } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx b/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx index 477812a50e..a32fbf32bf 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/editTemplate.aspx @@ -29,10 +29,10 @@ masterPageDropDown: $("#<%= MasterTemplate.ClientID %>"), treeSyncPath: '<%=TemplateTreeSyncPath%>', text: { - templateErrorHeader: "<%= umbraco.ui.Text("speechBubbles", "templateErrorHeader") %>", - templateSavedHeader: "<%= umbraco.ui.Text("speechBubbles", "templateSavedHeader") %>", - templateErrorText: "<%= umbraco.ui.Text("speechBubbles", "templateErrorText") %>", - templateSavedText: "<%= umbraco.ui.Text("speechBubbles", "templateSavedText") %>" + templateErrorHeader: "<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "templateErrorHeader")) %>", + templateSavedHeader: "<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "templateSavedHeader")) %>", + templateErrorText: "<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "templateErrorText")) %>", + templateSavedText: "<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "templateSavedText")) %>" } }); diff --git a/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx b/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx index d9ee1f6a9f..21fbcd45fe 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/scripts/editScript.aspx @@ -23,10 +23,10 @@ saveButton: $("#<%= ((Control)SaveButton).ClientID %>"), editorSourceElement: $('#<%= editorSource.ClientID %>'), text: { - fileErrorHeader: '<%= umbraco.ui.Text("speechBubbles", "fileErrorHeader") %>', - fileSavedHeader: '<%= umbraco.ui.Text("speechBubbles", "fileSavedHeader") %>', + fileErrorHeader: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "fileErrorHeader")) %>', + fileSavedHeader: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "fileSavedHeader")) %>', fileSavedText: '', - fileErrorText: '<%= umbraco.ui.Text("speechBubbles", "fileErrorText") %>', + fileErrorText: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "fileErrorText")) %>', } }); editor.init(); diff --git a/src/Umbraco.Web.UI/umbraco/settings/stylesheet/editstylesheet.aspx b/src/Umbraco.Web.UI/umbraco/settings/stylesheet/editstylesheet.aspx index 5ed8dd435e..810c8a1088 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/stylesheet/editstylesheet.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/stylesheet/editstylesheet.aspx @@ -18,9 +18,9 @@ saveButton: $("#<%= ((Control)SaveButton).ClientID %>"), editorSourceElement: $('#<%= editorSource.ClientID %>'), text: { - cssErrorHeader: '<%= umbraco.ui.Text("speechBubbles", "cssErrorHeader") %>', - cssSavedHeader: '<%= umbraco.ui.Text("speechBubbles", "cssSavedHeader") %>', - cssSavedText: '<%= umbraco.ui.Text("speechBubbles", "cssSavedText") %>', + cssErrorHeader: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "cssErrorHeader")) %>', + cssSavedHeader: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "cssSavedHeader")) %>', + cssSavedText: '<%= HttpUtility.JavaScriptStringEncode(umbraco.ui.Text("speechBubbles", "cssSavedText")) %>', cssErrorText: 'Please make sure that you have permissions set correctly', } }); diff --git a/src/Umbraco.Web.UI/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx b/src/Umbraco.Web.UI/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx index 1d0166285d..c554ab88b9 100644 --- a/src/Umbraco.Web.UI/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx +++ b/src/Umbraco.Web.UI/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx @@ -10,25 +10,25 @@
            - <%=umbraco.ui.Text("name", base.getUser())%>: + <%=umbraco.ui.Text("name", UmbracoUser)%>:
            - <%=umbraco.ui.Text("stylesheet", "nameHelp", base.getUser())%> + <%=umbraco.ui.Text("stylesheet", "nameHelp", UmbracoUser)%>
            - <%=umbraco.ui.Text("alias", base.getUser())%>: + <%=umbraco.ui.Text("alias", UmbracoUser)%>:
            - <%=umbraco.ui.Text("stylesheet", "aliasHelp", base.getUser())%> + <%=umbraco.ui.Text("stylesheet", "aliasHelp", UmbracoUser)%>
            - <%=umbraco.ui.Text("styles", base.getUser())%>: + <%=umbraco.ui.Text("styles", UmbracoUser)%>: @@ -38,7 +38,7 @@
            - <%=umbraco.ui.Text("preview", base.getUser())%>: + <%=umbraco.ui.Text("preview", UmbracoUser)%>:
            diff --git a/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js b/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js index 3761c316a9..ddd51059a7 100644 --- a/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js +++ b/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js @@ -44,10 +44,10 @@ submitSucces: function(t) { if (t != 'true') { - top.UmbSpeechBubble.ShowMessage('error', this._opts.text.fileErrorHeader, this._opts.text.fileErrorText); + top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.fileErrorHeader), unescape(this._opts.text.fileErrorText)); } else { - top.UmbSpeechBubble.ShowMessage('save', this._opts.text.fileSavedHeader, this._opts.text.fileSavedText); + top.UmbSpeechBubble.ShowMessage('save', unescape(this._opts.text.fileSavedHeader), unescape(this._opts.text.fileSavedText)); } @@ -60,7 +60,7 @@ }, submitFailure: function(t) { - top.UmbSpeechBubble.ShowMessage('error', this._opts.text.fileErrorHeader, this._opts.text.fileErrorText); + top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.fileErrorHeader), unescape(this._opts.text.fileErrorText)); } }); })(jQuery); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js b/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js index b68cc7e2ac..3fce5e3987 100644 --- a/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js +++ b/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js @@ -44,10 +44,10 @@ submitSucces: function(t) { if (t != 'true') { - top.UmbSpeechBubble.ShowMessage('error', this._opts.text.cssErrorHeader, this._opts.text.cssErrorText); + top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.cssErrorHeader), unescape(this._opts.text.cssErrorText)); } else { - top.UmbSpeechBubble.ShowMessage('save', this._opts.text.cssSavedHeader, this._opts.text.cssSavedText); + top.UmbSpeechBubble.ShowMessage('save', unescape(this._opts.text.cssSavedHeader), unescape(this._opts.text.cssSavedText)); } UmbClientMgr.mainTree().setActiveTreeType('stylesheets'); @@ -58,7 +58,7 @@ }, submitFailure: function(t) { - top.UmbSpeechBubble.ShowMessage('error', this._opts.text.cssErrorHeader, this._opts.text.cssErrorText); + top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.cssErrorHeader), unescape(this._opts.text.cssErrorText)); } }); })(jQuery); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/ui/default.css b/src/Umbraco.Web.UI/umbraco_client/ui/default.css index ecc1163290..576bd0d0ce 100644 --- a/src/Umbraco.Web.UI/umbraco_client/ui/default.css +++ b/src/Umbraco.Web.UI/umbraco_client/ui/default.css @@ -743,7 +743,12 @@ table.tabs-table tr.propertyContent td } table.tabs-table input[type=text] { width: 200px; } table.tabs-table input[type=submit] { text-align: right; } -table.tabs-table tr.propertyContent input.sort-order {display: none;} + +table.tabs-table tr.propertyContent input.sort-order { + width: 20px; + background-color: lightgray; + padding: 0px 5px 0px 5px; +} li.no-properties-on-tab {background: none; background-color: #fff; cursor: default; } diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config index 1ba153e45e..5c5a33545d 100644 --- a/src/Umbraco.Web.UI/web.Template.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.Debug.config @@ -25,7 +25,16 @@ - + + + + + + + + diff --git a/src/Umbraco.Web.UI/web.Template.Release.config b/src/Umbraco.Web.UI/web.Template.Release.config index 3a244ea9f6..388480b0c3 100644 --- a/src/Umbraco.Web.UI/web.Template.Release.config +++ b/src/Umbraco.Web.UI/web.Template.Release.config @@ -23,6 +23,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs index a9e52fbb2f..8b742b1b2c 100644 --- a/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs @@ -27,16 +27,19 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey); + base.RefreshAll(); } public override void Refresh(int id) { Remove(id); + base.Refresh(id); } public override void Remove(int id) { ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey); + base.Remove(id); } } diff --git a/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs b/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs index ba2306bfe4..e267b441a2 100644 --- a/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs @@ -27,16 +27,19 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey); + base.RefreshAll(); } public override void Refresh(int id) { Remove(id); + base.Refresh(id); } public override void Remove(int id) { ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey); + base.Remove(id); } } diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index a9b01fb6e5..469463d7a2 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -131,19 +131,40 @@ namespace Umbraco.Web.Cache MediaService.Moving += MediaServiceMoving; MediaService.Trashing += MediaServiceTrashing; - ContentService.Created += ContentServiceCreated; + //Bind to content events - this is for unpublished content syncing across servers (primarily for examine) + + ContentService.Saved += ContentServiceSaved; + ContentService.Deleted += ContentServiceDeleted; ContentService.Copied += ContentServiceCopied; + //NOTE: we do not listen for the trashed event because there is no cache to update for content in that case since + // the unpublishing event handles that, and for examine with unpublished content indexes, we want to keep that data + // in the index, it's not until it's complete deleted that we want to remove it. + + //public access events + Access.AfterSave += Access_AfterSave; } + #region Public access event handlers + + static void Access_AfterSave(Access sender, SaveEventArgs e) + { + DistributedCache.Instance.RefreshPublicAccess(); + } + + #endregion + #region Content service event handlers /// - /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the - /// case then we need to clear all user permissions cache. + /// Handles cache refreshgi for when content is copied /// /// /// - static void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e) + /// + /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the + /// case then we need to clear all user permissions cache. + /// + static void ContentServiceCopied(IContentService sender, CopyEventArgs e) { //check if permissions have changed var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged"); @@ -151,23 +172,63 @@ namespace Umbraco.Web.Cache { DistributedCache.Instance.RefreshAllUserPermissionsCache(); } + + //run the un-published cache refresher + DistributedCache.Instance.RefreshUnpublishedPageCache(e.Copy); } /// - /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the - /// case then we need to clear all user permissions cache. + /// Handles cache refreshing for when content is deleted (not unpublished) /// /// /// - static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e) + static void ContentServiceDeleted(IContentService sender, DeleteEventArgs e) { - //check if permissions have changed - var permissionsChanged = ((Content)e.Entity).WasPropertyDirty("PermissionsChanged"); - if (permissionsChanged) + DistributedCache.Instance.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray()); + } + + /// + /// Handles cache refreshing for when content is saved (not published) + /// + /// + /// + /// + /// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to + /// stay up-to-date for unpublished content. + /// + /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the + /// case then we need to clear all user permissions cache. + /// + static void ContentServiceSaved(IContentService sender, SaveEventArgs e) + { + var clearUserPermissions = false; + e.SavedEntities.ForEach(x => + { + //check if it is new + if (x.IsNewEntity()) + { + //check if permissions have changed + var permissionsChanged = ((Content)x).WasPropertyDirty("PermissionsChanged"); + if (permissionsChanged) + { + clearUserPermissions = true; + } + } + }); + + if (clearUserPermissions) { DistributedCache.Instance.RefreshAllUserPermissionsCache(); } - } + + //filter out the entities that have only been saved (not newly published) since + // newly published ones will be synced with the published page cache refresher + var unpublished = e.SavedEntities.Where(x => x.JustPublished() == false); + //run the un-published cache refresher + DistributedCache.Instance.RefreshUnpublishedPageCache(unpublished.ToArray()); + } + + #endregion #region ApplicationTree event handlers @@ -451,12 +512,12 @@ namespace Umbraco.Web.Cache InvalidateCacheForPermissionsChange(sender); } - void UserServiceSavedUser(IUserService sender, Core.Events.SaveEventArgs e) + static void UserServiceSavedUser(IUserService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserCache(x.Id)); } - void UserServiceDeletedUser(IUserService sender, Core.Events.DeleteEventArgs e) + static void UserServiceDeletedUser(IUserService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id)); } @@ -545,22 +606,22 @@ namespace Umbraco.Web.Cache #endregion #region Media event handlers - static void MediaServiceTrashing(IMediaService sender, Core.Events.MoveEventArgs e) + static void MediaServiceTrashing(IMediaService sender, MoveEventArgs e) { - DistributedCache.Instance.RemoveMediaCache(e.Entity); + DistributedCache.Instance.RemoveMediaCache(false, e.Entity); } - static void MediaServiceMoving(IMediaService sender, Core.Events.MoveEventArgs e) + static void MediaServiceMoving(IMediaService sender, MoveEventArgs e) { DistributedCache.Instance.RefreshMediaCache(e.Entity); } - static void MediaServiceDeleting(IMediaService sender, Core.Events.DeleteEventArgs e) + static void MediaServiceDeleting(IMediaService sender, DeleteEventArgs e) { - DistributedCache.Instance.RemoveMediaCache(e.DeletedEntities.ToArray()); + DistributedCache.Instance.RemoveMediaCache(true, e.DeletedEntities.ToArray()); } - static void MediaServiceSaved(IMediaService sender, Core.Events.SaveEventArgs e) + static void MediaServiceSaved(IMediaService sender, SaveEventArgs e) { DistributedCache.Instance.RefreshMediaCache(e.SavedEntities.ToArray()); } @@ -568,27 +629,21 @@ namespace Umbraco.Web.Cache #region Member event handlers - static void MemberServiceDeleted(IMemberService sender, Core.Events.DeleteEventArgs e) + static void MemberServiceDeleted(IMemberService sender, DeleteEventArgs e) { - foreach (var m in e.DeletedEntities.ToArray()) - { - DistributedCache.Instance.RemoveMemberCache(m.Id); - } + DistributedCache.Instance.RemoveMemberCache(e.DeletedEntities.ToArray()); } - static void MemberServiceSaved(IMemberService sender, Core.Events.SaveEventArgs e) + static void MemberServiceSaved(IMemberService sender, SaveEventArgs e) { - foreach (var m in e.SavedEntities.ToArray()) - { - DistributedCache.Instance.RefreshMemberCache(m.Id); - } + DistributedCache.Instance.RefreshMemberCache(e.SavedEntities.ToArray()); } #endregion #region Member group event handlers - static void MemberGroupService_Deleted(IMemberGroupService sender, Core.Events.DeleteEventArgs e) + static void MemberGroupService_Deleted(IMemberGroupService sender, DeleteEventArgs e) { foreach (var m in e.DeletedEntities.ToArray()) { @@ -596,7 +651,7 @@ namespace Umbraco.Web.Cache } } - static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e) + static void MemberGroupService_Saved(IMemberGroupService sender, SaveEventArgs e) { foreach (var m in e.SavedEntities.ToArray()) { diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs index b66dd2174a..c993129859 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Web/Cache/DistributedCache.cs @@ -39,6 +39,7 @@ namespace Umbraco.Web.Cache public const string ApplicationCacheRefresherId = "B15F34A1-BC1D-4F8B-8369-3222728AB4C8"; public const string TemplateRefresherId = "DD12B6A0-14B9-46e8-8800-C154F74047C8"; public const string PageCacheRefresherId = "27AB3022-3DFA-47b6-9119-5945BC88FD66"; + public const string UnpublishedPageCacheRefresherId = "55698352-DFC5-4DBE-96BD-A4A0F6F77145"; public const string MemberCacheRefresherId = "E285DF34-ACDC-4226-AE32-C0CB5CF388DA"; public const string MemberGroupCacheRefresherId = "187F236B-BD21-4C85-8A7C-29FBA3D6C00C"; public const string MediaCacheRefresherId = "B29286DD-2D40-4DDB-B325-681226589FEC"; @@ -53,6 +54,7 @@ namespace Umbraco.Web.Cache public const string StylesheetPropertyCacheRefresherId = "2BC7A3A4-6EB1-4FBC-BAA3-C9E7B6D36D38"; public const string DataTypeCacheRefresherId = "35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2"; public const string DictionaryCacheRefresherId = "D1D7E227-F817-4816-BFE9-6C39B6152884"; + public const string PublicAccessCacheRefresherId = "1DB08769-B104-4F8B-850E-169CAC1DF2EC"; #endregion @@ -220,4 +222,4 @@ namespace Umbraco.Web.Cache } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index faede0bffc..7263776ae5 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -13,6 +13,17 @@ namespace Umbraco.Web.Cache /// internal static class DistributedCacheExtensions { + #region Public access + + public static void RefreshPublicAccess(this DistributedCache dc) + { + dc.RefreshByJson(new Guid(DistributedCache.PublicAccessCacheRefresherId), + PublicAccessCacheRefresher.SerializeToJsonPayload( + Access.GetXmlDocumentCopy())); + } + + #endregion + #region Application tree cache public static void RefreshAllApplicationTreeCache(this DistributedCache dc) { @@ -123,8 +134,7 @@ namespace Umbraco.Web.Cache } #endregion - - + #region Data type cache /// /// Refreshes the cache amongst servers for a data type @@ -232,15 +242,58 @@ namespace Umbraco.Web.Cache public static void RemovePageCache(this DistributedCache dc, int documentId) { dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), documentId); - } + } + + /// + /// invokes the unpublished page cache refresher + /// + /// + /// + public static void RefreshUnpublishedPageCache(this DistributedCache dc, params IContent[] content) + { + dc.Refresh(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content); + } + + /// + /// invokes the unpublished page cache refresher + /// + /// + /// + public static void RemoveUnpublishedPageCache(this DistributedCache dc, params IContent[] content) + { + dc.Remove(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content); + } + #endregion #region Member cache + + /// + /// Refreshes the cache among servers for a member + /// + /// + /// + public static void RefreshMemberCache(this DistributedCache dc, params IMember[] members) + { + dc.Refresh(new Guid(DistributedCache.MemberCacheRefresherId), x => x.Id, members); + } + + /// + /// Removes the cache among servers for a member + /// + /// + /// + public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members) + { + dc.Remove(new Guid(DistributedCache.MemberCacheRefresherId), x => x.Id, members); + } + /// /// Refreshes the cache among servers for a member /// /// /// + [Obsolete("Use the RefreshMemberCache with strongly typed IMember objects instead")] public static void RefreshMemberCache(this DistributedCache dc, int memberId) { dc.Refresh(new Guid(DistributedCache.MemberCacheRefresherId), memberId); @@ -251,6 +304,7 @@ namespace Umbraco.Web.Cache /// /// /// + [Obsolete("Use the RemoveMemberCache with strongly typed IMember objects instead")] public static void RemoveMemberCache(this DistributedCache dc, int memberId) { dc.Remove(new Guid(DistributedCache.MemberCacheRefresherId), memberId); @@ -291,7 +345,7 @@ namespace Umbraco.Web.Cache public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media) { dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId), - MediaCacheRefresher.SerializeToJsonPayload(media)); + MediaCacheRefresher.SerializeToJsonPayload(MediaCacheRefresher.OperationType.Saved, media)); } /// @@ -304,6 +358,7 @@ namespace Umbraco.Web.Cache /// to clear all of the cache but the media item will be removed before the other servers can /// look it up. Only here for legacy purposes. /// + [Obsolete("Ensure to clear with other RemoveMediaCache overload")] public static void RemoveMediaCache(this DistributedCache dc, int mediaId) { dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId); @@ -313,11 +368,14 @@ namespace Umbraco.Web.Cache /// Removes the cache amongst servers for media items /// /// + /// /// - public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media) + public static void RemoveMediaCache(this DistributedCache dc, bool isPermanentlyDeleted, params IMedia[] media) { - dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId), - MediaCacheRefresher.SerializeToJsonPayload(media)); + dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId), + MediaCacheRefresher.SerializeToJsonPayload( + isPermanentlyDeleted ? MediaCacheRefresher.OperationType.Deleted : MediaCacheRefresher.OperationType.Trashed, + media)); } #endregion diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs index 53abacc7a5..a2de17626f 100644 --- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Web.Script.Serialization; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.IO; using Umbraco.Core.Models; using umbraco.interfaces; using System.Linq; namespace Umbraco.Web.Cache { - /// /// A cache refresher to ensure media cache is updated /// @@ -19,13 +20,13 @@ namespace Umbraco.Web.Cache public class MediaCacheRefresher : JsonCacheRefresherBase { #region Static helpers - + /// /// Converts the json to a JsonPayload object /// /// /// - private static JsonPayload[] DeserializeFromJsonPayload(string json) + internal static JsonPayload[] DeserializeFromJsonPayload(string json) { var serializer = new JavaScriptSerializer(); var jsonObject = serializer.Deserialize(json); @@ -35,12 +36,13 @@ namespace Umbraco.Web.Cache /// /// Creates the custom Json payload used to refresh cache amongst the servers /// + /// /// /// - internal static string SerializeToJsonPayload(params IMedia[] media) + internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media) { var serializer = new JavaScriptSerializer(); - var items = media.Select(FromMedia).ToArray(); + var items = media.Select(x => FromMedia(x, operation)).ToArray(); var json = serializer.Serialize(items); return json; } @@ -49,15 +51,17 @@ namespace Umbraco.Web.Cache /// Converts a macro to a jsonPayload object /// /// + /// /// - private static JsonPayload FromMedia(IMedia media) + internal static JsonPayload FromMedia(IMedia media, OperationType operation) { if (media == null) return null; var payload = new JsonPayload { Id = media.Id, - Path = media.Path + Path = media.Path, + Operation = operation }; return payload; } @@ -66,10 +70,18 @@ namespace Umbraco.Web.Cache #region Sub classes - private class JsonPayload + internal enum OperationType + { + Saved, + Trashed, + Deleted + } + + internal class JsonPayload { public string Path { get; set; } public int Id { get; set; } + public OperationType Operation { get; set; } } #endregion @@ -97,13 +109,15 @@ namespace Umbraco.Web.Cache public override void Refresh(int id) { - ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id))); + ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), OperationType.Saved)); base.Refresh(id); } public override void Remove(int id) - { - ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id))); + { + ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), + //NOTE: we'll just default to trashed for this one. + OperationType.Trashed)); base.Remove(id); } @@ -121,7 +135,7 @@ namespace Umbraco.Web.Cache string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart)); // Also clear calls that only query this specific item! - if (idPart == payload.Id.ToString()) + if (idPart == payload.Id.ToString(CultureInfo.InvariantCulture)) ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch( string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id)); diff --git a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs b/src/Umbraco.Web/Cache/MemberCacheRefresher.cs index 2b740e826d..97afd092a7 100644 --- a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MemberCacheRefresher.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Cache /// /// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it. /// - public class MemberCacheRefresher : CacheRefresherBase + public class MemberCacheRefresher : TypedCacheRefresherBase { protected override MemberCacheRefresher Instance @@ -44,6 +44,18 @@ namespace Umbraco.Web.Cache base.Remove(id); } + public override void Refresh(IMember instance) + { + ClearCache(instance.Id); + base.Refresh(instance); + } + + public override void Remove(IMember instance) + { + ClearCache(instance.Id); + base.Remove(instance); + } + private void ClearCache(int id) { ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); diff --git a/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs new file mode 100644 index 0000000000..0c700e9dfa --- /dev/null +++ b/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs @@ -0,0 +1,77 @@ +using System; +using System.Xml; +using Newtonsoft.Json; +using umbraco.cms.businesslogic.web; +using Umbraco.Core; +using Umbraco.Core.Cache; + +namespace Umbraco.Web.Cache +{ + public sealed class PublicAccessCacheRefresher : JsonCacheRefresherBase + { + #region Static helpers + + internal static JsonPayload DeserializeFromJsonPayload(string json) + { + return JsonConvert.DeserializeObject(json); + } + + internal static string SerializeToJsonPayload(XmlDocument doc) + { + return JsonConvert.SerializeObject(FromXml(doc)); + } + + internal static JsonPayload FromXml(XmlDocument doc) + { + if (doc == null) return null; + + var payload = new JsonPayload + { + XmlContent = doc.OuterXml + }; + return payload; + } + + #endregion + + #region Sub classes + + internal class JsonPayload + { + public string XmlContent { get; set; } + } + + #endregion + + protected override PublicAccessCacheRefresher Instance + { + get { return this; } + } + + public override Guid UniqueIdentifier + { + get { return new Guid(DistributedCache.PublicAccessCacheRefresherId); } + } + + public override string Name + { + get { return "Public access cache refresher"; } + } + + public override void Refresh(string jsonPayload) + { + if (jsonPayload.IsNullOrWhiteSpace()) return; + var deserialized = DeserializeFromJsonPayload(jsonPayload); + if (deserialized == null) return; + var xDoc = new XmlDocument(); + xDoc.LoadXml(deserialized.XmlContent); + ClearCache(xDoc); + base.Refresh(jsonPayload); + } + + private void ClearCache(XmlDocument xDoc) + { + Access.UpdateInMemoryDocument(xDoc); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs new file mode 100644 index 0000000000..4413a61e9b --- /dev/null +++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs @@ -0,0 +1,33 @@ +using System; +using Umbraco.Core.Cache; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Cache +{ + /// + /// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update + /// + public sealed class UnpublishedPageCacheRefresher : TypedCacheRefresherBase + { + protected override UnpublishedPageCacheRefresher Instance + { + get { return this; } + } + + public override Guid UniqueIdentifier + { + get { return new Guid(DistributedCache.UnpublishedPageCacheRefresherId); } + } + + public override string Name + { + get { return "Unpublished Page Refresher"; } + } + + //NOTE: There is no functionality for this cache refresher, it is here simply to emit events on each server for which examine + // binds to. We could put the Examine index functionality in here but we've kept it all in the ExamineEvents class so that all of + // the logic is in one place. In the future we may put the examine logic in a cache refresher instead (that would make sense) but we'd + // want to get this done before making more cache refreshers: + // http://issues.umbraco.org/issue/U4-2633 + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs index dda11ee2c4..deb36ae894 100644 --- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs @@ -32,11 +32,13 @@ namespace Umbraco.Web.Cache RuntimeCacheProvider.Current.Clear(typeof(IUser)); ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey); + base.RefreshAll(); } public override void Refresh(int id) { Remove(id); + base.Refresh(id); } public override void Remove(int id) @@ -48,6 +50,8 @@ namespace Umbraco.Web.Cache //we need to clear all UserContextCacheKey since we cannot invalidate based on ID since the cache is done so based //on the current contextId stored in the database ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey); + + base.Remove(id); } } diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs index fdafdedb09..e61ea0d7e0 100644 --- a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs @@ -31,17 +31,20 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { - ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); + ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey); + base.RefreshAll(); } public override void Refresh(int id) { Remove(id); + base.Refresh(id); } public override void Remove(int id) { ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + base.Remove(id); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs index 7c4628dbe3..1a726da815 100644 --- a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs @@ -29,16 +29,19 @@ namespace Umbraco.Web.Cache public override void RefreshAll() { RuntimeCacheProvider.Current.Clear(typeof (IUserType)); + base.RefreshAll(); } public override void Refresh(int id) { RuntimeCacheProvider.Current.Delete(typeof(IUserType), id); + base.Refresh(id); } public override void Remove(int id) { RuntimeCacheProvider.Current.Delete(typeof(IUserType), id); + base.Remove(id); } } diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 33d657cc3f..0d4ad8aa4a 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -32,9 +32,9 @@ namespace Umbraco.Web.Controllers } //redirect to current page by default - TempData.Add("LoginSuccess", true); - //return RedirectToCurrentUmbracoPage(); - return RedirectToCurrentUmbracoUrl(); + TempData["LoginSuccess"] = true; + return RedirectToCurrentUmbracoPage(); + //return RedirectToCurrentUmbracoUrl(); } } } diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs index a4081413c3..e1d9a7aab1 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.Controllers } //redirect to current page by default - TempData.Add("LogoutSuccess", true); + TempData["LogoutSuccess"] = true; return RedirectToCurrentUmbracoPage(); } } diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs index 03c5ee91ff..dfcf17a543 100644 --- a/src/Umbraco.Web/Controllers/UmbProfileController.cs +++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs @@ -16,7 +16,8 @@ namespace Umbraco.Web.Controllers [HttpPost] public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model) { - if (Membership.Provider.IsUmbracoMembershipProvider() == false) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("Profile editing with the " + typeof(UmbProfileController) + " is not supported when not using the default Umbraco membership provider"); } @@ -41,7 +42,7 @@ namespace Umbraco.Web.Controllers } //redirect to current page by default - TempData.Add("ProfileUpdateSuccess", true); + TempData["ProfileUpdateSuccess"] = true; return RedirectToCurrentUmbracoPage(); } } diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs index c661c24f69..dea1135131 100644 --- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Controllers return Redirect(model.RedirectUrl); } //redirect to current page by default - TempData.Add("FormSuccess", true); + TempData["FormSuccess"] = true; return RedirectToCurrentUmbracoPage(); case MembershipCreateStatus.InvalidUserName: ModelState.AddModelError((model.UsernameIsEmail || model.Username == null) diff --git a/src/Umbraco.Web/Install/Steps/Database.cs b/src/Umbraco.Web/Install/Steps/Database.cs index ff3363947a..a20c07a122 100644 --- a/src/Umbraco.Web/Install/Steps/Database.cs +++ b/src/Umbraco.Web/Install/Steps/Database.cs @@ -1,8 +1,9 @@ using System; +using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.IO; -using Umbraco.Web.Install; +using Umbraco.Core.Logging; namespace Umbraco.Web.Install.Steps { @@ -32,22 +33,39 @@ namespace Umbraco.Web.Install.Steps //here we determine if the installer should skip this step... public override bool Completed() { + // Fresh installs don't have a version number so this step cannot be complete yet - if (string.IsNullOrEmpty(Umbraco.Core.Configuration.GlobalSettings.ConfigurationStatus)) + if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)) { //Even though the ConfigurationStatus is blank we try to determine the version if we can connect to the database var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema(); var determinedVersion = result.DetermineInstalledVersion(); if (determinedVersion.Equals(new Version(0, 0, 0))) - return false; + { + // Now that we know this is a brand new Umbraco install, turn membership providers' "useLegacyEncoding" off + DisableMembershipProviderLegacyEncoding(); + return false; + } return UmbracoVersion.Current < determinedVersion; } - var configuredVersion = new Version(Umbraco.Core.Configuration.GlobalSettings.ConfigurationStatus); + var configuredVersion = new Version(GlobalSettings.ConfigurationStatus); var targetVersion = UmbracoVersion.Current; return targetVersion < configuredVersion; } + + private static void DisableMembershipProviderLegacyEncoding() + { + if (GlobalSettings.UmbracoMembershipProviderLegacyEncoding) + GlobalSettings.UmbracoMembershipProviderLegacyEncoding = false; + + if (GlobalSettings.UmbracoUsersMembershipProviderLegacyEncoding) + GlobalSettings.UmbracoUsersMembershipProviderLegacyEncoding = false; + + + LogHelper.Info("Updated Umbraco membership providers, set useLegacyEncoding to false."); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs index ea0b12b97b..eb476f50a0 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContent.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -329,37 +329,9 @@ namespace Umbraco.Web.Models // can handle properties only when using the proper casing. So what this // does is ensure that any casing is supported. - Func> getMember = - memberAlias => - { - try - { - return Attempt.Succeed( - content.GetType().InvokeMember(memberAlias, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public, - null, - content, - null)); - } - catch (MissingMethodException ex) - { - return Attempt.Fail(ex); - } - }; + var attempt = content.GetType().GetMemberIgnoreCase(content, alias); - //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.ConvertCase | CleanStringType.CamelCase)) - : getMember(alias.ToCleanString(CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.PascalCase)); - } - - return !attempt.Success + return attempt.Success == false || attempt.Result == null ? null : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty); } diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs index 7ce1593c4b..ee1ea71ac4 100644 --- a/src/Umbraco.Web/Models/ProfileModel.cs +++ b/src/Umbraco.Web/Models/ProfileModel.cs @@ -4,14 +4,25 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; +using System.Web.Mvc; +using System.Xml; +using System.Xml.Linq; using umbraco.cms.businesslogic.member; using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; namespace Umbraco.Web.Models { + /// + /// A readonly member profile model + /// + [ModelBinder(typeof(ProfileModelBinder))] public class ProfileModel : PostRedirectModel { + public static ProfileModel CreateModel() { var model = new ProfileModel(false); @@ -24,7 +35,7 @@ namespace Umbraco.Web.Models if (doLookup) { var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current)); - var model = helper.CreateProfileModel(); + var model = helper.GetCurrentMemberProfileModel(); MemberProperties = model.MemberProperties; } } @@ -49,7 +60,6 @@ namespace Umbraco.Web.Models /// The member's member type alias /// [ReadOnly(true)] - [Obsolete("This is not used and will be removed from the codebase in future versions")] public string MemberTypeAlias { get; set; } [ReadOnly(true)] @@ -90,5 +100,16 @@ namespace Umbraco.Web.Models /// public List MemberProperties { get; set; } + /// + /// A custom model binder for MVC because the default ctor performs a lookup! + /// + internal class ProfileModelBinder : DefaultModelBinder + { + protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) + { + return ProfileModel.CreateModel(); + } + + } } } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 3db343c8db..2ca1978c29 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -50,7 +50,7 @@ namespace Umbraco.Web.Models _url = prop.Value.ToString(); break; default: - throw new ArgumentOutOfRangeException(); + throw new NotSupportedException(); } return _url; diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index a65e7a990b..ad26aca17f 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -4,12 +4,14 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; +using System.Web.Mvc; using umbraco.cms.businesslogic.member; using Umbraco.Core; using Umbraco.Web.Security; namespace Umbraco.Web.Models { + [ModelBinder(typeof(RegisterModelBinder))] public class RegisterModel : PostRedirectModel { /// @@ -58,6 +60,7 @@ namespace Umbraco.Web.Models /// /// The member type alias to use to register the member /// + [Editable(false)] public string MemberTypeAlias { get; set; } /// @@ -90,5 +93,16 @@ namespace Umbraco.Web.Models /// public bool LoginOnSuccess { get; set; } + /// + /// A custom model binder for MVC because the default ctor performs a lookup! + /// + internal class RegisterModelBinder : DefaultModelBinder + { + protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) + { + return RegisterModel.CreateModel(); + } + } + } } diff --git a/src/Umbraco.Web/Models/UmbracoProperty.cs b/src/Umbraco.Web/Models/UmbracoProperty.cs index ad907df1a0..93f0238e9f 100644 --- a/src/Umbraco.Web/Models/UmbracoProperty.cs +++ b/src/Umbraco.Web/Models/UmbracoProperty.cs @@ -1,12 +1,40 @@ -namespace Umbraco.Web.Models +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Xml; +using Umbraco.Core; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models { /// /// A simple representation of an Umbraco property /// public class UmbracoProperty { + [Editable(false)] public string Alias { get; set; } + + //NOTE: This has to be a string currently, if it is an object it will bind as an array which we don't want. + // If we want to have this as an 'object' with a true type on it, we have to create a custom model binder + // for an UmbracoProperty and then bind with the correct type based on the property type for this alias. This + // would be a bit long winded and perhaps unnecessary. The reason is because it is always posted as a string anyways + // and when we set this value on the property object that gets sent to the database we do a TryConvertTo to the + // real type anyways. + + [DataType(DataType.Text)] public string Value { get; set; } + + [ReadOnly(true)] public string Name { get; set; } + + //TODO: Perhaps one day we'll ship with our own EditorTempates but for now developers can just render their own inside the view + + ///// + ///// This can dynamically be set to a custom template name to change + ///// the editor type for this property + ///// + //[ReadOnly(true)] + //public string EditorTemplate { get; set; } + } } diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs index 087f124c6a..03e31f34c4 100644 --- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerExtensions.cs @@ -7,6 +7,22 @@ namespace Umbraco.Web.Mvc { internal static class ControllerExtensions { + internal static object GetDataTokenInViewContextHierarchy(this ControllerContext controllerContext, string dataTokenName) + { + if (controllerContext.RouteData.DataTokens.ContainsKey(dataTokenName)) + { + return controllerContext.RouteData.DataTokens[dataTokenName]; + } + + if (controllerContext.ParentActionViewContext != null) + { + //recurse! + return controllerContext.ParentActionViewContext.GetDataTokenInViewContextHierarchy(dataTokenName); + } + + return null; + } + /// /// Return the controller name from the controller type /// diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs index 6fb3610411..e205966f78 100644 --- a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs @@ -8,43 +8,6 @@ using Umbraco.Web.Routing; namespace Umbraco.Web.Mvc { /// - /// Redirects to the current URL rendering an Umbraco page - /// - /// - /// this is useful if you need to redirect - /// to the current page but the current page is actually a rewritten URL normally done with something like - /// Server.Transfer. - /// - public class RedirectToUmbracoUrlResult : ActionResult - { - private readonly UmbracoContext _umbracoContext; - - /// - /// Creates a new RedirectToUmbracoResult - /// - /// - public RedirectToUmbracoUrlResult(UmbracoContext umbracoContext) - { - _umbracoContext = umbracoContext; - } - - public override void ExecuteResult(ControllerContext context) - { - if (context == null) throw new ArgumentNullException("context"); - - if (context.IsChildAction) - { - throw new InvalidOperationException("Cannot redirect from a Child Action"); - } - - var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery; - context.Controller.TempData.Keep(); - - context.HttpContext.Response.Redirect(destinationUrl, endResponse: false); - } - } - - /// /// Redirects to an Umbraco page by Id or Entity /// public class RedirectToUmbracoPageResult : ActionResult diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs new file mode 100644 index 0000000000..fc472c0255 --- /dev/null +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs @@ -0,0 +1,42 @@ +using System; +using System.Web.Mvc; + +namespace Umbraco.Web.Mvc +{ + /// + /// Redirects to the current URL rendering an Umbraco page + /// + /// + /// this is useful if you need to redirect + /// to the current page but the current page is actually a rewritten URL normally done with something like + /// Server.Transfer. + /// + public class RedirectToUmbracoUrlResult : ActionResult + { + private readonly UmbracoContext _umbracoContext; + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + public RedirectToUmbracoUrlResult(UmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + } + + public override void ExecuteResult(ControllerContext context) + { + if (context == null) throw new ArgumentNullException("context"); + + if (context.IsChildAction) + { + throw new InvalidOperationException("Cannot redirect from a Child Action"); + } + + var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery; + context.Controller.TempData.Keep(); + + context.HttpContext.Response.Redirect(destinationUrl, endResponse: false); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/RenderActionInvoker.cs b/src/Umbraco.Web/Mvc/RenderActionInvoker.cs index 4fa9702a61..f4efc6a570 100644 --- a/src/Umbraco.Web/Mvc/RenderActionInvoker.cs +++ b/src/Umbraco.Web/Mvc/RenderActionInvoker.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Web.Mvc; namespace Umbraco.Web.Mvc @@ -25,7 +26,13 @@ namespace Umbraco.Web.Mvc //check if the controller is an instance of IRenderMvcController if (controllerContext.Controller is IRenderMvcController) { - return new ReflectedActionDescriptor(controllerContext.Controller.GetType().GetMethod("Index"), "Index", controllerDescriptor); + return new ReflectedActionDescriptor( + controllerContext.Controller.GetType().GetMethods() + .First(x => x.Name == "Index" && + x.GetCustomAttributes(typeof (NonActionAttribute), false).Any() == false), + "Index", + controllerDescriptor); + } } return ad; diff --git a/src/Umbraco.Web/Mvc/RenderViewEngine.cs b/src/Umbraco.Web/Mvc/RenderViewEngine.cs index 650794b3a2..1e49314429 100644 --- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs +++ b/src/Umbraco.Web/Mvc/RenderViewEngine.cs @@ -94,19 +94,17 @@ namespace Umbraco.Web.Mvc /// private bool ShouldFindView(ControllerContext controllerContext, bool isPartial) { + var umbracoToken = controllerContext.GetDataTokenInViewContextHierarchy("umbraco"); + //first check if we're rendering a partial view for the back office, or surface controller, etc... //anything that is not IUmbracoRenderModel as this should only pertain to Umbraco views. - if (isPartial - && controllerContext.RouteData.DataTokens.ContainsKey("umbraco") - && !(controllerContext.RouteData.DataTokens["umbraco"] is RenderModel)) + if (isPartial && umbracoToken is RenderModel) { return true; } //only find views if we're rendering the umbraco front end - if (controllerContext.RouteData.DataTokens.ContainsKey("umbraco") - && controllerContext.RouteData.DataTokens["umbraco"] != null - && controllerContext.RouteData.DataTokens["umbraco"] is RenderModel) + if (umbracoToken is RenderModel) { return true; } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 373e103a56..b7220b7cc1 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -141,6 +141,7 @@ namespace Umbraco.Web.Mvc // maps model protected override void SetViewData(ViewDataDictionary viewData) { + // if view data contains no model, nothing to do var source = viewData.Model; if (source == null) { @@ -148,6 +149,8 @@ namespace Umbraco.Web.Mvc return; } + // get the type of the view data model (what we have) + // get the type of this view model (what we want) var sourceType = source.GetType(); var targetType = typeof (TModel); @@ -160,13 +163,15 @@ namespace Umbraco.Web.Mvc // try to grab the content // if no content is found, return, nothing we can do - var sourceContent = source as IPublishedContent; + var sourceContent = source as IPublishedContent; // check if what we have is an IPublishedContent if (sourceContent == null && sourceType.Implements()) { + // else check if it's an IRenderModel => get the content sourceContent = ((IRenderModel)source).Content; } if (sourceContent == null) { + // else check if we can convert it to a content var attempt = source.TryConvertTo(); if (attempt.Success) sourceContent = attempt.Result; } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index a80aaa41b3..f020dc6097 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -33,5 +33,53 @@ namespace Umbraco.Web.PublishedCache /// The route. /// The value of overrides the context. string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId); + + /// + /// Creates a content fragment. + /// + /// The content type alias. + /// The content property raw values. + /// A value indicating whether the fragment is previewing. + /// A value indicating whether the fragment is managed by the cache. + /// The newly created content fragment. + // + // notes + // + // in XmlPublishedCache, IPublishedContent instances are not meant to survive longer + // that a request or else we cannot guarantee that the converted property values will + // be properly managed - because XmlPublishedProperty just stores the result of the + // conversion locally. + // + // in DrippingPublishedCache, IPublishedContent instances are meant to survive for as + // long as the content itself has not been modified, and the property respects the + // converter's indication ie whether the converted value should be cached at + // .Content - cache until the content changes + // .ContentCache - cache until any content changes + // .Request - cache for the current request + // + // a fragment can be either "detached" or "managed". + // detached: created from code, managed by code, converted property values are + // cached within the fragment itself for as long as the fragment lives + // managed: created from a property converter as part of a content, managed by + // the cache, converted property values can be cached... + // + // XmlPublishedCache: same as content properties, store the result of the + // conversion locally, neither content nor fragments should survive longer + // than a request + // DrippingPublishedCache: depends + // .Content: cache within the fragment + // .ContentCache, .Request: cache within the cache + // + // in the latter case, use a fragment-owned guid as the cache key. because we + // don't really have any other choice. this opens potential memory leaks: if the + // fragment is re-created on each request and has a property that caches its + // converted value at .ContentCache level then we'll flood that cache with data + // that's never removed (as long as no content is edited). + // + // so a requirement should be that any converter that creates fragment, should + // be marked .Content -- and nothing else + // + IPublishedContent CreateFragment(string contentTypeAlias, IDictionary dataValues, + bool isPreviewing, bool managed); } } diff --git a/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs new file mode 100644 index 0000000000..deafcaab40 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Security; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Models; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// Exposes a member object as IPublishedContent + /// + internal class MemberPublishedContent : PublishedContentBase + { + + private readonly IMember _member; + private readonly MembershipUser _membershipUser; + private readonly List _properties; + private readonly PublishedContentType _publishedMemberType; + + public MemberPublishedContent(IMember member, MembershipUser membershipUser) + { + if (member == null) throw new ArgumentNullException("member"); + if (membershipUser == null) throw new ArgumentNullException("membershipUser"); + + _member = member; + _membershipUser = membershipUser; + _properties = new List(); + _publishedMemberType = PublishedContentType.Get(PublishedItemType.Member, _member.ContentTypeAlias); + if (_publishedMemberType == null) + { + throw new InvalidOperationException("Could not get member type with alias " + _member.ContentTypeAlias); + } + foreach (var propType in _publishedMemberType.PropertyTypes) + { + var val = _member.Properties.Any(x => x.Alias == propType.PropertyTypeAlias) == false + ? string.Empty + : _member.Properties[propType.PropertyTypeAlias].Value; + _properties.Add(new RawValueProperty(propType, val ?? string.Empty)); + } + } + + #region Membership provider member properties + public string Email + { + get { return _membershipUser.Email; } + } + public string UserName + { + get { return _membershipUser.UserName; } + } + public string PasswordQuestion + { + get { return _membershipUser.PasswordQuestion; } + } + public string Comments + { + get { return _membershipUser.Comment; } + } + public bool IsApproved + { + get { return _membershipUser.IsApproved; } + } + public bool IsLockedOut + { + get { return _membershipUser.IsLockedOut; } + } + public DateTime LastLockoutDate + { + get { return _membershipUser.LastLockoutDate; } + } + public DateTime CreationDate + { + get { return _membershipUser.CreationDate; } + } + public DateTime LastLoginDate + { + get { return _membershipUser.LastLoginDate; } + } + public DateTime LastActivityDate + { + get { return _membershipUser.LastActivityDate; } + } + public DateTime LastPasswordChangedDate + { + get { return _membershipUser.LastPasswordChangedDate; } + } + #endregion + + #region IPublishedContent + public override PublishedItemType ItemType + { + get { return PublishedItemType.Member; } + } + + public override bool IsDraft + { + get { return false; } + } + + public override IPublishedContent Parent + { + get { return null; } + } + + public override IEnumerable Children + { + get { return Enumerable.Empty(); } + } + + public override ICollection Properties + { + get { return _properties; } + } + + public override IPublishedProperty GetProperty(string alias, bool recurse) + { + if (recurse) + { + throw new NotSupportedException(); + } + return GetProperty(alias); + } + + public override IPublishedProperty GetProperty(string alias) + { + return _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + } + + public override PublishedContentType ContentType + { + get { return _publishedMemberType; } + } + + public override int Id + { + get { return _member.Id; } + } + + public override int TemplateId + { + get { throw new NotSupportedException(); } + } + + public override int SortOrder + { + get { return 0; } + } + + public override string Name + { + get { return _member.Name; } + } + + public override string UrlName + { + get { throw new NotSupportedException(); } + } + + public override string DocumentTypeAlias + { + get { return _member.ContentTypeAlias; } + } + + public override int DocumentTypeId + { + get { return _member.ContentType.Id; } + } + + public override string WriterName + { + get { return _member.GetCreatorProfile().Name; } + } + + public override string CreatorName + { + get { return _member.GetCreatorProfile().Name; } + } + + public override int WriterId + { + get { return _member.CreatorId; } + } + + public override int CreatorId + { + get { return _member.CreatorId; } + } + + public override string Path + { + get { return _member.Path; } + } + + public override DateTime CreateDate + { + get { return _member.CreateDate; } + } + + public override DateTime UpdateDate + { + get { return _member.UpdateDate; } + } + + public override Guid Version + { + get { return _member.Version; } + } + + public override int Level + { + get { return _member.Level; } + } + #endregion + } +} diff --git a/src/Umbraco.Web/PublishedCache/RawValueProperty.cs b/src/Umbraco.Web/PublishedCache/RawValueProperty.cs new file mode 100644 index 0000000000..5fa95f127b --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/RawValueProperty.cs @@ -0,0 +1,46 @@ +using System; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// A published property base that uses a raw object value + /// + internal class RawValueProperty : PublishedPropertyBase + { + private readonly object _dbVal; //the value in the db + private readonly Lazy _sourceValue; + private readonly Lazy _objectValue; + private readonly Lazy _xpathValue; + + /// + /// Gets the raw value of the property. + /// + public override object DataValue { get { return _dbVal; } } + + public override bool HasValue + { + get { return _dbVal != null && _dbVal.ToString().Trim().Length > 0; } + } + + public override object Value { get { return _objectValue.Value; } } + public override object XPathValue { get { return _xpathValue.Value; } } + + public RawValueProperty(PublishedPropertyType propertyType, object propertyData) + : this(propertyType) + { + if (propertyData == null) + throw new ArgumentNullException("propertyData"); + _dbVal = propertyData; + } + + public RawValueProperty(PublishedPropertyType propertyType) + : base(propertyType) + { + _dbVal = null; + _sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_dbVal, false)); + _objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, false)); + _xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, false)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 82a5b6f85a..2fe9c13c21 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -460,5 +460,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } #endregion + + #region Fragments + + public IPublishedContent CreateFragment(string contentTypeAlias, IDictionary dataValues, + bool isPreviewing, bool managed) + { + return new PublishedFragment(contentTypeAlias, dataValues, isPreviewing, managed); + } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs new file mode 100644 index 0000000000..860f9f043f --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Models; + +namespace Umbraco.Web.PublishedCache.XmlPublishedCache +{ + class PublishedFragment : PublishedContentBase + { + private readonly PublishedContentType _contentType; + private readonly IPublishedProperty[] _properties; + + public PublishedFragment(string contentTypeAlias, IDictionary dataValues, + bool isPreviewing, bool managed) + { + IsPreviewing = isPreviewing; + _contentType = PublishedContentType.Get(PublishedItemType.Content, contentTypeAlias); + + // we don't care about managed because in both cases, XmlPublishedCache stores + // converted property values in the IPublishedContent, which is not meant to + // survive the request + + var dataValues2 = new Dictionary(); + foreach (var kvp in dataValues) + dataValues2[kvp.Key.ToLowerInvariant()] = kvp.Value; + + _properties = _contentType.PropertyTypes + .Select(x => + { + object dataValue; + return dataValues2.TryGetValue(x.PropertyTypeAlias.ToLowerInvariant(), out dataValue) + ? new PublishedFragmentProperty(x, this, dataValue) + : new PublishedFragmentProperty(x, this); + }) + .Cast() + .ToArray(); + } + + #region IPublishedContent + + public override int Id + { + get { throw new NotImplementedException(); } + } + + public override int DocumentTypeId + { + get { return _contentType.Id; } + } + + public override string DocumentTypeAlias + { + get { return _contentType.Alias; } + } + + public override PublishedItemType ItemType + { + get { return PublishedItemType.Content; } + } + + public override string Name + { + get { throw new NotImplementedException(); } + } + + public override int Level + { + get { throw new NotImplementedException(); } + } + + public override string Path + { + get { throw new NotImplementedException(); } + } + + public override int SortOrder + { + // note - could a published fragment have a sort order? + get { throw new NotImplementedException(); } + } + + public override Guid Version + { + get { throw new NotImplementedException(); } + } + + public override int TemplateId + { + get { throw new NotImplementedException(); } + } + + public override string UrlName + { + get { return string.Empty; } + } + + public override DateTime CreateDate + { + get { throw new NotImplementedException(); } + } + + public override DateTime UpdateDate + { + get { throw new NotImplementedException(); } + } + + public override int CreatorId + { + get { throw new NotImplementedException(); } + } + + public override string CreatorName + { + get { throw new NotImplementedException(); } + } + + public override int WriterId + { + get { throw new NotImplementedException(); } + } + + public override string WriterName + { + get { throw new NotImplementedException(); } + } + + public override bool IsDraft + { + get { throw new NotImplementedException(); } + } + + public override IPublishedContent Parent + { + get { throw new NotImplementedException(); } + } + + public override IEnumerable Children + { + get { throw new NotImplementedException(); } + } + + public override ICollection Properties + { + get { return _properties; } + } + + public override IPublishedProperty GetProperty(string alias) + { + return _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + } + + public override PublishedContentType ContentType + { + get { return _contentType; } + } + + #endregion + + #region Internal + + // used by PublishedFragmentProperty + internal bool IsPreviewing { get; private set; } + + #endregion + } +} diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs new file mode 100644 index 0000000000..7ae10aebdd --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs @@ -0,0 +1,43 @@ +using System; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.PublishedCache.XmlPublishedCache +{ + class PublishedFragmentProperty : PublishedPropertyBase + { + private readonly object _dataValue; + private readonly PublishedFragment _content; + + private readonly Lazy _sourceValue; + private readonly Lazy _objectValue; + private readonly Lazy _xpathValue; + + public PublishedFragmentProperty(PublishedPropertyType propertyType, PublishedFragment content) + : this(propertyType, content, null) + { } + + public PublishedFragmentProperty(PublishedPropertyType propertyType, PublishedFragment content, object dataValue) + : base(propertyType) + { + _dataValue = dataValue; + _content = content; + + _sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_dataValue, _content.IsPreviewing)); + _objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _content.IsPreviewing)); + _xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _content.IsPreviewing)); + } + + public override bool HasValue + { + get { return _dataValue != null && ((_dataValue is string) == false || string.IsNullOrWhiteSpace((string)_dataValue) == false); } + } + + public override object DataValue + { + get { return _dataValue; } + } + + public override object Value { get { return _objectValue.Value; } } + public override object XPathValue { get { return _xpathValue.Value; } } + } +} diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index 85a1c5f66f..9f64b51e04 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -152,12 +152,21 @@ namespace Umbraco.Web.Routing } else { - // look for the first domain that would be the base of the hint - var hintWithSlash = current.EndPathWithSlash(); + // look for the first domain that would be the base of the current url + // ie current is www.example.com/foo/bar, look for domain www.example.com + var currentWithSlash = current.EndPathWithSlash(); domainAndUri = domainsAndUris - .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash)); + .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)); + if (domainAndUri != null) return domainAndUri; + + // look for the first domain that the current url would be the base of + // ie current is www.example.com, look for domain www.example.com/foo/bar + domainAndUri = domainsAndUris + .FirstOrDefault(d => currentWithSlash.IsBaseOf(d.Uri.EndPathWithSlash())); + if (domainAndUri != null) return domainAndUri; + // if none matches, then try to run the filter to pick a domain - if (domainAndUri == null && filter != null) + if (filter != null) { domainAndUri = filter(domainsAndUris); // if still nothing, pick the first one? diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs index 424ffa1b52..9ab312e33e 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs @@ -312,6 +312,16 @@ namespace Umbraco.Web.Routing TemplateModel = template; } + /// + /// Resets the template. + /// + /// The RenderingEngine becomes unknown. + public void ResetTemplate() + { + EnsureWriteable(); + TemplateModel = null; + } + /// /// Gets a value indicating whether the content request has a template. /// diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 40fa86b67b..da8bd37393 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -7,6 +7,7 @@ using System.IO; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Security; using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Web.Configuration; @@ -520,7 +521,8 @@ namespace Umbraco.Web.Routing System.Web.Security.MembershipUser user = null; try { - user = System.Web.Security.Membership.GetUser(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + user = provider.GetCurrentUser(); } catch (ArgumentException) { diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs index bb52a43193..b999e2241a 100644 --- a/src/Umbraco.Web/Routing/RoutingContext.cs +++ b/src/Umbraco.Web/Routing/RoutingContext.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Umbraco.Web.PublishedCache; @@ -9,6 +10,10 @@ namespace Umbraco.Web.Routing /// public class RoutingContext { + private readonly Lazy _urlProvider; + private readonly Lazy> _publishedContentFinders; + private readonly Lazy _publishedContentLastChanceFinder; + /// /// Initializes a new instance of the class. /// @@ -23,9 +28,21 @@ namespace Umbraco.Web.Routing UrlProvider urlProvider) { UmbracoContext = umbracoContext; - PublishedContentFinders = contentFinders; - PublishedContentLastChanceFinder = contentLastChanceFinder; - UrlProvider = urlProvider; + _publishedContentFinders = new Lazy>(() => contentFinders, false); + _publishedContentLastChanceFinder = new Lazy(() => contentLastChanceFinder, false); + _urlProvider = new Lazy(() => urlProvider, false); + } + + internal RoutingContext( + UmbracoContext umbracoContext, + Lazy> contentFinders, + Lazy contentLastChanceFinder, + Lazy urlProvider) + { + UmbracoContext = umbracoContext; + _publishedContentFinders = contentFinders; + _publishedContentLastChanceFinder = contentLastChanceFinder; + _urlProvider = urlProvider; } /// @@ -33,19 +50,28 @@ namespace Umbraco.Web.Routing /// public UmbracoContext UmbracoContext { get; private set; } - /// - /// Gets the published content finders. - /// - internal IEnumerable PublishedContentFinders { get; private set; } + /// + /// Gets the published content finders. + /// + internal IEnumerable PublishedContentFinders + { + get { return _publishedContentFinders.Value; } + } - /// - /// Gets the published content last chance finder. - /// - internal IContentFinder PublishedContentLastChanceFinder { get; private set; } + /// + /// Gets the published content last chance finder. + /// + internal IContentFinder PublishedContentLastChanceFinder + { + get { return _publishedContentLastChanceFinder.Value; } + } - /// - /// Gets the urls provider. - /// - public UrlProvider UrlProvider { get; private set; } + /// + /// Gets the urls provider. + /// + public UrlProvider UrlProvider + { + get { return _urlProvider.Value; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index 102038a7c0..c04920e289 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Linq; using System.Security; using System.Xml; @@ -7,9 +8,13 @@ using Examine; using Examine.LuceneEngine; using Lucene.Net.Documents; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Services; +using Umbraco.Core.Sync; +using Umbraco.Web.Cache; using UmbracoExamine; using umbraco; using umbraco.BusinessLogic; @@ -50,23 +55,13 @@ namespace Umbraco.Web.Search if (registeredProviders == 0) return; - MediaService.Saved += MediaServiceSaved; - MediaService.Deleted += MediaServiceDeleted; - MediaService.Moved += MediaServiceMoved; - MediaService.Trashed += MediaServiceTrashed; - - ContentService.Saved += ContentServiceSaved; - ContentService.Deleted += ContentServiceDeleted; - ContentService.Moved += ContentServiceMoved; - ContentService.Trashed += ContentServiceTrashed; - - //These should only fire for providers that DONT have SupportUnpublishedContent set to true - content.AfterUpdateDocumentCache += ContentAfterUpdateDocumentCache; - content.AfterClearDocumentCache += ContentAfterClearDocumentCache; - - Member.AfterSave += MemberAfterSave; - Member.AfterDelete += MemberAfterDelete; - + //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part + // in a load balanced environment. + CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated; + var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer; if (contentIndexer != null) { @@ -80,113 +75,245 @@ namespace Umbraco.Web.Search } [SecuritySafeCritical] - static void ContentServiceTrashed(IContentService sender, Core.Events.MoveEventArgs e) + static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) + { + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.MemberService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForMember(c1); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + DeleteIndexForEntity((int)e.MessageObject, false); + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IMember; + if (c3 != null) + { + ReIndexForMember(c3); + } + break; + case MessageType.RemoveByInstance: + + // This is triggered when the item is permanently deleted + + var c4 = e.MessageObject as IMember; + if (c4 != null) + { + DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } + } + + /// + /// Handles index management for all media events - basically handling saving/copying/trashing/deleting + /// + /// + /// + [SecuritySafeCritical] + static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e) { - IndexConent(e.Entity); + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForMedia(c1, c1.Trashed == false); + } + break; + case MessageType.RemoveById: + var c2 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); + if (c2 != null) + { + //This is triggered when the item has trashed. + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c2.Id, true); + + //We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForMedia(c2, false); + } + break; + case MessageType.RefreshByJson: + + var jsonPayloads = MediaCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); + if (jsonPayloads.Any()) + { + foreach (var payload in jsonPayloads) + { + switch (payload.Operation) + { + case MediaCacheRefresher.OperationType.Saved: + var media = ApplicationContext.Current.Services.MediaService.GetById(payload.Id); + if (media != null) + { + ReIndexForMedia(media, media.Trashed == false); + } + break; + case MediaCacheRefresher.OperationType.Trashed: + //keep if trashed for indexes supporting unpublished + DeleteIndexForEntity(payload.Id, true); + break; + case MediaCacheRefresher.OperationType.Deleted: + //permanently remove from all indexes + DeleteIndexForEntity(payload.Id, false); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + break; + case MessageType.RefreshByInstance: + case MessageType.RemoveByInstance: + case MessageType.RefreshAll: + default: + //We don't support these, these message types will not fire for media + break; + } } + /// + /// Handles index management for all published content events - basically handling published/unpublished + /// + /// + /// + /// + /// This will execute on all servers taking part in load balancing + /// [SecuritySafeCritical] - static void MediaServiceTrashed(IMediaService sender, Core.Events.MoveEventArgs e) + static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e) { - IndexMedia(e.Entity); + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForContent(c1, true); + } + break; + case MessageType.RemoveById: + + //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). + + var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); + if (c2 != null) + { + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c2.Id, true); + + // We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForContent(c2, false); + } + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IContent; + if (c3 != null) + { + ReIndexForContent(c3, true); + } + break; + case MessageType.RemoveByInstance: + + //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). + + var c4 = e.MessageObject as IContent; + if (c4 != null) + { + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c4.Id, true); + + // We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForContent(c4, false); + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these for examine indexing + break; + } } + /// + /// Handles index management for all unpublished content events - basically handling saving/copying/deleting + /// + /// + /// + /// + /// This will execute on all servers taking part in load balancing + /// [SecuritySafeCritical] - static void ContentServiceMoved(IContentService sender, Umbraco.Core.Events.MoveEventArgs e) + static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e) { - IndexConent(e.Entity); - } + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.ContentService.GetById((int) e.MessageObject); + if (c1 != null) + { + ReIndexForContent(c1, false); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + DeleteIndexForEntity((int)e.MessageObject, false); + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IContent; + if (c3 != null) + { + ReIndexForContent(c3, false); + } + break; + case MessageType.RemoveByInstance: - [SecuritySafeCritical] - static void ContentServiceDeleted(IContentService sender, Umbraco.Core.Events.DeleteEventArgs e) - { - e.DeletedEntities.ForEach( - content => - ExamineManager.Instance.DeleteFromIndex( - content.Id.ToString(), - ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler))); - } + // This is triggered when the item is permanently deleted - [SecuritySafeCritical] - static void ContentServiceSaved(IContentService sender, Umbraco.Core.Events.SaveEventArgs e) - { - e.SavedEntities.ForEach(IndexConent); - } - - [SecuritySafeCritical] - static void MediaServiceMoved(IMediaService sender, Umbraco.Core.Events.MoveEventArgs e) - { - IndexMedia(e.Entity); - } - - [SecuritySafeCritical] - static void MediaServiceDeleted(IMediaService sender, Umbraco.Core.Events.DeleteEventArgs e) - { - e.DeletedEntities.ForEach( - media => - ExamineManager.Instance.DeleteFromIndex( - media.Id.ToString(), - ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler))); - } - - [SecuritySafeCritical] - static void MediaServiceSaved(IMediaService sender, Umbraco.Core.Events.SaveEventArgs e) - { - e.SavedEntities.ForEach(IndexMedia); + var c4 = e.MessageObject as IContent; + if (c4 != null) + { + DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } } [SecuritySafeCritical] - private static void MemberAfterSave(Member sender, SaveEventArgs e) + private static void ReIndexForMember(IMember member) { - //ensure that only the providers are flagged to listen execute - var xml = ExamineXmlExtensions.ToXElement(sender.ToXml(new System.Xml.XmlDocument(), false)); - var providers = ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler); - ExamineManager.Instance.ReIndexNode(xml, IndexTypes.Member, providers); - } - - [SecuritySafeCritical] - private static void MemberAfterDelete(Member sender, DeleteEventArgs e) - { - var nodeId = sender.Id.ToString(); - - //ensure that only the providers are flagged to listen execute - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler)); - } - - /// - /// Only Update indexes for providers that dont SupportUnpublishedContent - /// - /// - /// - [SecuritySafeCritical] - private static void ContentAfterUpdateDocumentCache(Document sender, DocumentCacheEventArgs e) - { - //ensure that only the providers that have DONT unpublishing support enabled - //that are also flagged to listen - ExamineManager.Instance.ReIndexNode(ToXDocument(sender, true).Root, IndexTypes.Content, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => !x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); - } - - /// - /// Only update indexes for providers that don't SupportUnpublishedContnet - /// - /// - /// - [SecuritySafeCritical] - private static void ContentAfterClearDocumentCache(Document sender, DocumentCacheEventArgs e) - { - var nodeId = sender.Id.ToString(); - //ensure that only the providers that DONT have unpublishing support enabled - //that are also flagged to listen - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => !x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); + ExamineManager.Instance.ReIndexNode( + member.ToXml(), IndexTypes.Member, + ExamineManager.Instance.IndexProviderCollection.OfType() + //ensure that only the providers are flagged to listen execute + .Where(x => x.EnableDefaultEventHandler)); } /// @@ -210,28 +337,62 @@ namespace Umbraco.Web.Search } } - - private static void IndexMedia(IMedia sender) + [SecuritySafeCritical] + private static void ReIndexForMedia(IMedia sender, bool isMediaPublished) { ExamineManager.Instance.ReIndexNode( - sender.ToXml(), "media", - ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)); + sender.ToXml(), IndexTypes.Media, + ExamineManager.Instance.IndexProviderCollection.OfType() + + //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed + // then only index this for indexers supporting unpublished media + + .Where(x => isMediaPublished || (x.SupportUnpublishedContent)) + .Where(x => x.EnableDefaultEventHandler)); } - private static void IndexConent(IContent sender) - { - //only index this content if the indexer supports unpublished content. that is because the - // content.AfterUpdateDocumentCache will handle anything being published and will only index against indexers - // that only support published content. - // NOTE: The events for publishing have changed slightly from 6.0 to 6.1 and are streamlined in 6.1. Before - // this event would fire before publishing, then again after publishing. Now the save event fires once before - // publishing and that is all. - - ExamineManager.Instance.ReIndexNode( - sender.ToXml(), "content", - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.SupportUnpublishedContent && x.EnableDefaultEventHandler)); - } + /// + /// Remove items from any index that doesn't support unpublished content + /// + /// + /// + /// If true, indicates that we will only delete this item from indexes that don't support unpublished content. + /// If false it will delete this from all indexes regardless. + /// + [SecuritySafeCritical] + private static void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) + { + ExamineManager.Instance.DeleteFromIndex( + entityId.ToString(CultureInfo.InvariantCulture), + ExamineManager.Instance.IndexProviderCollection.OfType() + + //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, + // otherwise if keepIfUnpublished == false then remove from all indexes + + .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) + .Where(x => x.EnableDefaultEventHandler)); + } + + /// + /// Re-indexes a content item whether published or not but only indexes them for indexes supporting unpublished content + /// + /// + /// + /// Value indicating whether the item is published or not + /// + [SecuritySafeCritical] + private static void ReIndexForContent(IContent sender, bool isContentPublished) + { + ExamineManager.Instance.ReIndexNode( + sender.ToXml(), IndexTypes.Content, + ExamineManager.Instance.IndexProviderCollection.OfType() + + //Index this item for all indexers if the content is published, otherwise if the item is not published + // then only index this for indexers supporting unpublished content + + .Where(x => isContentPublished || (x.SupportUnpublishedContent)) + .Where(x => x.EnableDefaultEventHandler)); + } /// /// Converts a content node to XDocument diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 899661ead6..4572026938 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Security; using Umbraco.Web.Models; +using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Security { @@ -45,7 +46,8 @@ namespace Umbraco.Web.Security /// public bool IsUmbracoMembershipProviderActive() { - return Membership.Provider.IsUmbracoMembershipProvider(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + return provider.IsUmbracoMembershipProvider(); } /// @@ -63,7 +65,8 @@ namespace Umbraco.Web.Security } //get the current membership user - var membershipUser = Membership.GetUser(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var membershipUser = provider.GetCurrentUser(); //NOTE: This should never happen since they are logged in if (membershipUser == null) throw new InvalidOperationException("Could not find member with username " + _httpContext.User.Identity.Name); @@ -73,7 +76,7 @@ namespace Umbraco.Web.Security if (model.Email.InvariantEquals(membershipUser.Email) == false) { //Use the membership provider to change the email since that is configured to do the checks to check for unique emails if that is configured. - var requiresUpdating = UpdateMember(membershipUser, Membership.Provider, model.Email); + var requiresUpdating = UpdateMember(membershipUser, provider, model.Email); membershipUser = requiresUpdating.Result; } } @@ -128,16 +131,20 @@ namespace Umbraco.Web.Security { model.Username = (model.UsernameIsEmail || model.Username == null) ? model.Email : model.Username; - var membershipUser = Membership.CreateUser(model.Username, model.Password, model.Email, - //TODO: Support q/a http://issues.umbraco.org/issue/U4-3213 - null, null, - true, out status); - - if (status != MembershipCreateStatus.Success) return null; - + MembershipUser membershipUser; + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); //update their real name - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { + membershipUser = ((UmbracoMembershipProviderBase)provider).CreateUser( + model.MemberTypeAlias, + model.Username, model.Password, model.Email, + //TODO: Support q/a http://issues.umbraco.org/issue/U4-3213 + null, null, + true, null, out status); + + if (status != MembershipCreateStatus.Success) return null; + var member = _applicationContext.Services.MemberService.GetByUsername(membershipUser.UserName); member.Name = model.Name; @@ -154,11 +161,16 @@ namespace Umbraco.Web.Security } else { - //TODO: Support this scenario! + membershipUser = provider.CreateUser(model.Username, model.Password, model.Email, + //TODO: Support q/a http://issues.umbraco.org/issue/U4-3213 + null, null, + true, null, out status); + + if (status != MembershipCreateStatus.Success) return null; } //Set member online - Membership.GetUser(model.Username, true); + provider.GetUser(model.Username, true); //Log them in FormsAuthentication.SetAuthCookie(membershipUser.UserName, true); @@ -174,13 +186,14 @@ namespace Umbraco.Web.Security /// public bool Login(string username, string password) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); //Validate credentials - if (Membership.ValidateUser(username, password) == false) + if (provider.ValidateUser(username, password) == false) { return false; } //Set member online - var member = Membership.GetUser(username, true); + var member = provider.GetUser(username, true); if (member == null) { //this should not happen @@ -192,26 +205,83 @@ namespace Umbraco.Web.Security return true; } + #region Querying for front-end + + public IPublishedContent GetByProviderKey(object key) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider() == false) + { + throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); + } + + var result = _applicationContext.Services.MemberService.GetByProviderKey(key); + return result == null ? null : new MemberPublishedContent(result, provider.GetUser(result.Username, false)); + } + + public IPublishedContent GetById(int memberId) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider() == false) + { + throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); + } + + var result = _applicationContext.Services.MemberService.GetById(memberId); + return result == null ? null : new MemberPublishedContent(result, provider.GetUser(result.Username, false)); + } + + public IPublishedContent GetByUsername(string username) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider() == false) + { + throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); + } + + var result = _applicationContext.Services.MemberService.GetByUsername(username); + return result == null ? null : new MemberPublishedContent(result, provider.GetUser(result.Username, false)); + } + + public IPublishedContent GetByEmail(string email) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider() == false) + { + throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); + } + + var result = _applicationContext.Services.MemberService.GetByEmail(email); + return result == null ? null : new MemberPublishedContent(result, provider.GetUser(result.Username, false)); + } + + #endregion + + #region Model Creation methods for member data editing on the front-end /// - /// Creates a new profile model filled in with the current members details if they are logged in. + /// Creates a new profile model filled in with the current members details if they are logged in which allows for editing + /// profile properties /// /// - public ProfileModel CreateProfileModel() + public ProfileModel GetCurrentMemberProfileModel() { if (IsLoggedIn() == false) { return null; } - if (Membership.Provider.IsUmbracoMembershipProvider()) - { - var membershipUser = Membership.GetUser(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider()) + { + var membershipUser = provider.GetCurrentUser(); var member = GetCurrentMember(); //this shouldn't happen if (member == null) return null; var model = ProfileModel.CreateModel(); model.Name = member.Name; + model.MemberTypeAlias = member.ContentTypeAlias; model.Email = membershipUser.Email; model.UserName = membershipUser.UserName; @@ -225,24 +295,13 @@ namespace Umbraco.Web.Security model.LastActivityDate = membershipUser.LastActivityDate; model.LastPasswordChangedDate = membershipUser.LastPasswordChangedDate; + var memberType = member.ContentType; - foreach (var prop in memberType.PropertyTypes.Where(x => memberType.MemberCanEditProperty(x.Alias))) - { - var value = string.Empty; - var propValue = member.Properties[prop.Alias]; - if (propValue != null) - { - value = propValue.Value.ToString(); - } + var builtIns = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); + + model.MemberProperties = GetMemberPropertiesViewModel(memberType, builtIns, member).ToList(); - model.MemberProperties.Add(new UmbracoProperty - { - Alias = prop.Alias, - Name = prop.Name, - Value = value - }); - } return model; } @@ -258,24 +317,18 @@ namespace Umbraco.Web.Security /// public RegisterModel CreateRegistrationModel(string memberTypeAlias = null) { - if (Membership.Provider.IsUmbracoMembershipProvider()) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) { memberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.Member; var memberType = _applicationContext.Services.MemberTypeService.Get(memberTypeAlias); if (memberType == null) throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias); - var props = memberType.PropertyTypes - .Where(x => memberType.MemberCanEditProperty(x.Alias)) - .Select(prop => new UmbracoProperty - { - Alias = prop.Alias, - Name = prop.Name, - Value = string.Empty - }).ToList(); - + var builtIns = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); var model = RegisterModel.CreateModel(); - model.MemberProperties = props; + model.MemberTypeAlias = memberTypeAlias; + model.MemberProperties = GetMemberPropertiesViewModel(memberType, builtIns).ToList(); return model; } else @@ -286,6 +339,64 @@ namespace Umbraco.Web.Security } } + private IEnumerable GetMemberPropertiesViewModel(IMemberType memberType, IEnumerable builtIns, IMember member = null) + { + var viewProperties = new List(); + + foreach (var prop in memberType.PropertyTypes + .Where(x => builtIns.Contains(x.Alias) == false && memberType.MemberCanEditProperty(x.Alias))) + { + var value = string.Empty; + if (member != null) + { + var propValue = member.Properties[prop.Alias]; + if (propValue != null) + { + value = propValue.Value.ToString(); + } + } + + var viewProperty = new UmbracoProperty + { + Alias = prop.Alias, + Name = prop.Name, + Value = value + }; + + //TODO: Perhaps one day we'll ship with our own EditorTempates but for now developers + // can just render their own. + + ////This is a rudimentary check to see what data template we should render + //// if developers want to change the template they can do so dynamically in their views or controllers + //// for a given property. + ////These are the default built-in MVC template types: “Boolean”, “Decimal”, “EmailAddress”, “HiddenInput”, “Html”, “Object”, “String”, “Text”, and “Url” + //// by default we'll render a text box since we've defined that metadata on the UmbracoProperty.Value property directly. + //if (prop.DataTypeId == new Guid(Constants.PropertyEditors.TrueFalse)) + //{ + // viewProperty.EditorTemplate = "UmbracoBoolean"; + //} + //else + //{ + // switch (prop.DataTypeDatabaseType) + // { + // case DataTypeDatabaseType.Integer: + // viewProperty.EditorTemplate = "Decimal"; + // break; + // case DataTypeDatabaseType.Ntext: + // viewProperty.EditorTemplate = "Text"; + // break; + // case DataTypeDatabaseType.Date: + // case DataTypeDatabaseType.Nvarchar: + // break; + // } + //} + + viewProperties.Add(viewProperty); + } + return viewProperties; + } + #endregion + /// /// Returns the login status model of the currently logged in member, if no member is logged in it returns null; /// @@ -299,8 +410,10 @@ namespace Umbraco.Web.Security model.IsLoggedIn = false; return model; } - - if (Membership.Provider.IsUmbracoMembershipProvider()) + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider()) { var member = GetCurrentMember(); //this shouldn't happen @@ -311,7 +424,7 @@ namespace Umbraco.Web.Security } else { - var member = Membership.GetUser(); + var member = provider.GetCurrentUser(); //this shouldn't happen if (member == null) return null; model.Name = member.UserName; @@ -366,8 +479,10 @@ namespace Umbraco.Web.Security } else { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + string username; - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { var member = GetCurrentMember(); username = member.Username; @@ -388,7 +503,7 @@ namespace Umbraco.Web.Security } else { - var member = Membership.GetUser(); + var member = provider.GetCurrentUser(); username = member.UserName; } @@ -621,11 +736,14 @@ namespace Umbraco.Web.Security /// private IMember GetCurrentMember() { - if (Membership.Provider.IsUmbracoMembershipProvider() == false) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("An IMember model can only be retreived when using the built-in Umbraco membership providers"); } - var member = _applicationContext.Services.MemberService.GetByUsername(_httpContext.User.Identity.Name); + var username = provider.GetCurrentUserName(); + var member = _applicationContext.Services.MemberService.GetByUsername(username); return member; } diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index 0fc70a2ce4..4ad0836a12 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Security.Providers /// /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) /// - public class MembersMembershipProvider : UmbracoServiceMembershipProvider, IUmbracoContentTypeMembershipProvider + public class MembersMembershipProvider : UmbracoMembershipProvider, IUmbracoMemberTypeMembershipProvider { public MembersMembershipProvider() : this(ApplicationContext.Current.Services.MemberService) @@ -40,6 +40,8 @@ namespace Umbraco.Web.Security.Providers } private string _defaultMemberTypeAlias = "Member"; + private volatile bool _hasDefaultMember = false; + private static readonly object Locker = new object(); public override string ProviderName { @@ -77,20 +79,35 @@ namespace Umbraco.Web.Security.Providers if (config["defaultMemberTypeAlias"] != null) { _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; - } - else - { - _defaultMemberTypeAlias = MemberService.GetDefaultMemberType(); if (_defaultMemberTypeAlias.IsNullOrWhiteSpace()) { - throw new ProviderException("No default MemberType alias is specified in the web.config string. Please add a 'defaultMemberTypeAlias' to the add element in the provider declaration in web.config"); + throw new ProviderException("No default user type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config"); } + _hasDefaultMember = true; } } public override string DefaultMemberTypeAlias { - get { return _defaultMemberTypeAlias; } + get + { + if (_hasDefaultMember == false) + { + lock (Locker) + { + if (_hasDefaultMember == false) + { + _defaultMemberTypeAlias = MemberService.GetDefaultMemberType(); + if (_defaultMemberTypeAlias.IsNullOrWhiteSpace()) + { + throw new ProviderException("No default user type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config"); + } + _hasDefaultMember = true; + } + } + } + return _defaultMemberTypeAlias; + } } } } diff --git a/src/Umbraco.Web/Security/Providers/UmbracoServiceMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs similarity index 91% rename from src/Umbraco.Web/Security/Providers/UmbracoServiceMembershipProvider.cs rename to src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs index 84bd8dc65b..d2f520b86d 100644 --- a/src/Umbraco.Web/Security/Providers/UmbracoServiceMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs @@ -19,14 +19,14 @@ namespace Umbraco.Web.Security.Providers /// /// Abstract Membership Provider that users any implementation of IMembershipMemberService{TEntity} service /// - public abstract class UmbracoServiceMembershipProvider : UmbracoMembershipProviderBase + public abstract class UmbracoMembershipProvider : UmbracoMembershipProviderBase where T : IMembershipMemberService where TEntity : class, IMembershipUser { protected IMembershipMemberService MemberService { get; private set; } - protected UmbracoServiceMembershipProvider(IMembershipMemberService memberService) + protected UmbracoMembershipProvider(IMembershipMemberService memberService) { MemberService = memberService; } @@ -78,7 +78,7 @@ namespace Umbraco.Web.Security.Providers string salt; var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); - m.Password = FormatPasswordForStorage(encodedPassword, salt); + m.RawPasswordValue = FormatPasswordForStorage(encodedPassword, salt); m.LastPasswordChangeDate = DateTime.Now; MemberService.Save(m); @@ -105,7 +105,7 @@ namespace Umbraco.Web.Security.Providers } member.PasswordQuestion = newPasswordQuestion; - member.PasswordAnswer = EncryptString(newPasswordAnswer); + member.RawPasswordAnswerValue = EncryptString(newPasswordAnswer); MemberService.Save(member); @@ -134,7 +134,7 @@ namespace Umbraco.Web.Security.Providers if (MemberService.Exists(username)) { status = MembershipCreateStatus.DuplicateUserName; - LogHelper.Warn>("Cannot create member as username already exists: " + username); + LogHelper.Warn>("Cannot create member as username already exists: " + username); return null; } @@ -142,7 +142,7 @@ namespace Umbraco.Web.Security.Providers if (MemberService.GetByEmail(email) != null && RequiresUniqueEmail) { status = MembershipCreateStatus.DuplicateEmail; - LogHelper.Warn>( + LogHelper.Warn>( "Cannot create member as a member with the same email address exists: " + email); return null; } @@ -150,14 +150,14 @@ namespace Umbraco.Web.Security.Providers string salt; var encodedPassword = EncryptOrHashNewPassword(password, out salt); - var member = MemberService.CreateMemberWithIdentity( + var member = MemberService.CreateWithIdentity( username, email, FormatPasswordForStorage(encodedPassword, salt), memberTypeAlias); member.PasswordQuestion = passwordQuestion; - member.PasswordAnswer = EncryptString(passwordAnswer); + member.RawPasswordAnswerValue = EncryptString(passwordAnswer); member.IsApproved = isApproved; member.LastLoginDate = DateTime.Now; member.LastPasswordChangeDate = DateTime.Now; @@ -199,7 +199,7 @@ namespace Umbraco.Web.Security.Providers /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = MemberService.FindMembersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); + var byEmail = MemberService.FindByEmail(emailToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); var collection = new MembershipUserCollection(); foreach (var m in byEmail) @@ -221,7 +221,7 @@ namespace Umbraco.Web.Security.Providers /// public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = MemberService.FindMembersByUsername(usernameToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); + var byEmail = MemberService.FindByUsername(usernameToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); var collection = new MembershipUserCollection(); foreach (var m in byEmail) @@ -244,7 +244,7 @@ namespace Umbraco.Web.Security.Providers { var membersList = new MembershipUserCollection(); - var pagedMembers = MemberService.GetAllMembers(pageIndex, pageSize, out totalRecords); + var pagedMembers = MemberService.GetAll(pageIndex, pageSize, out totalRecords); foreach (var m in pagedMembers) { @@ -266,7 +266,7 @@ namespace Umbraco.Web.Security.Providers /// public override int GetNumberOfUsersOnline() { - return MemberService.GetMemberCount(MemberCountType.Online); + return MemberService.GetCount(MemberCountType.Online); } /// @@ -287,12 +287,12 @@ namespace Umbraco.Web.Security.Providers var encAnswer = EncryptString(answer); - if (RequiresQuestionAndAnswer && m.PasswordAnswer != encAnswer) + if (RequiresQuestionAndAnswer && m.RawPasswordAnswerValue != encAnswer) { throw new ProviderException("Incorrect password answer"); } - var decodedPassword = DecryptPassword(m.Password); + var decodedPassword = DecryptPassword(m.RawPasswordValue); return decodedPassword; } @@ -406,14 +406,14 @@ namespace Umbraco.Web.Security.Providers var encAnswer = EncryptString(answer); - if (RequiresQuestionAndAnswer && m.PasswordAnswer != encAnswer) + if (RequiresQuestionAndAnswer && m.RawPasswordAnswerValue != encAnswer) { throw new ProviderException("Incorrect password answer"); } string salt; var encodedPassword = EncryptOrHashNewPassword(generatedPassword, out salt); - m.Password = FormatPasswordForStorage(encodedPassword, salt); + m.RawPasswordValue = FormatPasswordForStorage(encodedPassword, salt); m.LastPasswordChangeDate = DateTime.Now; MemberService.Save(m); @@ -463,7 +463,7 @@ namespace Umbraco.Web.Security.Providers if (RequiresUniqueEmail && user.Email.Trim().IsNullOrWhiteSpace() == false) { int totalRecs; - var byEmail = MemberService.FindMembersByEmail(user.Email.Trim(), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact); + var byEmail = MemberService.FindByEmail(user.Email.Trim(), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact); if (byEmail.Count(x => x.Id != m.Id) > 0) { throw new ProviderException(string.Format("A member with the email '{0}' already exists", user.Email)); @@ -498,16 +498,16 @@ namespace Umbraco.Web.Security.Providers if (member.IsApproved == false) { - LogHelper.Info>("Cannot validate member " + username + " because they are not approved"); + LogHelper.Info>("Cannot validate member " + username + " because they are not approved"); return false; } if (member.IsLockedOut) { - LogHelper.Info>("Cannot validate member " + username + " because they are currently locked out"); + LogHelper.Info>("Cannot validate member " + username + " because they are currently locked out"); return false; } - var authenticated = CheckPassword(password, member.Password); + var authenticated = CheckPassword(password, member.RawPasswordValue); if (authenticated == false) { @@ -521,7 +521,7 @@ namespace Umbraco.Web.Security.Providers { member.IsLockedOut = true; member.LastLockoutDate = DateTime.Now; - LogHelper.Info>("Member " + username + " is now locked out, max invalid password attempts exceeded"); + LogHelper.Info>("Member " + username + " is now locked out, max invalid password attempts exceeded"); } } else diff --git a/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs index e3ede8f6d8..49a3f01b57 100644 --- a/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs @@ -4,6 +4,7 @@ using System.Security.Cryptography; using System.Text; using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Umbraco.Core.Services; @@ -13,24 +14,26 @@ namespace Umbraco.Web.Security.Providers /// /// Custom Membership Provider for Umbraco Users (User authentication for Umbraco Backend CMS) /// - public class UsersMembershipProvider : UmbracoServiceMembershipProvider, IUsersMembershipProvider + public class UsersMembershipProvider : UmbracoMembershipProvider, IUsersMembershipProvider { - + public UsersMembershipProvider() : this(ApplicationContext.Current.Services.UserService) - { + { } public UsersMembershipProvider(IMembershipMemberService memberService) : base(memberService) - { + { } private string _defaultMemberTypeAlias = "writer"; + private volatile bool _hasDefaultMember = false; + private static readonly object Locker = new object(); - public override string ProviderName + public override string ProviderName { - get { return "UsersMembershipProvider"; } + get { return UmbracoSettings.DefaultBackofficeProvider; } } /// @@ -44,7 +47,7 @@ namespace Umbraco.Web.Security.Providers protected override MembershipUser ConvertToMembershipUser(IUser entity) { //the provider user key is always the int id - return entity.AsConcreteMembershipUser(Name); + return entity.AsConcreteMembershipUser(Name); } public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) @@ -55,20 +58,35 @@ namespace Umbraco.Web.Security.Providers if (config["defaultUserTypeAlias"] != null) { _defaultMemberTypeAlias = config["defaultUserTypeAlias"]; - } - else - { - var defaultFromService = MemberService.GetDefaultMemberType(); - if (defaultFromService.IsNullOrWhiteSpace()) + if (_defaultMemberTypeAlias.IsNullOrWhiteSpace()) { throw new ProviderException("No default user type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config"); } - } - } + _hasDefaultMember = true; + } + } public override string DefaultMemberTypeAlias { - get { return _defaultMemberTypeAlias; } + get + { + if (_hasDefaultMember == false) + { + lock (Locker) + { + if (_hasDefaultMember == false) + { + _defaultMemberTypeAlias = MemberService.GetDefaultMemberType(); + if (_defaultMemberTypeAlias.IsNullOrWhiteSpace()) + { + throw new ProviderException("No default user type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config"); + } + _hasDefaultMember = true; + } + } + } + return _defaultMemberTypeAlias; + } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 88e7781aaa..b197820443 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -135,7 +135,7 @@ namespace Umbraco.Web.Security /// internal bool ValidateBackOfficeCredentials(string username, string password) { - var membershipProvider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; + var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); return membershipProvider != null && membershipProvider.ValidateUser(username, password); } diff --git a/src/Umbraco.Web/Standalone/PowershellAssemblyResolver.cs b/src/Umbraco.Web/Standalone/PowershellAssemblyResolver.cs new file mode 100644 index 0000000000..63dd2ff252 --- /dev/null +++ b/src/Umbraco.Web/Standalone/PowershellAssemblyResolver.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace Umbraco.Web.Standalone +{ + internal static class PowershellAssemblyResolver + { + private static readonly Dictionary Assemblies; + private static readonly object Locko = new object(); + + static PowershellAssemblyResolver() + { + var comparer = StringComparer.CurrentCultureIgnoreCase; + Assemblies = new Dictionary(comparer); + AppDomain.CurrentDomain.AssemblyResolve += ResolveHandler; + } + + public static void AddAssemblyLocation(string path) + { + if (string.IsNullOrWhiteSpace(path)) + throw new ArgumentException("Arg is null or empty.", "path"); + + lock (Locko) + { + var name = Path.GetFileNameWithoutExtension(path); + Assemblies.Add(name, path); + } + } + + private static Assembly ResolveHandler(object sender, ResolveEventArgs args) + { + var assemblyName = new AssemblyName(args.Name); + string assemblyFile; + return Assemblies.TryGetValue(assemblyName.Name, out assemblyFile) + ? Assembly.LoadFrom(assemblyFile) + : null; + } + } +} diff --git a/src/Umbraco.Web/Standalone/StandaloneApplication.cs b/src/Umbraco.Web/Standalone/StandaloneApplication.cs index 64e2975dc2..e490684dc7 100644 --- a/src/Umbraco.Web/Standalone/StandaloneApplication.cs +++ b/src/Umbraco.Web/Standalone/StandaloneApplication.cs @@ -59,7 +59,15 @@ namespace Umbraco.Web.Standalone if (noerr) return; throw new InvalidOperationException("Application has already started."); } - Application_Start(this, EventArgs.Empty); + try + { + Application_Start(this, EventArgs.Empty); + } + catch + { + TerminateInternal(); + throw; + } _started = true; } } @@ -74,14 +82,24 @@ namespace Umbraco.Web.Standalone throw new InvalidOperationException("Application has already been terminated."); } + TerminateInternal(); + } + } + + private void TerminateInternal() + { + if (ApplicationContext.Current != null) + { ApplicationContext.Current.DisposeIfDisposable(); // should reset resolution, clear caches & resolvers... ApplicationContext.Current = null; + } + if (UmbracoContext.Current != null) + { UmbracoContext.Current.DisposeIfDisposable(); // dunno UmbracoContext.Current = null; - - _started = false; - _application = null; } + _started = false; + _application = null; } #endregion diff --git a/src/Umbraco.Web/Standalone/WriteableConfigSystem.cs b/src/Umbraco.Web/Standalone/WriteableConfigSystem.cs new file mode 100644 index 0000000000..2438256cce --- /dev/null +++ b/src/Umbraco.Web/Standalone/WriteableConfigSystem.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Configuration.Internal; +using System.Reflection; +using System.Threading; + +namespace Umbraco.Web.Standalone +{ + // see http://stackoverflow.com/questions/15653621/how-to-update-add-modify-delete-keys-in-appsettings-section-of-web-config-at-r + // see http://www.codeproject.com/Articles/69364/Override-Configuration-Manager + + internal sealed class WriteableConfigSystem : IInternalConfigSystem + { + private static readonly ReaderWriterLockSlim RwLock = new ReaderWriterLockSlim(); + private static WriteableConfigSystem _installed; + private static IInternalConfigSystem _clientConfigSystem; + private object _appsettings; + private object _connectionStrings; + private static object _sInitStateOrig; + private static object _sConfigSystemOrig; + + public static bool Installed + { + get + { + try + { + RwLock.EnterReadLock(); + return _installed != null; + } + finally + { + RwLock.ExitReadLock(); + } + } + } + + /// + /// Re-initializes the ConfigurationManager, allowing us to merge in the settings from Core.Config + /// + public static void Install() + { + try + { + RwLock.EnterWriteLock(); + + if (_installed != null) + throw new InvalidOperationException("ConfigSystem is already installed."); + + FieldInfo[] fiStateValues = null; + var tInitState = typeof(ConfigurationManager).GetNestedType("InitState", BindingFlags.NonPublic); + + if (tInitState != null) + fiStateValues = tInitState.GetFields(); + // 0: NotStarted + // 1: Started + // 2: Usable + // 3: Completed + + var fiInit = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static); + var fiSystem = typeof(ConfigurationManager).GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static); + + if (fiInit != null && fiSystem != null && fiStateValues != null) + { + _sInitStateOrig = fiInit.GetValue(null); + _sConfigSystemOrig = fiSystem.GetValue(null); + fiInit.SetValue(null, fiStateValues[1].GetValue(null)); // set to Started + fiSystem.SetValue(null, null); // clear current config system + } + + _installed = new WriteableConfigSystem(); + + var configFactoryType = Type.GetType("System.Configuration.Internal.InternalConfigSettingsFactory, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true); + var configSettingsFactory = (IInternalConfigSettingsFactory)Activator.CreateInstance(configFactoryType, true); + // just does ConfigurationManager.SetConfigurationSystem(_installed, false); + // 'false' turns initState to 2 ie usable (vs 3 ie completed) + configSettingsFactory.SetConfigurationSystem(_installed, false); + + // note: prob. don't need the factory... see how we uninstall... + + var clientConfigSystemType = Type.GetType("System.Configuration.ClientConfigurationSystem, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true); + _clientConfigSystem = (IInternalConfigSystem)Activator.CreateInstance(clientConfigSystemType, true); + } + finally + { + RwLock.ExitWriteLock(); + } + } + + public static void Uninstall() + { + try + { + RwLock.EnterWriteLock(); + + if (_installed == null) + throw new InvalidOperationException("ConfigSystem is not installed."); + + FieldInfo[] fiStateValues = null; + var tInitState = typeof(ConfigurationManager).GetNestedType("InitState", BindingFlags.NonPublic); + + if (tInitState != null) + fiStateValues = tInitState.GetFields(); + + var fiInit = typeof(ConfigurationManager).GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static); + var fiSystem = typeof(ConfigurationManager).GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static); + + if (fiInit != null && fiSystem != null && fiStateValues != null) + { + // reset - the hard way + fiInit.SetValue(null, _sInitStateOrig); + fiSystem.SetValue(null, _sConfigSystemOrig); + } + + _installed = null; + _clientConfigSystem = null; + } + finally + { + RwLock.ExitWriteLock(); + } + } + + public static void Reset() + { + try + { + RwLock.EnterWriteLock(); + + if (_installed == null) + throw new InvalidOperationException("ConfigSystem is not installed."); + + _installed._appsettings = null; + _installed._connectionStrings = null; + } + finally + { + RwLock.ExitWriteLock(); + } + } + + #region IInternalConfigSystem Members + + public object GetSection(string configKey) + { + // get the section from the default location (web.config or app.config) + var section = _clientConfigSystem.GetSection(configKey); + + try + { + RwLock.EnterReadLock(); + + switch (configKey) + { + case "appSettings": + // Return cached version if exists + if (_appsettings != null) + return _appsettings; + + // create a new collection because the underlying collection is read-only + var cfg = new NameValueCollection(); + + // If an AppSettings section exists in Web.config, read and add values from it + var nvSection = section as NameValueCollection; + if (nvSection != null) + { + var localSettings = nvSection; + foreach (string key in localSettings) + cfg.Add(key, localSettings[key]); + } + + //// -------------------------------------------------------------------- + //// Here I read and decrypt keys and add them to secureConfig dictionary + //// To test assume the following line is a key stored in secure sotrage. + ////secureConfig = SecureConfig.LoadConfig(); + //secureConfig.Add("ACriticalKey", "VeryCriticalValue"); + //// -------------------------------------------------------------------- + //foreach (KeyValuePair item in secureConfig) + //{ + // if (cfg.AllKeys.Contains(item.Key)) + // { + // cfg[item.Key] = item.Value; + // } + // else + // { + // cfg.Add(item.Key, item.Value); + // } + //} + //// -------------------------------------------------------------------- + + + // Cach the settings for future use + + _appsettings = cfg; + // return the merged version of the items from secure storage and appsettings + section = _appsettings; + break; + + case "connectionStrings": + // Return cached version if exists + if (_connectionStrings != null) + return _connectionStrings; + + // create a new collection because the underlying collection is read-only + var connectionStringsSection = new ConnectionStringsSection(); + + // copy the existing connection strings into the new collection + foreach ( + ConnectionStringSettings connectionStringSetting in + ((ConnectionStringsSection)section).ConnectionStrings) + connectionStringsSection.ConnectionStrings.Add(connectionStringSetting); + + // -------------------------------------------------------------------- + // Again Load connection strings from secure storage and merge like below + // connectionStringsSection.ConnectionStrings.Add(connectionStringSetting); + // -------------------------------------------------------------------- + + // Cach the settings for future use + _connectionStrings = connectionStringsSection; + // return the merged version of the items from secure storage and appsettings + section = _connectionStrings; + break; + } + } + finally + { + RwLock.ExitReadLock(); + } + + return section; + } + + public void RefreshConfig(string sectionName) + { + try + { + RwLock.EnterWriteLock(); + + if (sectionName == "appSettings") + { + _appsettings = null; + } + + if (sectionName == "connectionStrings") + { + _connectionStrings = null; + } + } + finally + { + RwLock.ExitWriteLock(); + } + + _clientConfigSystem.RefreshConfig(sectionName); + } + + public bool SupportsUserConfig { get { return _clientConfigSystem.SupportsUserConfig; } } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/LegacyActionHandlerEventHandler.cs b/src/Umbraco.Web/Strategies/LegacyActionHandlerEventHandler.cs new file mode 100644 index 0000000000..4ab8b25849 --- /dev/null +++ b/src/Umbraco.Web/Strategies/LegacyActionHandlerEventHandler.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using umbraco.BusinessLogic.Actions; +using umbraco.cms.businesslogic.web; +using Umbraco.Core; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Publishing; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Strategies +{ + + /// + /// This is used to trigger the legacy ActionHandlers based on events + /// + public sealed class LegacyActionHandlerEventHandler : ApplicationEventHandler + { + //NOTE: this is to fix this currently: http://issues.umbraco.org/issue/U4-1550 + + protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + ContentService.Published += ContentService_Published; + ContentService.UnPublished += ContentService_UnPublished; + } + + static void ContentService_UnPublished(IPublishingStrategy sender, PublishEventArgs e) + { + e.PublishedEntities.ForEach(x => + global::umbraco.BusinessLogic.Actions.Action.RunActionHandlers( + new Document(x), ActionUnPublish.Instance)); + } + + static void ContentService_Published(IPublishingStrategy sender, PublishEventArgs e) + { + e.PublishedEntities.ForEach(x => + global::umbraco.BusinessLogic.Actions.Action.RunActionHandlers( + new Document(x), ActionPublish.Instance)); + } + } +} diff --git a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs index 43269731e7..adde52a5bc 100644 --- a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs @@ -3,7 +3,6 @@ using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Services; using umbraco.interfaces; namespace Umbraco.Web.Strategies.Migrations @@ -29,24 +28,15 @@ namespace Umbraco.Web.Strategies.Migrations if (e.ConfiguredVersion <= target70) { - - var sql = @"DELETE a - FROM cmsContentXml a - INNER JOIN umbracoNode b - ON a.nodeId = b.id - WHERE nodeObjectType = 'B796F64C-1F99-4FFB-B886-4BF4BC011A9C' AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + " like '%-21%'"; - -// var sql = @"DELETE FROM cmsContentXml WHERE nodeId IN -// (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml -// INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id -// WHERE nodeObjectType = 'B796F64C-1F99-4FFB-B886-4BF4BC011A9C' AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + " like '%-21%')"; + var sql = @"DELETE FROM cmsContentXml WHERE nodeId IN + (SELECT nodeId FROM (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml + INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id + WHERE nodeObjectType = '" + Constants.ObjectTypes.Media + "' AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + " LIKE '%-21%') x)"; var count = e.MigrationContext.Database.Execute(sql); LogHelper.Info("Cleared " + count + " items from the media xml cache that were trashed and not meant to be there"); - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fff9344b83..22fd963203 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -95,7 +95,7 @@ False - ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll False @@ -103,7 +103,7 @@ False - ..\packages\Examine.0.1.53.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False @@ -285,9 +285,11 @@ + + @@ -298,6 +300,11 @@ + + + + + @@ -319,8 +326,9 @@ - + + @@ -396,6 +404,8 @@ + + @@ -418,9 +428,15 @@ ASPXCodeBehind + + ASPXCodeBehind + ASPXCodeBehind + + ASPXCodeBehind + ASPXCodeBehind @@ -1170,13 +1186,6 @@ script.ascx - - simple.ascx - ASPXCodeBehind - - - simple.ascx - Code @@ -1208,13 +1217,6 @@ autoDoc.aspx - - editDatatype.aspx - ASPXCodeBehind - - - editDatatype.aspx - BrowseRepository.aspx ASPXCodeBehind @@ -1890,6 +1892,7 @@ ASPXCodeBehind + @@ -1923,16 +1926,12 @@ ASPXCodeBehind - ASPXCodeBehind - - ASPXCodeBehind - diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 47c55de9e0..ab9a9ae70e 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -31,6 +31,8 @@ namespace Umbraco.Web private static readonly object Locker = new object(); private bool _replacing; + private Lazy _contentCache; + private Lazy _mediaCache; /// /// Used if not running in a web application (no real HttpContext) @@ -94,21 +96,21 @@ namespace Umbraco.Web var umbracoContext = new UmbracoContext( httpContext, applicationContext, - PublishedCachesResolver.Current.Caches, + new Lazy(() => PublishedCachesResolver.Current.Caches, false), preview); - // create the nice urls provider - // there's one per request because there are some behavior parameters that can be changed - var urlProvider = new UrlProvider( - umbracoContext, - UrlProviderResolver.Current.Providers); - // create the RoutingContext, and assign var routingContext = new RoutingContext( umbracoContext, - ContentFinderResolver.Current.Finders, - ContentLastChanceFinderResolver.Current.Finder, - urlProvider); + new Lazy>(() => ContentFinderResolver.Current.Finders), + new Lazy(() => ContentLastChanceFinderResolver.Current.Finder), + // create the nice urls provider + // there's one per request because there are some behavior parameters that can be changed + new Lazy( + () => new UrlProvider( + umbracoContext, + UrlProviderResolver.Current.Providers), + false)); //assign the routing context back umbracoContext.RoutingContext = routingContext; @@ -118,6 +120,22 @@ namespace Umbraco.Web return UmbracoContext.Current; } + /// + /// Creates a new Umbraco context. + /// + /// + /// + /// The published caches. + /// An optional value overriding detection of preview mode. + internal UmbracoContext( + HttpContextBase httpContext, + ApplicationContext applicationContext, + IPublishedCaches publishedCaches, + bool? preview = null) + : this(httpContext, applicationContext, new Lazy(() => publishedCaches), preview) + { + } + /// /// Creates a new Umbraco context. /// @@ -128,7 +146,7 @@ namespace Umbraco.Web internal UmbracoContext( HttpContextBase httpContext, ApplicationContext applicationContext, - IPublishedCaches publishedCaches, + Lazy publishedCaches, bool? preview = null) { if (httpContext == null) throw new ArgumentNullException("httpContext"); @@ -141,8 +159,8 @@ namespace Umbraco.Web Application = applicationContext; Security = new WebSecurity(); - ContentCache = publishedCaches.CreateContextualContentCache(this); - MediaCache = publishedCaches.CreateContextualMediaCache(this); + _contentCache = new Lazy(() => publishedCaches.Value.CreateContextualContentCache(this)); + _mediaCache = new Lazy(() => publishedCaches.Value.CreateContextualMediaCache(this)); InPreviewMode = preview ?? DetectInPreviewModeFromRequest(); // set the urls... @@ -241,17 +259,23 @@ namespace Umbraco.Web /// /// Gets or sets the published content cache. /// - public ContextualPublishedContentCache ContentCache { get; private set; } + public ContextualPublishedContentCache ContentCache + { + get { return _contentCache.Value; } + } /// /// Gets or sets the published media cache. /// - public ContextualPublishedMediaCache MediaCache { get; private set; } + public ContextualPublishedMediaCache MediaCache + { + get { return _mediaCache.Value; } + } /// - /// Boolean value indicating whether the current request is a front-end umbraco request - /// - public bool IsFrontEndUmbracoRequest + /// Boolean value indicating whether the current request is a front-end umbraco request + /// + public bool IsFrontEndUmbracoRequest { get { return PublishedContentRequest != null; } } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 6aa2754fbc..1c0882b9f5 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -13,10 +13,12 @@ using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Core.Security; using Umbraco.Core.Xml; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using Umbraco.Web.Templates; using umbraco; using System.Collections.Generic; @@ -35,7 +37,8 @@ namespace Umbraco.Web { private readonly UmbracoContext _umbracoContext; private readonly IPublishedContent _currentPage; - + private readonly MembershipHelper _membershipHelper; + /// /// Custom constructor setting the current page to the parameter passed in /// @@ -46,6 +49,7 @@ namespace Umbraco.Web { if (content == null) throw new ArgumentNullException("content"); _currentPage = content; + _membershipHelper = new MembershipHelper(_umbracoContext); } /// @@ -57,6 +61,7 @@ namespace Umbraco.Web if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); if (umbracoContext.RoutingContext == null) throw new NullReferenceException("The RoutingContext on the UmbracoContext cannot be null"); _umbracoContext = umbracoContext; + _membershipHelper = new MembershipHelper(_umbracoContext); if (_umbracoContext.IsFrontEndUmbracoRequest) { _currentPage = _umbracoContext.PublishedContentRequest.PublishedContent; @@ -405,7 +410,8 @@ namespace Umbraco.Web { if (IsProtected(nodeId, path)) { - return Member.IsLoggedOn() && Access.HasAccess(nodeId, path, Membership.GetUser()); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + return _membershipHelper.IsLoggedIn() && Access.HasAccess(nodeId, path, provider.GetCurrentUser()); } return true; } @@ -416,11 +422,7 @@ namespace Umbraco.Web /// True is the current user is logged in public bool MemberIsLoggedOn() { - /* - MembershipUser u = Membership.GetUser(); - return u != null; - */ - return Member.IsLoggedOn(); + return _membershipHelper.IsLoggedIn(); } #endregion @@ -482,9 +484,51 @@ namespace Umbraco.Web #endregion - #region Content + #region Members - public IPublishedContent TypedContent(object id) + public IPublishedContent TypedMember(object id) + { + var asInt = id.TryConvertTo(); + return asInt ? _membershipHelper.GetById(asInt.Result) : _membershipHelper.GetByProviderKey(id); + } + + public IPublishedContent TypedMember(int id) + { + return _membershipHelper.GetById(id); + } + + public IPublishedContent TypedMember(string id) + { + var asInt = id.TryConvertTo(); + return asInt ? _membershipHelper.GetById(asInt.Result) : _membershipHelper.GetByProviderKey(id); + } + + public dynamic Member(object id) + { + var asInt = id.TryConvertTo(); + return asInt + ? _membershipHelper.GetById(asInt.Result).AsDynamic() + : _membershipHelper.GetByProviderKey(id).AsDynamic(); + } + + public dynamic Member(int id) + { + return _membershipHelper.GetById(id).AsDynamic(); + } + + public dynamic Member(string id) + { + var asInt = id.TryConvertTo(); + return asInt + ? _membershipHelper.GetById(asInt.Result).AsDynamic() + : _membershipHelper.GetByProviderKey(id).AsDynamic(); + } + + #endregion + + #region Content + + public IPublishedContent TypedContent(object id) { return TypedDocumentById(id, _umbracoContext.ContentCache); } @@ -1335,6 +1379,16 @@ namespace Umbraco.Web #endregion + #region Prevalues + + public string GetPreValueAsString(int id) + { + var ds = _umbracoContext.Application.Services.DataTypeService; + return ds.GetPreValueAsString(id); + } + + #endregion + /// /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. diff --git a/src/Umbraco.Web/app.config b/src/Umbraco.Web/app.config new file mode 100644 index 0000000000..568750da9c --- /dev/null +++ b/src/Umbraco.Web/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 8d5d335b9b..7932361039 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -1,8 +1,8 @@  - + - + diff --git a/src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs b/src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs index a2166e82c0..e30fcedabe 100644 --- a/src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs +++ b/src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.umbraco.presentation public INode Parent { - get { return ConvertToNode(_doc.Parent); } + get { return _doc.Parent == null ? null : ConvertToNode(_doc.Parent); } } public int Id { get; private set; } public int template { get; private set; } diff --git a/src/Umbraco.Web/umbraco.presentation/LegacyClasses.cs b/src/Umbraco.Web/umbraco.presentation/LegacyClasses.cs index c515f5e16f..dffac19054 100644 --- a/src/Umbraco.Web/umbraco.presentation/LegacyClasses.cs +++ b/src/Umbraco.Web/umbraco.presentation/LegacyClasses.cs @@ -10,6 +10,7 @@ using System.Web.UI; using System.Xml; using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Security; using umbraco.NodeFactory; using umbraco.cms.businesslogic.web; using umbraco.interfaces; @@ -421,7 +422,8 @@ namespace umbraco { HttpContext.Current.Trace.Write("umbracoRequestHandler", "Page protected"); - var user = System.Web.Security.Membership.GetUser(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var user = provider.GetCurrentUser(); if (user == null || !library.IsLoggedOn()) { diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 9270e57cc0..ab611bda87 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -521,9 +521,7 @@ namespace umbraco var cachedFieldKeyStart = string.Format("{0}{1}_", CacheKeys.ContentItemCacheKey, d.Id); ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(cachedFieldKeyStart); - - Action.RunActionHandlers(d, ActionPublish.Instance); - + FireAfterUpdateDocumentCache(d, e); } } @@ -532,6 +530,7 @@ namespace umbraco /// Updates the document cache for multiple documents /// /// The documents. + [Obsolete("This is not used and will be removed from the codebase in future versions")] public virtual void UpdateDocumentCache(List Documents) { // We need to lock content cache here, because we cannot allow other threads @@ -550,11 +549,6 @@ namespace umbraco XmlContentInternal = xmlContentCopy; ClearContextCache(); } - - foreach (Document d in Documents) - { - Action.RunActionHandlers(d, ActionPublish.Instance); - } } /// @@ -635,12 +629,6 @@ namespace umbraco } } - if (x != null) - { - // Run Handler - Action.RunActionHandlers(doc, ActionUnPublish.Instance); - } - //SD: changed to fire event BEFORE running the sitemap!! argh. FireAfterClearDocumentCache(doc, e); diff --git a/src/Umbraco.Web/umbraco.presentation/install/steps/LegacyClasses.cs b/src/Umbraco.Web/umbraco.presentation/install/steps/LegacyClasses.cs index d62bada27a..573c97d4de 100644 --- a/src/Umbraco.Web/umbraco.presentation/install/steps/LegacyClasses.cs +++ b/src/Umbraco.Web/umbraco.presentation/install/steps/LegacyClasses.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using umbraco.BusinessLogic; +using Umbraco.Core.Security; using umbraco.DataLayer; using umbraco.presentation.install.utills; using umbraco.providers; @@ -523,19 +524,21 @@ namespace umbraco.presentation.install { Page.Validate(); + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); + if (Page.IsValid) { User u = User.GetUser(0); - MembershipUser user = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].GetUser(0, true); + var user = provider.GetUser(0, true); user.ChangePassword(u.GetPassword(), tb_password.Text.Trim()); // Is it using the default membership provider - if (Membership.Providers[UmbracoSettings.DefaultBackofficeProvider] is UsersMembershipProvider) + if (provider is UsersMembershipProvider) { // Save user in membership provider - UsersMembershipUser umbracoUser = user as UsersMembershipUser; + var umbracoUser = user as UsersMembershipUser; umbracoUser.FullName = tb_name.Text.Trim(); - Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].UpdateUser(umbracoUser); + provider.UpdateUser(umbracoUser); // Save user details u.Email = tb_email.Text.Trim(); @@ -543,7 +546,7 @@ namespace umbraco.presentation.install else { u.Name = tb_name.Text.Trim(); - if (!(Membership.Providers[UmbracoSettings.DefaultBackofficeProvider] is ActiveDirectoryMembershipProvider)) Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].UpdateUser(user); + if ((provider is ActiveDirectoryMembershipProvider) == false) provider.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! @@ -555,8 +558,8 @@ namespace umbraco.presentation.install { try { - System.Net.WebClient client = new System.Net.WebClient(); - NameValueCollection values = new NameValueCollection(); + var client = new System.Net.WebClient(); + var values = new NameValueCollection(); values.Add("name", tb_name.Text); values.Add("email", tb_email.Text); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberGroups.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberGroups.cs index 1b12404840..ac987fb1d0 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberGroups.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberGroups.cs @@ -63,7 +63,10 @@ function openMemberGroup(id) { public override void Render(ref XmlTree tree) { - foreach(string role in Roles.GetAllRoles()) { + var roles = Roles.GetAllRoles(); + Array.Sort(roles); + + foreach(string role in roles) { // MemberGroup[] MemberGroups = MemberGroup.GetAll; // for (int i = 0; i < MemberGroups.Length; i++) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs index 19ccefe64d..4da1bd9162 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs @@ -39,9 +39,11 @@ namespace umbraco public loadMemberTypes(string application) : base(application) { } protected override void CreateRootNode(ref XmlTreeNode rootNode) - { + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + // only show member types if we're using umbraco members on the website - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { rootNode.NodeType = "init" + TreeAlias; rootNode.NodeID = "init"; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs index dee3940b96..d0ce98941f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs @@ -92,6 +92,8 @@ function openContentItem(id) { /// The tree. public override void Render(ref XmlDocument Tree) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + string letter = ""; string ContentItemParent = ""; if (HttpContext.Current.Request.QueryString.ToString().IndexOf("letter") >= 0) @@ -158,7 +160,7 @@ function openContentItem(id) { } else { - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { foreach (Member m in Member.getMemberFromFirstLetter(letter.ToCharArray()[0])) { @@ -178,7 +180,7 @@ function openContentItem(id) { else { int total; - foreach (System.Web.Security.MembershipUser u in System.Web.Security.Membership.Provider.FindUsersByName(letter + "%", 0, 9999, out total)) + foreach (MembershipUser u in provider.FindUsersByName(letter + "%", 0, 9999, out total)) { XmlElement treeElement = Tree.CreateElement("tree"); @@ -222,7 +224,7 @@ function openContentItem(id) { } //Add folder named "Others", only supported by umbraco - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { XmlElement treeElementOther = Tree.CreateElement("tree"); treeElementOther.SetAttribute("menu", ""); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/cache/LegacyClasses.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/cache/LegacyClasses.cs index a497c86884..ad00646305 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/cache/LegacyClasses.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/cache/LegacyClasses.cs @@ -52,7 +52,7 @@ namespace umbraco.presentation.cache /// public ICacheRefresher[] GetAll() { - return CacheRefreshersResolver.Current.CacheResolvers.ToArray(); + return CacheRefreshersResolver.Current.CacheRefreshers.ToArray(); } #endregion diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/channels/UmbracoMetaWeblogAPI.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/channels/UmbracoMetaWeblogAPI.cs index 4df569f87d..00e1fc721f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/channels/UmbracoMetaWeblogAPI.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/channels/UmbracoMetaWeblogAPI.cs @@ -14,6 +14,7 @@ using umbraco.cms.businesslogic.media; using umbraco.cms.businesslogic.property; using umbraco.cms.businesslogic.propertytype; using umbraco.cms.businesslogic.web; +using Umbraco.Core.Security; using umbraco.presentation.channels.businesslogic; using Post = CookComputing.MetaWeblog.Post; @@ -45,7 +46,7 @@ namespace umbraco.presentation.channels Description = "Where applicable, this specifies whether the blog " + "should be republished after the post has been deleted.")] bool publish) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { Channel userChannel = new Channel(username); new Document(int.Parse(postid)) @@ -62,7 +63,7 @@ namespace umbraco.presentation.channels Post post, bool publish) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { Channel userChannel = new Channel(username); Document doc = new Document(Convert.ToInt32(postid)); @@ -140,7 +141,7 @@ namespace umbraco.presentation.channels string username, string password) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { Channel userChannel = new Channel(username); if (userChannel.FieldCategoriesAlias != null && userChannel.FieldCategoriesAlias != "") @@ -218,7 +219,7 @@ namespace umbraco.presentation.channels string username, string password) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { Channel userChannel = new Channel(username); Document d = new Document(int.Parse(postid)); @@ -258,7 +259,7 @@ namespace umbraco.presentation.channels string password, int numberOfPosts) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { ArrayList blogPosts = new ArrayList(); ArrayList blogPostsObjects = new ArrayList(); @@ -369,7 +370,7 @@ namespace umbraco.presentation.channels Post post, bool publish) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { Channel userChannel = new Channel(username); User u = new User(username); @@ -427,7 +428,7 @@ namespace umbraco.presentation.channels string password, FileData file) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { User u = new User(username); Channel userChannel = new Channel(username); @@ -512,9 +513,11 @@ namespace umbraco.presentation.channels return new UrlData(); } - private static bool validateUser(string username, string password) + private static bool ValidateUser(string username, string password) { - return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password); + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); + + return provider.ValidateUser(username, password); } [XmlRpcMethod("blogger.getUsersBlogs", @@ -525,7 +528,7 @@ namespace umbraco.presentation.channels string username, string password) { - if (validateUser(username, password)) + if (ValidateUser(username, password)) { BlogInfo[] blogs = new BlogInfo[1]; User u = new User(username); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index a81ff499d9..9138a470e8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -334,7 +334,17 @@ namespace umbraco.controls SavingContentType(_contentType); } - _contentType.Save(); + try + { + _contentType.Save(); + } + catch (DuplicateNameException ex) + { + DuplicateAliasValidator.IsValid = false; + asyncState.SaveArgs.IconType = BasePage.speechBubbleIcon.error; + asyncState.SaveArgs.Message = ex.Message; + return; + } Trace.Write("ContentTypeControlNew", "task completing"); }; @@ -1628,5 +1638,14 @@ Umbraco.Controls.TabView.onActiveTabChange(function(tabviewid, tabid, tabs) { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.Literal checkTxtAliasJs; + + /// + /// DuplicateAliasValidator control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator DuplicateAliasValidator; } } \ No newline at end of file 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 3582e873a4..a3f76246e0 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; } 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 4a611add0f..ca87db9600 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Umbraco.Core.Security; namespace umbraco.cms.presentation.create.controls @@ -22,12 +23,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 (Membership.Provider.IsUmbracoMembershipProvider()) + 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 +44,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 +63,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); } @@ -105,7 +113,9 @@ 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; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs index cb602098b8..976098a937 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs @@ -76,15 +76,18 @@ namespace umbraco string email = nameAndMail.Length > 0 ? nameAndMail[1] : ""; string password = nameAndMail.Length > 1 ? nameAndMail[2] : ""; string loginName = nameAndMail.Length > 2 ? nameAndMail[3] : ""; - if (Membership.Provider.IsUmbracoMembershipProvider() && TypeID != -1) + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider() && TypeID != -1) { var dt = new MemberType(TypeID); - var provider = (UmbracoMembershipProviderBase)Membership.Provider; + 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) @@ -105,7 +108,7 @@ namespace umbraco else { MembershipCreateStatus mc; - Membership.CreateUser(name, password, email, "empty", "empty", true, out mc); + provider.CreateUser(name, password, email, "empty", "empty", true, null, out mc); if (mc != MembershipCreateStatus.Success) { throw new Exception("Error creating Member: " + mc); @@ -118,9 +121,10 @@ namespace umbraco public bool Delete() { - 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 be6f59cab4..8953793caa 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodeType.ascx.cs @@ -1,4 +1,6 @@ +using System.Globalization; using Umbraco.Core; +using Umbraco.Web; namespace umbraco.cms.presentation.create.controls { @@ -19,20 +21,20 @@ 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"); if (!IsPostBack) { - string nodeId = umbraco.helper.Request("nodeId"); + string nodeId = Request.GetItemAsString("nodeId"); if (String.IsNullOrEmpty(nodeId) || nodeId == "init") { masterType.Attributes.Add("style", "width: 350px;"); 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 @@ -56,7 +58,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) { @@ -65,10 +67,10 @@ namespace umbraco.cms.presentation.create.controls createTemplateVal = 1; // check master type - string 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"); string returnUrl = umbraco.presentation.create.dialogHandler_temp.Create( - umbraco.helper.Request("nodeType"), + Request.GetItemAsString("nodeType"), int.Parse(masterTypeVal), createTemplateVal, rename.Text); @@ -81,24 +83,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 a885ca9008..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx +++ /dev/null @@ -1,15 +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" %> - -
            <%=umbraco.ui.Text("name")%>:*
            - - - -
            - -
            - -   <%= umbraco.ui.Text("or") %>   - <%=umbraco.ui.Text("cancel")%> -
            diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.cs index db219d6a05..ec3ca3dc95 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.cs @@ -1,65 +1,90 @@ -using System; +using System; +using System.Web.UI.WebControls; using umbraco.BasePages; +using Umbraco.Web; namespace umbraco.cms.presentation.create.controls { /// - /// Summary description for simple. - /// - public partial class simple : System.Web.UI.UserControl - { + /// Summary description for simple. + ///
            + public partial class simple : System.Web.UI.UserControl + { - protected void Page_Load(object sender, System.EventArgs e) - { - sbmt.Text = ui.Text("create"); - // Put user code to initialize the page here - } + protected void Page_Load(object sender, EventArgs e) + { + sbmt.Text = ui.Text("create"); + // 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() - { + protected void sbmt_Click(object sender, EventArgs e) + { + if (Page.IsValid) + { + int nodeId; + if (int.TryParse(Request.QueryString["nodeId"], out nodeId) == false) + nodeId = -1; - } - #endregion + if (Request.GetItemAsString("nodeId") != "init") + nodeId = int.Parse(Request.GetItemAsString("nodeId")); - 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; + try + { + var returnUrl = umbraco.presentation.create.dialogHandler_temp.Create( + Request.GetItemAsString("nodeType"), + nodeId, + rename.Text.Trim()); - if (umbraco.helper.Request("nodeId") != "init") - nodeId = int.Parse(umbraco.helper.Request("nodeId")); - - string returnUrl = umbraco.presentation.create.dialogHandler_temp.Create( - umbraco.helper.Request("nodeType"), - nodeId, - rename.Text.Trim()); - - - BasePage.Current.ClientTools - .ChangeContentFrameUrl(returnUrl) - .ChildNodeCreated() - .CloseModalWindow(); - - + BasePage.Current.ClientTools + .ChangeContentFrameUrl(returnUrl) + .ChildNodeCreated() + .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 aa789441c0..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/simple.ascx.designer.cs +++ /dev/null @@ -1,52 +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 simple { - - /// - /// 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/userTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs index 942405d0ae..7be7679178 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/userTasks.cs @@ -3,6 +3,7 @@ using System.Data; using System.Web.Security; using Umbraco.Core.Logging; using umbraco.BusinessLogic; +using Umbraco.Core.Security; using umbraco.DataLayer; using umbraco.BasePages; using Umbraco.Core.IO; @@ -38,14 +39,16 @@ 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 password = Membership.GeneratePassword( - Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].MinRequiredPasswordLength, - Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].MinRequiredNonAlphanumericCharacters); + provider.MinRequiredPasswordLength, + provider.MinRequiredNonAlphanumericCharacters); var parts = Alias.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) @@ -55,7 +58,7 @@ namespace umbraco var login = parts[0]; var email = parts[1]; - var u = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].CreateUser( + var u = provider.CreateUser( login, password, email.Trim().ToLower(), "", "", true, null, out status); if (u == null) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/ChangePassword.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/ChangePassword.ascx.cs index 82cb31733f..09deb110ec 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/ChangePassword.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/ChangePassword.ascx.cs @@ -6,6 +6,7 @@ using System.Web.UI; using System.Web.UI.WebControls; using System.Web.Security; using umbraco.BusinessLogic; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.dashboard { @@ -13,7 +14,7 @@ namespace umbraco.presentation.umbraco.dashboard { protected MembershipProvider Provider { - get { return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; } + get { return MembershipProviderExtensions.GetUsersMembershipProvider(); } } protected override void OnLoad(EventArgs e) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx deleted file mode 100644 index 86066a5c63..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx +++ /dev/null @@ -1,27 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" Title="Edit data type" - CodeBehind="editDatatype.aspx.cs" AutoEventWireup="True" Inherits="umbraco.cms.presentation.developer.editDatatype" %> - -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs index 5cf50baa6c..d713a9b689 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.cs @@ -1,9 +1,11 @@ -using System; +using System; using System.Collections; +using System.Data; using System.Web.UI; using System.Web.UI.WebControls; using umbraco.cms.presentation.Trees; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace umbraco.cms.presentation.developer { @@ -64,17 +66,17 @@ namespace umbraco.cms.presentation.developer ClientTools .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias) - .SyncTree("-1,init," + _id.ToString(), false); + .SyncTree("-1,init," + _id, false); } if (dt.DataType != null) litGuid.Text = dt.DataType.Id.ToString(); - Panel1.Text = umbraco.ui.Text("edit") + " datatype: " + dt.Text; - insertPrevalueEditor(); + Panel1.Text = ui.Text("edit") + " datatype: " + dt.Text; + InsertPrevalueEditor(); } - private void insertPrevalueEditor() + private void InsertPrevalueEditor() { try { @@ -93,7 +95,10 @@ namespace umbraco.cms.presentation.developer plcEditorPrevalueControl.Controls.Add(new LiteralControl("No editor control selected")); } } - catch { } + catch (Exception ex) + { + LogHelper.Error("An error occurred inserting pre-value editor", ex); + } } @@ -106,41 +111,122 @@ namespace umbraco.cms.presentation.developer dt.Text = txtName.Text; dt.DataType = f.DataType(new Guid(ddlRenderControl.SelectedValue)); - dt.Save(); - ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dataTypeSaved", null), ""); - - ClientTools.SyncTree(dt.Path, true); + try + { + dt.Save(); + ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dataTypeSaved", null), ""); + ClientTools.SyncTree(dt.Path, true); + } + catch (DuplicateNameException) + { + DuplicateNameValidator.IsValid = false; + ClientTools.ShowSpeechBubble(speechBubbleIcon.error, "A data type with the name " + dt.Text + " already exists", ""); + } } - - #region Web Form Designer generated code - - override protected void OnInit(EventArgs e) { save = Panel1.Menu.NewImageButton(); save.ID = "save"; - save.Click += new System.Web.UI.ImageClickEventHandler(save_click); + save.Click += save_click; save.ImageUrl = SystemDirectories.Umbraco + "/images/editor/save.gif"; Panel1.hasMenu = true; - // - // 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() - { + protected CustomValidator DuplicateNameValidator; - } - #endregion + /// + /// Panel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.UmbracoPanel Panel1; + + /// + /// pane_control control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_control; + + /// + /// pp_name control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_name; + + /// + /// txtName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtName; + + /// + /// pp_renderControl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_renderControl; + + /// + /// ddlRenderControl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList ddlRenderControl; + + /// + /// pp_guid control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_guid; + + /// + /// litGuid control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal litGuid; + + /// + /// pane_settings control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_settings; + + /// + /// plcEditorPrevalueControl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder plcEditorPrevalueControl; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.designer.cs deleted file mode 100644 index 9d1d8fc550..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/DataTypes/editDatatype.aspx.designer.cs +++ /dev/null @@ -1,105 +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.developer { - - - public partial class editDatatype { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel Panel1; - - /// - /// pane_control control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_control; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_name; - - /// - /// txtName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtName; - - /// - /// pp_renderControl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_renderControl; - - /// - /// ddlRenderControl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList ddlRenderControl; - - /// - /// pp_guid control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_guid; - - /// - /// litGuid control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal litGuid; - - /// - /// pane_settings control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_settings; - - /// - /// plcEditorPrevalueControl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder plcEditorPrevalueControl; - } -} 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 c81e1cf8ad..9f419e2513 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 @@ -41,7 +41,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("/")) @@ -109,7 +109,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/Xslt/xsltInsertValueOf.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs index 05be8c77e7..267792a4b7 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,12 +3,14 @@ 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.cms.businesslogic.propertytype; +using Umbraco.Core; namespace umbraco.developer { @@ -34,16 +36,20 @@ 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(UmbracoSettings.UseLegacyXmlSchema) + if (UmbracoSettings.UseLegacyXmlSchema) preValuesSource.Add(string.Format("data [@alias = '{0}']", pt.Alias)); else preValuesSource.Add(pt.Alias); existingGenProps += pt.Alias + ","; } + } + preValuesSource.Sort(); preValues.DataSource = preValuesSource; @@ -56,24 +62,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/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs index 0e515da061..42bc8b9bc9 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Globalization; using System.Web.UI; using System.Web.UI.WebControls; using System.Xml; @@ -58,10 +59,10 @@ namespace umbraco.dialogs masterType.Items.Add(new ListItem(ui.Text("none") + "...", "0")); foreach (var docT in DocumentType.GetAllAsList()) { - masterType.Items.Add(new ListItem(docT.Text, docT.Id.ToString())); + masterType.Items.Add(new ListItem(docT.Text, docT.Id.ToString(CultureInfo.InvariantCulture))); } - masterType.SelectedValue = documentType.MasterContentType.ToString(); + masterType.SelectedValue = documentType.MasterContentType.ToString(CultureInfo.InvariantCulture); rename.Text = documentType.Text + " (copy)"; pane_settings.Text = "Make a copy of the document type '" + documentType.Text + "' and save it under a new name"; @@ -146,6 +147,9 @@ 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"))); @@ -153,6 +157,20 @@ namespace umbraco.dialogs 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.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs index 2af1add9fa..c658db703f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -102,14 +102,17 @@ namespace umbraco.presentation.umbraco.dialogs if (Access.GetProtectionType(documentId) == ProtectionType.Simple) { - MembershipUser m = Access.GetAccessingMembershipUser(documentId); - 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"; + MembershipUser m = Access.GetAccessingMembershipUser(documentId); + 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) @@ -177,6 +180,8 @@ namespace umbraco.presentation.umbraco.dialogs //reset SimpleLoginNameValidator.IsValid = true; + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (Page.IsValid) { int pageId = int.Parse(helper.Request("nodeId")); @@ -185,15 +190,15 @@ namespace umbraco.presentation.umbraco.dialogs { var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; - var member = Membership.GetUser(memberLogin); + 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 (Membership.Provider.IsUmbracoMembershipProvider() == false) + if (provider.IsUmbracoMembershipProvider() == false) { - member = Membership.CreateUser(memberLogin, simplePassword.Text, tempEmail); + member = provider.CreateUser(memberLogin, simplePassword.Text, tempEmail); } else { @@ -202,9 +207,9 @@ namespace umbraco.presentation.umbraco.dialogs { MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } - var provider = Membership.Provider.AsUmbracoMembershipProvider(); + var castedProvider = provider.AsUmbracoMembershipProvider(); MembershipCreateStatus status; - member = provider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, + member = castedProvider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); if (status != MembershipCreateStatus.Success) { 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 38518804b4..30f9fbb12d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/treePicker.aspx.cs @@ -21,10 +21,14 @@ namespace umbraco.dialogs TreeParams = TreeRequestParams.FromQueryStrings().CreateTreeService(); DataBind(); - if(Request.QueryString["selected"] != null && TreeParams.TreeType == "content") { + if(Request.QueryString["selected"] != null && TreeParams.TreeType == "content") + { var currContent = Services.ContentService.GetById(int.Parse(Request.QueryString["selected"])); - if (currContent.ParentId > 0) - DialogTree.SelectedNodePath = currContent.Path; + if (currContent != null) + { + if (currContent.ParentId > 0) + DialogTree.SelectedNodePath = currContent.Path; + } } } 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 671891701e..449d51832d 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,14 @@ 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; +using Umbraco.Core.Persistence.SqlSyntax; using umbraco.DataLayer; namespace umbraco.dialogs @@ -58,7 +60,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"); } @@ -98,24 +106,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/login.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/login.aspx.cs index 3773e1ffab..a9a6339d88 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/login.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/login.aspx.cs @@ -37,7 +37,7 @@ namespace umbraco.cms.presentation { get { - var provider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; + var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); if (provider == null) { throw new ProviderException("The membership provider " + UmbracoSettings.DefaultBackofficeProvider + " was not found"); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs index 4151c3cecd..c62eebbd6a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs @@ -51,9 +51,11 @@ namespace umbraco.cms.presentation.members { _membershipHelper = new MembershipHelper(UmbracoContext.Current); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + // Add password changer var passwordChanger = (passwordChanger)LoadControl(SystemDirectories.Umbraco + "/controls/passwordChanger.ascx"); - passwordChanger.MembershipProviderName = Membership.Provider.Name; + passwordChanger.MembershipProviderName = Constants.Conventions.Member.UmbracoMemberProviderName; //Add a custom validation message for the password changer var passwordValidation = new CustomValidator { @@ -73,11 +75,11 @@ namespace umbraco.cms.presentation.members MemberPasswordTxt.Controls.Add(passwordChanger); MemberPasswordTxt.Controls.Add(validatorContainer); - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { _memberEntity = new Member(int.Parse(Request.QueryString["id"])); - - _membershipUser = Membership.GetUser(_memberEntity.LoginName, false); + + _membershipUser = provider.GetUser(_memberEntity.LoginName, false); _contentControl = new ContentControl(_memberEntity, ContentControl.publishModes.NoPublish, "TabView1"); _contentControl.Width = Unit.Pixel(666); _contentControl.Height = Unit.Pixel(666); @@ -132,7 +134,7 @@ namespace umbraco.cms.presentation.members menuSave.Click += MenuSaveClick; menuSave.AltText = ui.Text("buttons", "save", null); - _membershipUser = Membership.GetUser(Request.QueryString["id"], false); + _membershipUser = provider.GetUser(Request.QueryString["id"], false); MemberLoginNameTxt.Text = _membershipUser.UserName; if (IsPostBack == false) { @@ -156,7 +158,10 @@ namespace umbraco.cms.presentation.members _memberGroups.ID = "Membergroups"; _memberGroups.Width = 175; var selectedMembers = ""; - foreach (var role in Roles.GetAllRoles()) + var roles = Roles.GetAllRoles(); + Array.Sort(roles); + + foreach(var role in roles) { // if a role starts with __umbracoRole we won't show it as it's an internal role used for public access if (role.StartsWith("__umbracoRole") == false) @@ -177,7 +182,7 @@ namespace umbraco.cms.presentation.members p.addProperty(ui.Text("membergroup"), _memberGroups); - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { _contentControl.tpProp.Controls.Add(p); _contentControl.Save += tmp_save; @@ -202,10 +207,12 @@ namespace umbraco.cms.presentation.members void MemberEmailExistCheck_ServerValidate(object source, ServerValidateEventArgs args) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var oldEmail = _memberEntity.Email.ToLower(); var newEmail = MemberEmail.Text.ToLower(); - var requireUniqueEmail = Membership.Provider.RequiresUniqueEmail; + var requireUniqueEmail = provider.RequiresUniqueEmail; var howManyMembersWithEmail = 0; var membersWithEmail = Member.GetMembersFromEmail(newEmail); @@ -256,11 +263,13 @@ namespace umbraco.cms.presentation.members private void ChangePassword(passwordChanger passwordChangerControl, MembershipUser membershipUser, CustomValidator passwordChangerValidator) { //Change the password + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); if (passwordChangerControl.IsChangingPassword) { var changePassResult = _membershipHelper.ChangePassword( - membershipUser.UserName, passwordChangerControl.ChangingPasswordModel, Membership.Provider); + membershipUser.UserName, passwordChangerControl.ChangingPasswordModel, provider); if (changePassResult.Success) { @@ -279,6 +288,7 @@ namespace umbraco.cms.presentation.members private bool UpdateWithMembershipProvider(MembershipUser membershipUser, string email, IDataType isApprovedDt, IDataType commentsDt, bool performUnlock) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); var membershipHelper = new MembershipHelper(ApplicationContext, new HttpContextWrapper(Context)); //set the writable properties that we are editing @@ -305,7 +315,7 @@ namespace umbraco.cms.presentation.members var unlockSuccess = false; if (performUnlock) { - unlockSuccess = Membership.Provider.UnlockUser(membershipUser.UserName); + unlockSuccess = provider.UnlockUser(membershipUser.UserName); if (unlockSuccess == false) { LogHelper.Warn("Could not unlock the member " + membershipUser.UserName); @@ -316,7 +326,7 @@ namespace umbraco.cms.presentation.members } } - return membershipHelper.UpdateMember(membershipUser, Membership.Provider, email, isApproved, comment: comments).Success + return membershipHelper.UpdateMember(membershipUser, provider, email, isApproved, comment: comments).Success || unlockSuccess; } @@ -339,6 +349,8 @@ namespace umbraco.cms.presentation.members protected void tmp_save(object sender, EventArgs e) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + Page.Validate(); if (Page.IsValid == false) { @@ -353,14 +365,14 @@ namespace umbraco.cms.presentation.members { // hide validation summaries - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { foreach (uicontrols.TabPage tp in _contentControl.GetPanels()) { tp.ErrorControl.Visible = false; } - var memberTypeProvider = (IUmbracoContentTypeMembershipProvider) Membership.Provider; + var memberTypeProvider = (IUmbracoMemberTypeMembershipProvider)provider; //update the membership provider var commentsProp = _contentControl.DataTypes.GetValue(memberTypeProvider.CommentPropertyTypeAlias); @@ -400,7 +412,7 @@ namespace umbraco.cms.presentation.members var passwordChangerValidator = (CustomValidator)MemberPasswordTxt.Controls[1].Controls[0].Controls[0]; ChangePassword(passwordChangerControl, _membershipUser, passwordChangerValidator); - if (Membership.Provider.IsUmbracoMembershipProvider()) + if (provider.IsUmbracoMembershipProvider()) { //Hrm, with the membership provider you cannot change the login name - I guess this will do that // in the underlying data layer 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 bc95326f49..305bd8bfee 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs @@ -14,7 +14,9 @@ namespace umbraco.presentation.umbraco.members { protected void Page_Load(object sender, EventArgs e) { - if (Membership.Provider.IsUmbracoMembershipProvider()) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider()) ButtonSearch.Text = ui.Text("search"); } @@ -22,8 +24,8 @@ namespace umbraco.presentation.umbraco.members protected void ButtonSearch_Click(object sender, EventArgs e) { resultsPane.Visible = true; - - if (Membership.Provider.IsUmbracoMembershipProvider()) + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + if (provider.IsUmbracoMembershipProvider()) { var query = searchQuery.Text.ToLower(); var internalSearcher = UmbracoContext.Current.InternalMemberSearchProvider; @@ -56,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() { @@ -68,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 e26847b1e7..562073ecf1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs @@ -6,44 +6,53 @@ 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 (Membership.Provider.IsUmbracoMembershipProvider()) + 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 (Membership.Provider.IsUmbracoMembershipProvider()) + 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"); @@ -61,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"); @@ -76,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 e0cf298d5d..7d614795bc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs @@ -300,7 +300,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/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/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index f7da73ff3f..adef77ea6f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -159,41 +159,46 @@ namespace umbraco.presentation.templateControls /// /// The item. protected virtual void ParseMacros(Item item) - { - - //Don't parse macros if there's a content item assigned since the content value - // converters take care of that - if (item.ContentItem != null) return; - + { // do nothing if the macros have already been rendered if (item.Controls.Count > 0) return; + + var elementText = GetFieldContents(item); - string elementText = GetFieldContents(item); - - using (DisposableTimer.DebugDuration("Parsing Macros")) + //Don't parse macros if there's a content item assigned since the content value + // converters take care of that, just add the already parsed text + if (item.ContentItem != null) { - - MacroTagParser.ParseMacros( - elementText, - - //callback for when a text block is parsed - textBlock => item.Controls.Add(new LiteralControl(textBlock)), - - //callback for when a macro is parsed: - (macroAlias, attributes) => - { - var macroControl = new Macro - { - Alias = macroAlias - }; - foreach (var i in attributes.Where(i => macroControl.Attributes[i.Key] == null)) - { - macroControl.Attributes.Add(i.Key, i.Value); - } - item.Controls.Add(macroControl); - }); + item.Controls.Add(new LiteralControl(elementText)); } + else + { + using (DisposableTimer.DebugDuration("Parsing Macros")) + { + + MacroTagParser.ParseMacros( + elementText, + + //callback for when a text block is parsed + textBlock => item.Controls.Add(new LiteralControl(textBlock)), + + //callback for when a macro is parsed: + (macroAlias, attributes) => + { + var macroControl = new Macro + { + Alias = macroAlias + }; + foreach (var i in attributes.Where(i => macroControl.Attributes[i.Key] == null)) + { + macroControl.Attributes.Add(i.Key, i.Value); + } + item.Controls.Add(macroControl); + }); + } + } + } /// 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 84b8f42e96..1f0262a1d0 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -73,15 +73,7 @@ namespace umbraco.cms.presentation.user 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 MembershipProviderExtensions.GetUsersMembershipProvider(); } } protected void Page_Load(object sender, EventArgs e) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs index 8a5308a24a..33818d43df 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/UserPermissions.cs @@ -80,14 +80,14 @@ namespace umbraco.cms.presentation.user //if permissions are to be assigned, then assign them if (permissions.Count > 0) { - ApplicationContext.Current.Services.UserService.AssignUserPermissions( + 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. - ApplicationContext.Current.Services.UserService.AssignUserPermissions( + ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( _user.Id, new[] { ActionNull.Instance.Letter }, allNodes.ToArray()); } 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/MediaUploader.ashx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs index e5902b34eb..97532cf590 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/MediaUploader.ashx.cs @@ -15,6 +15,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 { @@ -222,7 +223,7 @@ namespace umbraco.presentation.umbraco.webservices if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { - var mp = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; + var mp = MembershipProviderExtensions.GetUsersMembershipProvider(); if (mp != null && mp.ValidateUser(username, password)) { var user = new User(username); diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj index d334bcd566..b2fe3e2758 100644 --- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj +++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj @@ -39,7 +39,7 @@ False - ..\packages\Examine.0.1.53.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False @@ -85,6 +85,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 f159081ad4..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 7678890fee..94495d21d1 100644 --- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj +++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj @@ -39,7 +39,7 @@ False - ..\packages\Examine.0.1.53.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False @@ -86,6 +86,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 f159081ad4..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 5543ab3f49..a7e57efc14 100644 --- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj +++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj @@ -46,7 +46,7 @@ False - ..\packages\Examine.0.1.53.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll @@ -76,6 +76,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 0ea9c20ae7..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 b66daed73d..c9d8502076 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -82,7 +82,7 @@ False - ..\packages\Examine.0.1.53.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 1bb39ddebc..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/App.Config b/src/umbraco.MacroEngines/App.Config index 0e50bdd518..794c0925aa 100644 --- a/src/umbraco.MacroEngines/App.Config +++ b/src/umbraco.MacroEngines/App.Config @@ -1,4 +1,4 @@ - + @@ -10,12 +10,12 @@ - - + + - + @@ -26,11 +26,42 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 5ff63fec91..6a752cdb7a 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -645,36 +645,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 5d13bc89e8..7665569f18 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index 335ca62c2d..22e7c846d8 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -44,7 +44,7 @@ False - ..\packages\Examine.0.1.53.2941\lib\Examine.dll + ..\packages\Examine.0.1.55.2941\lib\Examine.dll False diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index 89ce9324f5..ef1b736d73 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -162,7 +162,7 @@ namespace umbraco.BusinessLogic } set { - _user.Password = value; + _user.RawPasswordValue = value; } } @@ -173,7 +173,7 @@ namespace umbraco.BusinessLogic public string GetPassword() { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.Password; + return _user.RawPasswordValue; } /// @@ -341,7 +341,7 @@ namespace umbraco.BusinessLogic public static User[] getAll() { int totalRecs; - var users = ApplicationContext.Current.Services.UserService.GetAllMembers( + var users = ApplicationContext.Current.Services.UserService.GetAll( 0, int.MaxValue, out totalRecs); return users.Select(x => new User(x)) @@ -390,14 +390,14 @@ namespace umbraco.BusinessLogic int totalRecs; if (useExactMatch) { - return ApplicationContext.Current.Services.UserService.FindMembersByEmail( + 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.FindMembersByEmail( + return ApplicationContext.Current.Services.UserService.FindByEmail( string.Format("%{0}%", email), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Wildcard) .Select(x => new User(x)) .ToArray(); @@ -430,14 +430,14 @@ namespace umbraco.BusinessLogic int totalRecs; if (partialMatch) { - return ApplicationContext.Current.Services.UserService.FindMembersByUsername( + return ApplicationContext.Current.Services.UserService.FindByUsername( string.Format("%{0}%", login), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Wildcard) .Select(x => new User(x)) .ToArray(); } else { - return ApplicationContext.Current.Services.UserService.FindMembersByUsername( + return ApplicationContext.Current.Services.UserService.FindByUsername( login, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact) .Select(x => new User(x)) .ToArray(); @@ -548,7 +548,7 @@ namespace umbraco.BusinessLogic public static int getUserId(string lname, string passw) { var found = ApplicationContext.Current.Services.UserService.GetByUsername(lname); - return found.Password == passw ? found.Id : -1; + return found.RawPasswordValue == passw ? found.Id : -1; } /// 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 4052d89cc4..5b4381e13d 100644 --- a/src/umbraco.cms/businesslogic/CMSNode.cs +++ b/src/umbraco.cms/businesslogic/CMSNode.cs @@ -377,6 +377,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. /// @@ -1016,6 +1029,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 /// diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index f2e52a1287..7dd0c264ae 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -39,13 +39,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) { } @@ -223,7 +228,7 @@ namespace umbraco.cms.businesslogic get { EnsureProperties(); - return m_LoadedProperties; + return _loadedProperties; } } @@ -236,7 +241,7 @@ namespace umbraco.cms.businesslogic get { EnsureProperties(); - return m_LoadedProperties.ToArray(); + return _loadedProperties.ToArray(); } } @@ -288,7 +293,7 @@ namespace umbraco.cms.businesslogic { EnsureProperties(); - return m_LoadedProperties.SingleOrDefault(x => x.PropertyType.Alias == alias); + return _loadedProperties.SingleOrDefault(x => x.PropertyType.Alias == alias); } /// @@ -300,7 +305,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); } /// @@ -462,6 +467,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. @@ -616,7 +636,7 @@ namespace umbraco.cms.businesslogic /// private void ClearLoadedProperties() { - m_LoadedProperties = null; + _loadedProperties = null; } /// @@ -624,7 +644,7 @@ namespace umbraco.cms.businesslogic /// private void EnsureProperties() { - if (m_LoadedProperties == null) + if (_loadedProperties == null) { InitializeProperties(); } @@ -643,13 +663,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; } @@ -658,7 +678,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"; @@ -697,7 +717,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/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/Permission.cs b/src/umbraco.cms/businesslogic/Permission.cs index 91fb96630e..ba0ad56b14 100644 --- a/src/umbraco.cms/businesslogic/Permission.cs +++ b/src/umbraco.cms/businesslogic/Permission.cs @@ -191,7 +191,7 @@ namespace umbraco.BusinessLogic [MethodImpl(MethodImplOptions.Synchronized)] public static void UpdateCruds(User user, CMSNode node, string permissions) { - ApplicationContext.Current.Services.UserService.AssignUserPermissions( + ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( user.Id, permissions.ToCharArray(), node.Id); diff --git a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs index a0aff986f5..7334a1b585 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs @@ -1,7 +1,9 @@ using System; using System.Collections; +using System.Data; using System.Linq; using Umbraco.Core.Cache; +using Umbraco.Core.Persistence.SqlSyntax; using umbraco.DataLayer; using System.Xml; using umbraco.cms.businesslogic.media; @@ -26,8 +28,9 @@ namespace umbraco.cms.businesslogic.datatype private static Guid _objectType = new Guid(Constants.ObjectTypes.DataType); private string _dbType; + private string _text1; - #endregion + #endregion #region Constructors @@ -46,6 +49,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 /// @@ -120,6 +130,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); } @@ -237,10 +264,17 @@ 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"); + } int newId = CMSNode.MakeNew(-1, _objectType, u.Id, 1, Text, UniqueId).Id; - cms.businesslogic.datatype.controls.Factory f = new cms.businesslogic.datatype.controls.Factory(); - + // initial control id changed to empty to ensure that it'll always work no matter if 3rd party configurators fail // ref: http://umbraco.codeplex.com/workitem/29788 Guid FirstcontrolId = Guid.Empty; diff --git a/src/umbraco.cms/businesslogic/language/Item.cs b/src/umbraco.cms/businesslogic/language/Item.cs index 6d59b45ffa..b0f2dcbb66 100644 --- a/src/umbraco.cms/businesslogic/language/Item.cs +++ b/src/umbraco.cms/businesslogic/language/Item.cs @@ -5,7 +5,8 @@ using System.Data; using System.Linq; using Umbraco.Core; - +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; using umbraco.DataLayer; using umbraco.BusinessLogic; using System.Collections.Generic; @@ -135,8 +136,9 @@ namespace umbraco.cms.businesslogic.language if (!hasText(key, languageId)) throw new ArgumentException("Key does not exist"); ApplicationContext.Current.DatabaseContext.Database.Update( - "set [value] = @value where LanguageId = @languageId And UniqueId = @key", - new { value = value, languageId = languageId, key = key }); + string.Format("set {0} = @value where LanguageId = @languageId And UniqueId = @key", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("value")), + new {value = value, languageId = languageId, key = key}); } /// diff --git a/src/umbraco.cms/businesslogic/media/MediaType.cs b/src/umbraco.cms/businesslogic/media/MediaType.cs index c597774472..5f00c9388a 100644 --- a/src/umbraco.cms/businesslogic/media/MediaType.cs +++ b/src/umbraco.cms/businesslogic/media/MediaType.cs @@ -91,12 +91,12 @@ namespace umbraco.cms.businesslogic.media /// The name of the MediaType /// The new MediaType [Obsolete("Obsolete, Use Umbraco.Core.Models.MediaType and Umbraco.Core.Services.ContentTypeService.Save()", false)] - public static MediaType MakeNew(BusinessLogic.User u, string Text) + public static MediaType MakeNew(User u, string Text) { return MakeNew(u, Text, -1); } - internal static MediaType MakeNew(BusinessLogic.User u, string text, int parentId) + internal static MediaType MakeNew(User u, string text, int parentId) { var mediaType = new Umbraco.Core.Models.MediaType(parentId) { Name = text, Alias = text, CreatorId = u.Id, Thumbnail = "folder.png", Icon = "folder.gif" }; ApplicationContext.Current.Services.ContentTypeService.Save(mediaType, u.Id); diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index b90d1e3eb0..c0d102613e 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -11,6 +11,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Css; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Querying; @@ -113,7 +114,7 @@ namespace umbraco.cms.businesslogic.member public static IEnumerable GetAllAsList() { int totalRecs; - return ApplicationContext.Current.Services.MemberService.GetAllMembers(0, int.MaxValue, out totalRecs) + return ApplicationContext.Current.Services.MemberService.GetAll(0, int.MaxValue, out totalRecs) .Select(x => new Member(x)) .ToArray(); } @@ -140,9 +141,14 @@ namespace umbraco.cms.businesslogic.member } } - return ApplicationContext.Current.Services.MemberService.GetAllMembers(ids.ToArray()) - .Select(x => new Member(x)) - .ToArray(); + if (ids.Any()) + { + return ApplicationContext.Current.Services.MemberService.GetAllMembers(ids.ToArray()) + .Select(x => new Member(x)) + .ToArray(); + } + + return new Member[] { }; } /// @@ -153,7 +159,8 @@ namespace umbraco.cms.businesslogic.member public static Member[] getMemberFromFirstLetter(char letter) { int totalRecs; - return ApplicationContext.Current.Services.MemberService.FindMembersByUsername( + + return ApplicationContext.Current.Services.MemberService.FindMembersByDisplayName( letter.ToString(CultureInfo.InvariantCulture), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.StartsWith) .Select(x => new Member(x)) .ToArray(); @@ -170,7 +177,7 @@ namespace umbraco.cms.businesslogic.member } else { - var found = ApplicationContext.Current.Services.MemberService.FindMembersByUsername( + var found = ApplicationContext.Current.Services.MemberService.FindByUsername( usernameToMatch, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.StartsWith); return found.Select(x => new Member(x)).ToArray(); } @@ -215,20 +222,21 @@ namespace umbraco.cms.businesslogic.member { if (mbt == null) throw new ArgumentNullException("mbt"); var loginName = (string.IsNullOrEmpty(LoginName) == false) ? LoginName : Name; - + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + //NOTE: This check is ONLY for backwards compatibility, this check shouldn't really be here it is up to the Membership provider // logic to deal with this but it was here before so we can't really change that. // Test for e-mail - if (Email != "" && GetMemberFromEmail(Email) != null && Membership.Providers[UmbracoMemberProviderName].RequiresUniqueEmail) + if (Email != "" && GetMemberFromEmail(Email) != null && provider.RequiresUniqueEmail) throw new Exception(string.Format("Duplicate Email! A member with the e-mail {0} already exists", Email)); if (GetMemberFromLoginName(loginName) != null) throw new Exception(string.Format("Duplicate User name! A member with the user name {0} already exists", loginName)); var model = ApplicationContext.Current.Services.MemberService.CreateMemberWithIdentity( - loginName, Email.ToLower(), "", mbt.MemberTypeItem); - model.Name = Name; + loginName, Email.ToLower(), Name, mbt.MemberTypeItem); - //The content object will only have the 'WasCancelled' flag set to 'True' if the 'Creating' event has been cancelled, so we return null. + //The content object will only have the 'WasCancelled' flag set to 'True' if the 'Saving' event has been cancelled, so we return null. if (((Entity)model).WasCancelled) return null; @@ -290,7 +298,7 @@ namespace umbraco.cms.businesslogic.member return null; int totalRecs; - var found = ApplicationContext.Current.Services.MemberService.FindMembersByEmail( + var found = ApplicationContext.Current.Services.MemberService.FindByEmail( email, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact); return found.Select(x => new Member(x)).ToArray(); @@ -309,8 +317,10 @@ namespace umbraco.cms.businesslogic.member { if (IsMember(loginName)) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + // validate user via provider - if (Membership.ValidateUser(loginName, password)) + if (provider.ValidateUser(loginName, password)) { return GetMemberFromLoginName(loginName); } @@ -348,7 +358,8 @@ namespace umbraco.cms.businesslogic.member [Obsolete("Use MembershipProviderExtensions.IsUmbracoMembershipProvider instead")] public static bool InUmbracoMemberMode() { - return Membership.Provider.IsUmbracoMembershipProvider(); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + return provider.IsUmbracoMembershipProvider(); } public static bool IsUsingUmbracoRoles() @@ -472,7 +483,7 @@ namespace umbraco.cms.businesslogic.member { get { - return MemberItem.Password; + return MemberItem.RawPasswordValue; } set { @@ -480,7 +491,8 @@ namespace umbraco.cms.businesslogic.member // To write directly to the db use the ChangePassword method // this is not pretty but nessecary due to a design flaw (the membership provider should have been a part of the cms project) var helper = new MemberShipHelper(); - MemberItem.Password = helper.EncodePassword(value, Membership.Provider.PasswordFormat); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + MemberItem.RawPasswordValue = helper.EncodePassword(value, provider.PasswordFormat); } } @@ -571,11 +583,13 @@ namespace umbraco.cms.businesslogic.member /// public override void Save() { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); //Due to backwards compatibility with this API we need to check for duplicate emails here if required. // This check should not be done here, as this logic is based on the MembershipProvider - var requireUniqueEmail = Membership.Providers[UmbracoMemberProviderName].RequiresUniqueEmail; + var requireUniqueEmail = provider.RequiresUniqueEmail; //check if there's anyone with this email in the db that isn't us - if (requireUniqueEmail && GetMembersFromEmail(Email).Any(x => x.Id != Id)) + var membersFromEmail = GetMembersFromEmail(Email); + if (requireUniqueEmail && membersFromEmail != null && membersFromEmail.Any(x => x.Id != Id)) { throw new Exception(string.Format("Duplicate Email! A member with the e-mail {0} already exists", Email)); } @@ -743,7 +757,7 @@ namespace umbraco.cms.businesslogic.member /// public void ChangePassword(string newPassword) { - Password = newPassword; + MemberItem.RawPasswordValue = newPassword; } /// @@ -752,7 +766,7 @@ namespace umbraco.cms.businesslogic.member /// public string GetPassword() { - return MemberItem.Password; + return MemberItem.RawPasswordValue; } /// @@ -1067,7 +1081,13 @@ namespace umbraco.cms.businesslogic.member // For backwards compatibility between umbraco members and .net membership if (HttpContext.Current.User.Identity.IsAuthenticated) { - int.TryParse(Membership.GetUser().ProviderUserKey.ToString(), out currentMemberId); + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var member = provider.GetCurrentUser(); + if (member == null) + { + throw new InvalidOperationException("No member object found with username " + provider.GetCurrentUserName()); + } + int.TryParse(member.ProviderUserKey.ToString(), out currentMemberId); } return currentMemberId; @@ -1083,8 +1103,15 @@ namespace umbraco.cms.businesslogic.member { if (HttpContext.Current.User.Identity.IsAuthenticated) { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + var member = provider.GetCurrentUser(); + if (member == null) + { + throw new InvalidOperationException("No member object found with username " + provider.GetCurrentUserName()); + } + int currentMemberId = 0; - if (int.TryParse(Membership.GetUser().ProviderUserKey.ToString(), out currentMemberId)) + if (int.TryParse(member.ProviderUserKey.ToString(), out currentMemberId)) { var m = new Member(currentMemberId); return m; diff --git a/src/umbraco.cms/businesslogic/web/Access.cs b/src/umbraco.cms/businesslogic/web/Access.cs index 555818952d..9e009e8b9d 100644 --- a/src/umbraco.cms/businesslogic/web/Access.cs +++ b/src/umbraco.cms/businesslogic/web/Access.cs @@ -1,641 +1,817 @@ using System; using System.Data; +using System.Globalization; +using System.Threading; +using System.Web; using System.Xml; +using System.Xml.Linq; using System.Xml.XPath; using System.Collections; using System.IO; using System.Web.Security; +using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Security; namespace umbraco.cms.businesslogic.web { - /// - /// Summary description for Access. - /// - public class Access - { - static private readonly Hashtable CheckedPages = new Hashtable(); + /// + /// Summary description for Access. + /// + public class Access + { + static private readonly Hashtable CheckedPages = new Hashtable(); - //must be volatile for double check lock to work - static private volatile XmlDocument _accessXmlContent; - static private string _accessXmlSource; + static private volatile XmlDocument _accessXmlContent; + static private string _accessXmlFilePath; + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); + private static readonly object LoadLocker = new object(); - private static void ClearCheckPages() - { - CheckedPages.Clear(); - } + [Obsolete("Do not access this property directly, it is not thread safe, use GetXmlDocumentCopy instead")] + public static XmlDocument AccessXml + { + get { return GetXmlDocument(); } + } - static readonly object Locko = new object(); - - public static XmlDocument AccessXml - { - get + //private method to initialize and return the in-memory xmldocument + private static XmlDocument GetXmlDocument() + { + if (_accessXmlContent == null) { - if (_accessXmlContent == null) + lock (LoadLocker) { - lock (Locko) + if (_accessXmlContent == null) { - if (_accessXmlContent == null) + if (_accessXmlFilePath == null) { - if (_accessXmlSource == null) - { - //if we pop it here it'll make for better stack traces ;) - _accessXmlSource = IOHelper.MapPath(SystemFiles.AccessXml, true); - } + //if we pop it here it'll make for better stack traces ;) + _accessXmlFilePath = IOHelper.MapPath(SystemFiles.AccessXml); + } - _accessXmlContent = new XmlDocument(); + _accessXmlContent = new XmlDocument(); - if (!System.IO.File.Exists(_accessXmlSource)) + if (File.Exists(_accessXmlFilePath) == false) + { + var file = new FileInfo(_accessXmlFilePath); + if (Directory.Exists(file.DirectoryName) == false) { - var file = new FileInfo(_accessXmlSource); - if (!Directory.Exists(file.DirectoryName)) - { - Directory.CreateDirectory(file.Directory.FullName); //ensure the folder exists! - } - System.IO.FileStream f = System.IO.File.Open(_accessXmlSource, FileMode.Create); - System.IO.StreamWriter sw = new StreamWriter(f); - sw.WriteLine(""); - sw.Close(); - f.Close(); + Directory.CreateDirectory(file.Directory.FullName); //ensure the folder exists! + } + var f = File.Open(_accessXmlFilePath, FileMode.Create); + var sw = new StreamWriter(f); + sw.WriteLine(""); + sw.Close(); + f.Close(); + } + _accessXmlContent.Load(_accessXmlFilePath); + } + } + } + return _accessXmlContent; + } + + //used by all other methods in this class to read and write the document which + // is thread safe and only clones once per request so it's still fast. + public static XmlDocument GetXmlDocumentCopy() + { + if (HttpContext.Current == null) + { + return (XmlDocument)GetXmlDocument().Clone(); + } + + if (HttpContext.Current.Items.Contains(typeof (Access)) == false) + { + HttpContext.Current.Items.Add(typeof (Access), GetXmlDocument().Clone()); + } + + return (XmlDocument)HttpContext.Current.Items[typeof(Access)]; + } + + #region Manipulation methods + + public static void AddMembershipRoleToDocument(int documentId, string role) + { + //event + var e = new AddMemberShipRoleToDocumentEventArgs(); + new Access().FireBeforeAddMemberShipRoleToDocument(new Document(documentId), role, e); + + if (e.Cancel) return; + + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(documentId); + + if (x == null) + throw new Exception("Document is not protected!"); + + if (x.SelectSingleNode("group [@id = '" + role + "']") == null) + { + var groupXml = (XmlElement)x.OwnerDocument.CreateNode(XmlNodeType.Element, "group", ""); + groupXml.SetAttribute("id", role); + x.AppendChild(groupXml); + Save(x.OwnerDocument); + } + } + + new Access().FireAfterAddMemberShipRoleToDocument(new Document(documentId), role, e); + } + + /// + /// Used to refresh cache among servers in an LB scenario + /// + /// + internal static void UpdateInMemoryDocument(XmlDocument newDoc) + { + //NOTE: This would be better to use our normal ReaderWriter lock but because we are emitting an + // event inside of the WriteLock and code can then listen to the event and call this method we end + // up in a dead-lock. This specifically happens in the PublicAccessCacheRefresher. + //So instead we use the load locker which is what is used for the static XmlDocument instance, we'll + // lock that, set the doc to null which will cause any reader threads to block for the AccessXml instance + // then save the doc and re-load it, then all blocked threads can carry on. + lock (LoadLocker) + { + _accessXmlContent = null; + //do a real clone + _accessXmlContent = new XmlDocument(); + _accessXmlContent.LoadXml(newDoc.OuterXml); + ClearCheckPages(); + } + } + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static void AddMemberGroupToDocument(int DocumentId, int MemberGroupId) + { + var x = (XmlElement)GetPage(DocumentId); + + if (x == null) + throw new Exception("Document is not protected!"); + + using (new WriteLock(Locker)) + { + if (x.SelectSingleNode("group [@id = '" + MemberGroupId + "']") == null) + { + var groupXml = (XmlElement)x.OwnerDocument.CreateNode(XmlNodeType.Element, "group", ""); + groupXml.SetAttribute("id", MemberGroupId.ToString(CultureInfo.InvariantCulture)); + x.AppendChild(groupXml); + Save(x.OwnerDocument); + } + } + } + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static void AddMemberToDocument(int DocumentId, int MemberId) + { + var x = (XmlElement)GetPage(DocumentId); + + if (x == null) + throw new Exception("Document is not protected!"); + + using (new WriteLock(Locker)) + { + if (x.Attributes.GetNamedItem("memberId") != null) + { + x.Attributes.GetNamedItem("memberId").Value = MemberId.ToString(CultureInfo.InvariantCulture); + } + else + { + x.SetAttribute("memberId", MemberId.ToString(CultureInfo.InvariantCulture)); + } + Save(x.OwnerDocument); + } + } + + public static void AddMembershipUserToDocument(int documentId, string membershipUserName) + { + //event + var e = new AddMembershipUserToDocumentEventArgs(); + new Access().FireBeforeAddMembershipUserToDocument(new Document(documentId), membershipUserName, e); + + if (e.Cancel) return; + + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(documentId); + + if (x == null) + throw new Exception("Document is not protected!"); + + if (x.Attributes.GetNamedItem("memberId") != null) + x.Attributes.GetNamedItem("memberId").Value = membershipUserName; + else + x.SetAttribute("memberId", membershipUserName); + Save(x.OwnerDocument); + } + + new Access().FireAfterAddMembershipUserToDocument(new Document(documentId), membershipUserName, e); + } + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static void RemoveMemberGroupFromDocument(int DocumentId, int MemberGroupId) + { + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(DocumentId); + + if (x == null) + throw new Exception("Document is not protected!"); + + var xGroup = x.SelectSingleNode("group [@id = '" + MemberGroupId + "']"); + if (xGroup == null) return; + + x.RemoveChild(xGroup); + Save(x.OwnerDocument); + } + } + + public static void RemoveMembershipRoleFromDocument(int documentId, string role) + { + var e = new RemoveMemberShipRoleFromDocumentEventArgs(); + new Access().FireBeforeRemoveMemberShipRoleFromDocument(new Document(documentId), role, e); + + if (e.Cancel) return; + + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(documentId); + if (x == null) + throw new Exception("Document is not protected!"); + var xGroup = x.SelectSingleNode("group [@id = '" + role + "']"); + + if (xGroup != null) + { + x.RemoveChild(xGroup); + Save(x.OwnerDocument); + } + } + + new Access().FireAfterRemoveMemberShipRoleFromDocument(new Document(documentId), role, e); + } + + public static bool RenameMemberShipRole(string oldRolename, string newRolename) + { + var hasChange = false; + if (oldRolename == newRolename) return false; + + using (new WriteLock(Locker)) + { + var xDoc = GetXmlDocumentCopy(); + oldRolename = oldRolename.Replace("'", "'"); + foreach (XmlNode x in xDoc.SelectNodes("//group [@id = '" + oldRolename + "']")) + { + x.Attributes["id"].Value = newRolename; + hasChange = true; + } + if (hasChange) + Save(xDoc); + } + + return hasChange; + } + + public static void ProtectPage(bool Simple, int DocumentId, int LoginDocumentId, int ErrorDocumentId) + { + var e = new AddProtectionEventArgs(); + new Access().FireBeforeAddProtection(new Document(DocumentId), e); + + if (e.Cancel) return; + + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(DocumentId); + + if (x == null) + { + var xDoc = GetXmlDocumentCopy(); + + x = (XmlElement)xDoc.CreateNode(XmlNodeType.Element, "page", ""); + x.OwnerDocument.DocumentElement.AppendChild(x); + } + // if using simple mode, make sure that all existing groups are removed + else if (Simple) + { + x.RemoveAll(); + } + x.SetAttribute("id", DocumentId.ToString()); + x.SetAttribute("loginPage", LoginDocumentId.ToString()); + x.SetAttribute("noRightsPage", ErrorDocumentId.ToString()); + x.SetAttribute("simple", Simple.ToString()); + Save(x.OwnerDocument); + ClearCheckPages(); + } + + new Access().FireAfterAddProtection(new Document(DocumentId), e); + } + + public static void RemoveProtection(int DocumentId) + { + //event + var e = new RemoveProtectionEventArgs(); + new Access().FireBeforeRemoveProtection(new Document(DocumentId), e); + + if (e.Cancel) return; + + using (new WriteLock(Locker)) + { + var x = (XmlElement)GetPage(DocumentId); + if (x == null) return; + + x.ParentNode.RemoveChild(x); + Save(x.OwnerDocument); + ClearCheckPages(); + } + + new Access().FireAfterRemoveProtection(new Document(DocumentId), e); + } + #endregion + + #region Reading methods + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static bool IsProtectedByGroup(int DocumentId, int GroupId) + { + bool isProtected = false; + + var d = new Document(DocumentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(DocumentId, d.Path)) + { + var currentNode = GetPage(GetProtectedPage(d.Path)); + if (currentNode.SelectSingleNode("./group [@id=" + GroupId + "]") != null) + { + isProtected = true; + } + } + } + + return isProtected; + } + + public static bool IsProtectedByMembershipRole(int documentId, string role) + { + bool isProtected = false; + + var d = new CMSNode(documentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(documentId, d.Path)) + { + var currentNode = GetPage(GetProtectedPage(d.Path)); + if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) + { + isProtected = true; + } + } + } + + return isProtected; + } + + public static string[] GetAccessingMembershipRoles(int documentId, string path) + { + var roles = new ArrayList(); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(documentId, path) == false) + return null; + + var currentNode = GetPage(GetProtectedPage(path)); + foreach (XmlNode n in currentNode.SelectNodes("./group")) + { + roles.Add(n.Attributes.GetNamedItem("id").Value); + } + } + + return (string[])roles.ToArray(typeof(string)); + } + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static member.MemberGroup[] GetAccessingGroups(int DocumentId) + { + var d = new Document(DocumentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(DocumentId, d.Path) == false) + return null; + + var currentNode = GetPage(GetProtectedPage(d.Path)); + var mg = new member.MemberGroup[currentNode.SelectNodes("./group").Count]; + int count = 0; + foreach (XmlNode n in currentNode.SelectNodes("./group")) + { + mg[count] = new member.MemberGroup(int.Parse(n.Attributes.GetNamedItem("id").Value)); + count++; + } + return mg; + } + + } + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static member.Member GetAccessingMember(int DocumentId) + { + var d = new Document(DocumentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(DocumentId, d.Path) == false) + return null; + + if (GetProtectionTypeInternal(DocumentId) != ProtectionType.Simple) + throw new Exception("Document isn't protected using Simple mechanism. Use GetAccessingMemberGroups instead"); + + var currentNode = GetPage(GetProtectedPage(d.Path)); + if (currentNode.Attributes.GetNamedItem("memberId") != null) + return new member.Member(int.Parse( + currentNode.Attributes.GetNamedItem("memberId").Value)); + } + + throw new Exception("Document doesn't contain a memberId. This might be caused if document is protected using umbraco RC1 or older."); + } + + public static MembershipUser GetAccessingMembershipUser(int documentId) + { + var d = new CMSNode(documentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(documentId, d.Path) == false) + return null; + + if (GetProtectionTypeInternal(documentId) != ProtectionType.Simple) + throw new Exception("Document isn't protected using Simple mechanism. Use GetAccessingMemberGroups instead"); + + var currentNode = GetPage(GetProtectedPage(d.Path)); + if (currentNode.Attributes.GetNamedItem("memberId") != null) + { + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + return provider.GetUser(currentNode.Attributes.GetNamedItem("memberId").Value, true); + } + } + + throw new Exception("Document doesn't contain a memberId. This might be caused if document is protected using umbraco RC1 or older."); + } + + + [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] + public static bool HasAccess(int DocumentId, member.Member Member) + { + bool hasAccess = false; + + var d = new Document(DocumentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(DocumentId, d.Path) == false) + { + hasAccess = true; + } + else + { + var currentNode = GetPage(GetProtectedPage(d.Path)); + if (Member != null) + { + var ide = Member.Groups.GetEnumerator(); + while (ide.MoveNext()) + { + var mg = (member.MemberGroup)ide.Value; + if (currentNode.SelectSingleNode("./group [@id=" + mg.Id.ToString() + "]") != null) + { + hasAccess = true; + break; } - _accessXmlContent.Load(_accessXmlSource); } } } - return _accessXmlContent; } - } - - public static void AddMembershipRoleToDocument(int documentId, string role) { - //event - AddMemberShipRoleToDocumentEventArgs e = new AddMemberShipRoleToDocumentEventArgs(); - new Access().FireBeforeAddMemberShipRoleToDocument(new Document(documentId), role, e); - - if (!e.Cancel) { - XmlElement x = (XmlElement)GetPage(documentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else { - if (x.SelectSingleNode("group [@id = '" + role + "']") == null) { - XmlElement groupXml = (XmlElement)AccessXml.CreateNode(XmlNodeType.Element, "group", ""); - groupXml.SetAttribute("id", role); - x.AppendChild(groupXml); - Save(); - } - } - - new Access().FireAfterAddMemberShipRoleToDocument(new Document(documentId), role, e); - } - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static void AddMemberGroupToDocument(int DocumentId, int MemberGroupId) - { - XmlElement x = (XmlElement) GetPage(DocumentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else - { - if (x.SelectSingleNode("group [@id = '" + MemberGroupId.ToString() + "']") == null) - { - XmlElement groupXml = (XmlElement) AccessXml.CreateNode(XmlNodeType.Element, "group", ""); - groupXml.SetAttribute("id", MemberGroupId.ToString()); - x.AppendChild(groupXml); - Save(); - } - } - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static void AddMemberToDocument(int DocumentId, int MemberId) - { - XmlElement x = (XmlElement) GetPage(DocumentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else - { - if (x.Attributes.GetNamedItem("memberId") != null) - x.Attributes.GetNamedItem("memberId").Value = MemberId.ToString(); - else - x.SetAttribute("memberId", MemberId.ToString()); - Save(); - } - } - - public static void AddMembershipUserToDocument(int documentId, string membershipUserName) { - //event - AddMembershipUserToDocumentEventArgs e = new AddMembershipUserToDocumentEventArgs(); - new Access().FireBeforeAddMembershipUserToDocument(new Document(documentId), membershipUserName, e); - - if (!e.Cancel) { - XmlElement x = (XmlElement)GetPage(documentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else { - if (x.Attributes.GetNamedItem("memberId") != null) - x.Attributes.GetNamedItem("memberId").Value = membershipUserName; - else - x.SetAttribute("memberId", membershipUserName); - Save(); - } - - new Access().FireAfterAddMembershipUserToDocument(new Document(documentId), membershipUserName, e); - } - - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static void RemoveMemberGroupFromDocument(int DocumentId, int MemberGroupId) - { - XmlElement x = (XmlElement) GetPage(DocumentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else - { - XmlNode xGroup = x.SelectSingleNode("group [@id = '" + MemberGroupId.ToString() + "']"); - if (xGroup != null) - { - x.RemoveChild(xGroup); - Save(); - } - } - } - - public static void RemoveMembershipRoleFromDocument(int documentId, string role) { - - RemoveMemberShipRoleFromDocumentEventArgs e = new RemoveMemberShipRoleFromDocumentEventArgs(); - new Access().FireBeforeRemoveMemberShipRoleFromDocument(new Document(documentId), role, e); - - if (!e.Cancel) { - XmlElement x = (XmlElement)GetPage(documentId); - - if (x == null) - throw new Exception("Document is not protected!"); - else { - XmlNode xGroup = x.SelectSingleNode("group [@id = '" + role + "']"); - if (xGroup != null) { - x.RemoveChild(xGroup); - Save(); - } - } - - new Access().FireAfterRemoveMemberShipRoleFromDocument(new Document(documentId), role, e); - } - } - - public static bool RenameMemberShipRole(string oldRolename, string newRolename) - { - bool hasChange = false; - if (oldRolename != newRolename) - { - oldRolename = oldRolename.Replace("'", "'"); - foreach (XmlNode x in AccessXml.SelectNodes("//group [@id = '" + oldRolename + "']")) - { - x.Attributes["id"].Value = newRolename; - hasChange = true; - } - if (hasChange) - Save(); - } - - return hasChange; - - } - - public static void ProtectPage(bool Simple, int DocumentId, int LoginDocumentId, int ErrorDocumentId) - { - AddProtectionEventArgs e = new AddProtectionEventArgs(); - new Access().FireBeforeAddProtection(new Document(DocumentId), e); - - if (!e.Cancel) { - - XmlElement x = (XmlElement)GetPage(DocumentId); - if (x == null) { - x = (XmlElement)_accessXmlContent.CreateNode(XmlNodeType.Element, "page", ""); - AccessXml.DocumentElement.AppendChild(x); - } - // if using simple mode, make sure that all existing groups are removed - else if (Simple) { - x.RemoveAll(); - } - x.SetAttribute("id", DocumentId.ToString()); - x.SetAttribute("loginPage", LoginDocumentId.ToString()); - x.SetAttribute("noRightsPage", ErrorDocumentId.ToString()); - x.SetAttribute("simple", Simple.ToString()); - Save(); - - ClearCheckPages(); - - new Access().FireAfterAddProtection(new Document(DocumentId), e); - } - } - - public static void RemoveProtection(int DocumentId) - { - XmlElement x = (XmlElement) GetPage(DocumentId); - if (x != null) - { - //event - RemoveProtectionEventArgs e = new RemoveProtectionEventArgs(); - new Access().FireBeforeRemoveProtection(new Document(DocumentId), e); - - if (!e.Cancel) { - - x.ParentNode.RemoveChild(x); - Save(); - ClearCheckPages(); - - new Access().FireAfterRemoveProtection(new Document(DocumentId), e); - } - - } - } - - private static void Save() - { - SaveEventArgs e = new SaveEventArgs(); - - new Access().FireBeforeSave(e); - - if (!e.Cancel) { - System.IO.FileStream f = System.IO.File.Open(_accessXmlSource, FileMode.Create); - AccessXml.Save(f); - f.Close(); - - new Access().FireAfterSave(e); - } - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static bool IsProtectedByGroup(int DocumentId, int GroupId) - { - bool isProtected = false; - - cms.businesslogic.web.Document d = new Document(DocumentId); - - if (!IsProtected(DocumentId, d.Path)) - isProtected = false; - else - { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - if (currentNode.SelectSingleNode("./group [@id=" + GroupId.ToString() + "]") != null) - { - isProtected = true; - } - } - - return isProtected; - } - - public static bool IsProtectedByMembershipRole(int documentId, string role) { - bool isProtected = false; - - CMSNode d = new CMSNode(documentId); - - if (!IsProtected(documentId, d.Path)) - isProtected = false; - else { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) { - isProtected = true; - } - } - - return isProtected; - } - - public static string[] GetAccessingMembershipRoles(int documentId, string path) { - ArrayList roles = new ArrayList(); - - if (!IsProtected(documentId, path)) - return null; - else { - XmlNode currentNode = GetPage(GetProtectedPage(path)); - foreach (XmlNode n in currentNode.SelectNodes("./group")) { - roles.Add(n.Attributes.GetNamedItem("id").Value); - } - return (string[])roles.ToArray(typeof(string)); - } - - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static cms.businesslogic.member.MemberGroup[] GetAccessingGroups(int DocumentId) - { - cms.businesslogic.web.Document d = new Document(DocumentId); - - if (!IsProtected(DocumentId, d.Path)) - return null; - else - { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - cms.businesslogic.member.MemberGroup[] mg = new umbraco.cms.businesslogic.member.MemberGroup[currentNode.SelectNodes("./group").Count]; - int count = 0; - foreach (XmlNode n in currentNode.SelectNodes("./group")) - { - mg[count] = new cms.businesslogic.member.MemberGroup(int.Parse(n.Attributes.GetNamedItem("id").Value)); - count++; - } - return mg; - } - - } - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static cms.businesslogic.member.Member GetAccessingMember(int DocumentId) { - cms.businesslogic.web.Document d = new Document(DocumentId); - - if (!IsProtected(DocumentId, d.Path)) - return null; - else if (GetProtectionType(DocumentId) != ProtectionType.Simple) - throw new Exception("Document isn't protected using Simple mechanism. Use GetAccessingMemberGroups instead"); - else { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - if (currentNode.Attributes.GetNamedItem("memberId") != null) - return new cms.businesslogic.member.Member(int.Parse( - currentNode.Attributes.GetNamedItem("memberId").Value)); - else - throw new Exception("Document doesn't contain a memberId. This might be caused if document is protected using umbraco RC1 or older."); - - } - - } - - public static MembershipUser GetAccessingMembershipUser(int documentId) { - CMSNode d = new CMSNode(documentId); - - if (!IsProtected(documentId, d.Path)) - return null; - else if (GetProtectionType(documentId) != ProtectionType.Simple) - throw new Exception("Document isn't protected using Simple mechanism. Use GetAccessingMemberGroups instead"); - else { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - if (currentNode.Attributes.GetNamedItem("memberId") != null) - return Membership.GetUser(currentNode.Attributes.GetNamedItem("memberId").Value); - else - throw new Exception("Document doesn't contain a memberId. This might be caused if document is protected using umbraco RC1 or older."); - - } - - } - - - [Obsolete("This method is no longer supported. Use the ASP.NET MemberShip methods instead", true)] - public static bool HasAccess(int DocumentId, cms.businesslogic.member.Member Member) - { - bool hasAccess = false; - - cms.businesslogic.web.Document d = new Document(DocumentId); - - if (!IsProtected(DocumentId, d.Path)) - hasAccess = true; - else - { - XmlNode currentNode = GetPage(GetProtectedPage(d.Path)); - if (Member != null) - { - IDictionaryEnumerator ide = Member.Groups.GetEnumerator(); - while(ide.MoveNext()) - { - cms.businesslogic.member.MemberGroup mg = (cms.businesslogic.member.MemberGroup) ide.Value; - if (currentNode.SelectSingleNode("./group [@id=" + mg.Id.ToString() + "]") != null) - { - hasAccess = true; - break; - } - } - } - } - - return hasAccess; - } - - public static bool HasAccces(int documentId, object memberId) { - bool hasAccess = false; - cms.businesslogic.CMSNode node = new CMSNode(documentId); - - if (!IsProtected(documentId, node.Path)) - return true; - else { - MembershipUser member = Membership.GetUser(memberId); - XmlNode currentNode = GetPage(GetProtectedPage(node.Path)); - - if (member != null) { - foreach(string role in Roles.GetRolesForUser()) { - if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) { - hasAccess = true; - break; - } - } - } - } - return hasAccess; - } - - public static bool HasAccess(int documentId, string path, MembershipUser member) { - bool hasAccess = false; - - if (!IsProtected(documentId, path)) - hasAccess = true; - else { - XmlNode currentNode = GetPage(GetProtectedPage(path)); - if (member != null) { - string[] roles = Roles.GetRolesForUser(member.UserName); - foreach(string role in roles) { - if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) { - hasAccess = true; - break; - } - } - } - } - - return hasAccess; - } - - public static ProtectionType GetProtectionType(int DocumentId) - { - XmlNode x = GetPage(DocumentId); - try - { - if (bool.Parse(x.Attributes.GetNamedItem("simple").Value)) - return ProtectionType.Simple; - else - return ProtectionType.Advanced; - } - catch - { - return ProtectionType.NotProtected; - } - - } - - public static bool IsProtected(int DocumentId, string Path) - { - bool isProtected = false; - - if (!CheckedPages.ContainsKey(DocumentId)) - { - foreach(string id in Path.Split(',')) - { - if (GetPage(int.Parse(id)) != null) - { - isProtected = true; - break; - } - } - - // Add thread safe updating to the hashtable - if (System.Web.HttpContext.Current != null) - System.Web.HttpContext.Current.Application.Lock(); - if (!CheckedPages.ContainsKey(DocumentId)) - CheckedPages.Add(DocumentId, isProtected); - if (System.Web.HttpContext.Current != null) - System.Web.HttpContext.Current.Application.UnLock(); - } - else - isProtected = (bool) CheckedPages[DocumentId]; - - return isProtected; - } - - public static int GetErrorPage(string Path) - { - return int.Parse(GetPage(GetProtectedPage(Path)).Attributes.GetNamedItem("noRightsPage").Value); - } - - public static int GetLoginPage(string Path) - { - return int.Parse(GetPage(GetProtectedPage(Path)).Attributes.GetNamedItem("loginPage").Value); - } - - private static int GetProtectedPage(string Path) - { - int protectedPage = 0; - - foreach(string id in Path.Split(',')) - if (GetPage(int.Parse(id)) != null) - protectedPage = int.Parse(id); - - return protectedPage; - } - - private static XmlNode GetPage(int documentId) - { - XmlNode x = AccessXml.SelectSingleNode("/access/page [@id=" + documentId.ToString() + "]"); - return x; - } - - - //Event delegates - public delegate void SaveEventHandler(Access sender, SaveEventArgs e); - - public delegate void AddProtectionEventHandler(Document sender, AddProtectionEventArgs e); - public delegate void RemoveProtectionEventHandler(Document sender, RemoveProtectionEventArgs e); - - public delegate void AddMemberShipRoleToDocumentEventHandler(Document sender, string role, AddMemberShipRoleToDocumentEventArgs e); - public delegate void RemoveMemberShipRoleFromDocumentEventHandler(Document sender, string role, RemoveMemberShipRoleFromDocumentEventArgs e); - - public delegate void RemoveMemberShipUserFromDocumentEventHandler(Document sender, string MembershipUserName, RemoveMemberShipUserFromDocumentEventArgs e); - public delegate void AddMembershipUserToDocumentEventHandler(Document sender, string MembershipUserName, AddMembershipUserToDocumentEventArgs e); - - //Events - - public static event SaveEventHandler BeforeSave; - protected virtual void FireBeforeSave(SaveEventArgs e) { - if (BeforeSave != null) - BeforeSave(this, e); - } - - public static event SaveEventHandler AfterSave; - protected virtual void FireAfterSave(SaveEventArgs e) { - if (AfterSave != null) - AfterSave(this, e); - } - - public static event AddProtectionEventHandler BeforeAddProtection; - protected virtual void FireBeforeAddProtection(Document doc, AddProtectionEventArgs e) { - if (BeforeAddProtection != null) - BeforeAddProtection(doc, e); - } - - public static event AddProtectionEventHandler AfterAddProtection; - protected virtual void FireAfterAddProtection(Document doc, AddProtectionEventArgs e) { - if (AfterAddProtection != null) - AfterAddProtection(doc, e); - } - - public static event RemoveProtectionEventHandler BeforeRemoveProtection; - protected virtual void FireBeforeRemoveProtection(Document doc, RemoveProtectionEventArgs e) { - if (BeforeRemoveProtection != null) - BeforeRemoveProtection(doc, e); - } - - public static event RemoveProtectionEventHandler AfterRemoveProtection; - protected virtual void FireAfterRemoveProtection(Document doc, RemoveProtectionEventArgs e) { - if (AfterRemoveProtection != null) - AfterRemoveProtection(doc, e); - } - - public static event AddMemberShipRoleToDocumentEventHandler BeforeAddMemberShipRoleToDocument; - protected virtual void FireBeforeAddMemberShipRoleToDocument(Document doc, string role, AddMemberShipRoleToDocumentEventArgs e) { - if (BeforeAddMemberShipRoleToDocument != null) - BeforeAddMemberShipRoleToDocument(doc, role, e); - } - - public static event AddMemberShipRoleToDocumentEventHandler AfterAddMemberShipRoleToDocument; - protected virtual void FireAfterAddMemberShipRoleToDocument(Document doc, string role, AddMemberShipRoleToDocumentEventArgs e) { - if (AfterAddMemberShipRoleToDocument != null) - AfterAddMemberShipRoleToDocument(doc, role, e); - } - - public static event RemoveMemberShipRoleFromDocumentEventHandler BeforeRemoveMemberShipRoleToDocument; - protected virtual void FireBeforeRemoveMemberShipRoleFromDocument(Document doc, string role, RemoveMemberShipRoleFromDocumentEventArgs e) { - if (BeforeRemoveMemberShipRoleToDocument != null) - BeforeRemoveMemberShipRoleToDocument(doc, role, e); - } - - public static event RemoveMemberShipRoleFromDocumentEventHandler AfterRemoveMemberShipRoleToDocument; - protected virtual void FireAfterRemoveMemberShipRoleFromDocument(Document doc, string role, RemoveMemberShipRoleFromDocumentEventArgs e) { - if (AfterRemoveMemberShipRoleToDocument != null) - AfterRemoveMemberShipRoleToDocument(doc, role, e); - } - - public static event RemoveMemberShipUserFromDocumentEventHandler BeforeRemoveMembershipUserFromDocument; - protected virtual void FireBeforeRemoveMembershipUserFromDocument(Document doc, string username, RemoveMemberShipUserFromDocumentEventArgs e) { - if (BeforeRemoveMembershipUserFromDocument != null) - BeforeRemoveMembershipUserFromDocument(doc, username, e); - } - - public static event RemoveMemberShipUserFromDocumentEventHandler AfterRemoveMembershipUserFromDocument; - protected virtual void FireAfterRemoveMembershipUserFromDocument(Document doc, string username, RemoveMemberShipUserFromDocumentEventArgs e) { - if (AfterRemoveMembershipUserFromDocument != null) - AfterRemoveMembershipUserFromDocument(doc, username, e); - } - - public static event AddMembershipUserToDocumentEventHandler BeforeAddMembershipUserToDocument; - protected virtual void FireBeforeAddMembershipUserToDocument(Document doc, string username, AddMembershipUserToDocumentEventArgs e) { - if (BeforeAddMembershipUserToDocument != null) - BeforeAddMembershipUserToDocument(doc, username, e); - } - - public static event AddMembershipUserToDocumentEventHandler AfterAddMembershipUserToDocument; - protected virtual void FireAfterAddMembershipUserToDocument(Document doc, string username, AddMembershipUserToDocumentEventArgs e) { - if (AfterAddMembershipUserToDocument != null) - AfterAddMembershipUserToDocument(doc, username, e); - } - } - - public enum ProtectionType - { - NotProtected, - Simple, - Advanced - } + + return hasAccess; + } + + public static bool HasAccces(int documentId, object memberId) + { + bool hasAccess = false; + var node = new CMSNode(documentId); + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(documentId, node.Path) == false) + return true; + + var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); + + var member = provider.GetUser(memberId, true); + var currentNode = GetPage(GetProtectedPage(node.Path)); + + if (member != null) + { + foreach (string role in Roles.GetRolesForUser()) + { + if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) + { + hasAccess = true; + break; + } + } + } + } + + return hasAccess; + } + + public static bool HasAccess(int documentId, string path, MembershipUser member) + { + bool hasAccess = false; + + using (new ReadLock(Locker)) + { + if (IsProtectedInternal(documentId, path) == false) + { + hasAccess = true; + } + else + { + XmlNode currentNode = GetPage(GetProtectedPage(path)); + if (member != null) + { + string[] roles = Roles.GetRolesForUser(member.UserName); + foreach (string role in roles) + { + if (currentNode.SelectSingleNode("./group [@id='" + role + "']") != null) + { + hasAccess = true; + break; + } + } + } + } + } + + return hasAccess; + } + + public static ProtectionType GetProtectionType(int DocumentId) + { + using (new ReadLock(Locker)) + { + XmlNode x = GetPage(DocumentId); + try + { + return bool.Parse(x.Attributes.GetNamedItem("simple").Value) + ? ProtectionType.Simple + : ProtectionType.Advanced; + } + catch + { + return ProtectionType.NotProtected; + } + } + + } + + public static bool IsProtected(int DocumentId, string Path) + { + using (new ReadLock(Locker)) + { + return IsProtectedInternal(DocumentId, Path); + } + } + + public static int GetErrorPage(string Path) + { + using (new ReadLock(Locker)) + { + return int.Parse(GetPage(GetProtectedPage(Path)).Attributes.GetNamedItem("noRightsPage").Value); + } + } + + public static int GetLoginPage(string Path) + { + using (new ReadLock(Locker)) + { + return int.Parse(GetPage(GetProtectedPage(Path)).Attributes.GetNamedItem("loginPage").Value); + } + } + #endregion + + private static ProtectionType GetProtectionTypeInternal(int DocumentId) + { + //NOTE: No locks here! the locking is done in callers to this method + + XmlNode x = GetPage(DocumentId); + try + { + return bool.Parse(x.Attributes.GetNamedItem("simple").Value) + ? ProtectionType.Simple + : ProtectionType.Advanced; + } + catch + { + return ProtectionType.NotProtected; + } + } + + private static bool IsProtectedInternal(int DocumentId, string Path) + { + //NOTE: No locks here! the locking is done in callers to this method + + bool isProtected = false; + + if (CheckedPages.ContainsKey(DocumentId) == false) + { + foreach (string id in Path.Split(',')) + { + if (GetPage(int.Parse(id)) != null) + { + isProtected = true; + break; + } + } + + if (CheckedPages.ContainsKey(DocumentId) == false) + { + CheckedPages.Add(DocumentId, isProtected); + } + } + else + { + isProtected = (bool)CheckedPages[DocumentId]; + } + + return isProtected; + } + + private static void Save(XmlDocument newDoc) + { + //NOTE: No locks here! the locking is done in callers to this method + + var e = new SaveEventArgs(); + + new Access().FireBeforeSave(e); + + if (e.Cancel) return; + + using (var f = File.Open(_accessXmlFilePath, FileMode.Create)) + { + newDoc.Save(f); + f.Close(); + //set the underlying in-mem object to null so it gets re-read + _accessXmlContent = null; + } + + new Access().FireAfterSave(e); + } + + private static int GetProtectedPage(string Path) + { + //NOTE: No locks here! the locking is done in callers to this method + + int protectedPage = 0; + + foreach (string id in Path.Split(',')) + if (GetPage(int.Parse(id)) != null) + protectedPage = int.Parse(id); + + return protectedPage; + } + + private static XmlNode GetPage(int documentId) + { + //NOTE: No locks here! the locking is done in callers to this method + var xDoc = GetXmlDocumentCopy(); + + var x = xDoc.SelectSingleNode("/access/page [@id=" + documentId + "]"); + return x; + } + + private static void ClearCheckPages() + { + CheckedPages.Clear(); + } + + //Event delegates + public delegate void SaveEventHandler(Access sender, SaveEventArgs e); + + public delegate void AddProtectionEventHandler(Document sender, AddProtectionEventArgs e); + public delegate void RemoveProtectionEventHandler(Document sender, RemoveProtectionEventArgs e); + + public delegate void AddMemberShipRoleToDocumentEventHandler(Document sender, string role, AddMemberShipRoleToDocumentEventArgs e); + public delegate void RemoveMemberShipRoleFromDocumentEventHandler(Document sender, string role, RemoveMemberShipRoleFromDocumentEventArgs e); + + public delegate void RemoveMemberShipUserFromDocumentEventHandler(Document sender, string MembershipUserName, RemoveMemberShipUserFromDocumentEventArgs e); + public delegate void AddMembershipUserToDocumentEventHandler(Document sender, string MembershipUserName, AddMembershipUserToDocumentEventArgs e); + + //Events + + public static event SaveEventHandler BeforeSave; + protected virtual void FireBeforeSave(SaveEventArgs e) + { + if (BeforeSave != null) + BeforeSave(this, e); + } + + public static event SaveEventHandler AfterSave; + protected virtual void FireAfterSave(SaveEventArgs e) + { + if (AfterSave != null) + AfterSave(this, e); + } + + public static event AddProtectionEventHandler BeforeAddProtection; + protected virtual void FireBeforeAddProtection(Document doc, AddProtectionEventArgs e) + { + if (BeforeAddProtection != null) + BeforeAddProtection(doc, e); + } + + public static event AddProtectionEventHandler AfterAddProtection; + protected virtual void FireAfterAddProtection(Document doc, AddProtectionEventArgs e) + { + if (AfterAddProtection != null) + AfterAddProtection(doc, e); + } + + public static event RemoveProtectionEventHandler BeforeRemoveProtection; + protected virtual void FireBeforeRemoveProtection(Document doc, RemoveProtectionEventArgs e) + { + if (BeforeRemoveProtection != null) + BeforeRemoveProtection(doc, e); + } + + public static event RemoveProtectionEventHandler AfterRemoveProtection; + protected virtual void FireAfterRemoveProtection(Document doc, RemoveProtectionEventArgs e) + { + if (AfterRemoveProtection != null) + AfterRemoveProtection(doc, e); + } + + public static event AddMemberShipRoleToDocumentEventHandler BeforeAddMemberShipRoleToDocument; + protected virtual void FireBeforeAddMemberShipRoleToDocument(Document doc, string role, AddMemberShipRoleToDocumentEventArgs e) + { + if (BeforeAddMemberShipRoleToDocument != null) + BeforeAddMemberShipRoleToDocument(doc, role, e); + } + + public static event AddMemberShipRoleToDocumentEventHandler AfterAddMemberShipRoleToDocument; + protected virtual void FireAfterAddMemberShipRoleToDocument(Document doc, string role, AddMemberShipRoleToDocumentEventArgs e) + { + if (AfterAddMemberShipRoleToDocument != null) + AfterAddMemberShipRoleToDocument(doc, role, e); + } + + public static event RemoveMemberShipRoleFromDocumentEventHandler BeforeRemoveMemberShipRoleToDocument; + protected virtual void FireBeforeRemoveMemberShipRoleFromDocument(Document doc, string role, RemoveMemberShipRoleFromDocumentEventArgs e) + { + if (BeforeRemoveMemberShipRoleToDocument != null) + BeforeRemoveMemberShipRoleToDocument(doc, role, e); + } + + public static event RemoveMemberShipRoleFromDocumentEventHandler AfterRemoveMemberShipRoleToDocument; + protected virtual void FireAfterRemoveMemberShipRoleFromDocument(Document doc, string role, RemoveMemberShipRoleFromDocumentEventArgs e) + { + if (AfterRemoveMemberShipRoleToDocument != null) + AfterRemoveMemberShipRoleToDocument(doc, role, e); + } + + public static event RemoveMemberShipUserFromDocumentEventHandler BeforeRemoveMembershipUserFromDocument; + protected virtual void FireBeforeRemoveMembershipUserFromDocument(Document doc, string username, RemoveMemberShipUserFromDocumentEventArgs e) + { + if (BeforeRemoveMembershipUserFromDocument != null) + BeforeRemoveMembershipUserFromDocument(doc, username, e); + } + + public static event RemoveMemberShipUserFromDocumentEventHandler AfterRemoveMembershipUserFromDocument; + protected virtual void FireAfterRemoveMembershipUserFromDocument(Document doc, string username, RemoveMemberShipUserFromDocumentEventArgs e) + { + if (AfterRemoveMembershipUserFromDocument != null) + AfterRemoveMembershipUserFromDocument(doc, username, e); + } + + public static event AddMembershipUserToDocumentEventHandler BeforeAddMembershipUserToDocument; + protected virtual void FireBeforeAddMembershipUserToDocument(Document doc, string username, AddMembershipUserToDocumentEventArgs e) + { + if (BeforeAddMembershipUserToDocument != null) + BeforeAddMembershipUserToDocument(doc, username, e); + } + + public static event AddMembershipUserToDocumentEventHandler AfterAddMembershipUserToDocument; + protected virtual void FireAfterAddMembershipUserToDocument(Document doc, string username, AddMembershipUserToDocumentEventArgs e) + { + if (AfterAddMembershipUserToDocument != null) + AfterAddMembershipUserToDocument(doc, username, e); + } + } + + public enum ProtectionType + { + NotProtected, + Simple, + Advanced + } } diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index b95a8402f7..51ecb20967 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -55,9 +55,8 @@ namespace umbraco.cms.businesslogic.web /// The id of the document /// The version of the document public Document(int id, Guid Version) - : base(id) + : base(id, Version) { - this.Version = Version; } /// diff --git a/src/umbraco.cms/businesslogic/web/Domain.cs b/src/umbraco.cms/businesslogic/web/Domain.cs index 4547078673..8c3e630285 100644 --- a/src/umbraco.cms/businesslogic/web/Domain.cs +++ b/src/umbraco.cms/businesslogic/web/Domain.cs @@ -143,7 +143,7 @@ namespace umbraco.cms.businesslogic.web return GetDomains(false); } - internal static IEnumerable GetDomains(bool includeWildcards) + public static IEnumerable GetDomains(bool includeWildcards) { var domains = ApplicationContext.Current.ApplicationCache.GetCacheItem( CacheKeys.DomainCacheKey, @@ -191,6 +191,11 @@ namespace umbraco.cms.businesslogic.web return GetDomains().Where(d => d._root == nodeId).ToArray(); } + public static Domain[] GetDomainsById(int nodeId, bool includeWildcards) + { + return GetDomains(includeWildcards).Where(d => d._root == nodeId).ToArray(); + } + public static bool Exists(string DomainName) { return GetDomain(DomainName) != null; diff --git a/src/umbraco.cms/packages.config b/src/umbraco.cms/packages.config index 4be7142dcc..fb9ebcc90d 100644 --- a/src/umbraco.cms/packages.config +++ b/src/umbraco.cms/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index 7a7df3b79e..f44c856634 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -1,4 +1,4 @@ - + Local @@ -106,7 +106,7 @@ False - ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll False @@ -432,6 +432,7 @@ + diff --git a/src/umbraco.controls/TreePicker/SimpleContentPicker.cs b/src/umbraco.controls/TreePicker/SimpleContentPicker.cs index 21adda53a6..82838b11dc 100644 --- a/src/umbraco.controls/TreePicker/SimpleContentPicker.cs +++ b/src/umbraco.controls/TreePicker/SimpleContentPicker.cs @@ -13,9 +13,9 @@ namespace umbraco.uicontrols.TreePicker { get { - if (HttpContext.Current != null && HttpContext.Current.Request.QueryString["id"] != null) + if ( Context.Request.QueryString["id"] != null) { - return TreeUrlGenerator.GetPickerUrl(Constants.Applications.Content, "content") + "&selected=" + HttpContext.Current.Request.QueryString["id"]; + return TreeUrlGenerator.GetPickerUrl(Constants.Applications.Content, "content") + "&selected=" + Context.Request.QueryString["id"]; } diff --git a/src/umbraco.controls/app.config b/src/umbraco.controls/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/umbraco.controls/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.controls/packages.config b/src/umbraco.controls/packages.config index 6471b6a3c4..52f6561bd3 100644 --- a/src/umbraco.controls/packages.config +++ b/src/umbraco.controls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.controls/umbraco.controls.csproj b/src/umbraco.controls/umbraco.controls.csproj index 6beea87cae..4a312cbc6e 100644 --- a/src/umbraco.controls/umbraco.controls.csproj +++ b/src/umbraco.controls/umbraco.controls.csproj @@ -1,4 +1,4 @@ - + Debug @@ -68,7 +68,7 @@ False - ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll @@ -165,6 +165,7 @@ + diff --git a/src/umbraco.editorControls/app.config b/src/umbraco.editorControls/app.config new file mode 100644 index 0000000000..568750da9c --- /dev/null +++ b/src/umbraco.editorControls/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.editorControls/dropdownlist/dropdown.cs b/src/umbraco.editorControls/dropdownlist/dropdown.cs index f4d887cfb4..2cc44ae6a9 100644 --- a/src/umbraco.editorControls/dropdownlist/dropdown.cs +++ b/src/umbraco.editorControls/dropdownlist/dropdown.cs @@ -59,7 +59,7 @@ namespace umbraco.editorControls { foreach (object key in _prevalues.Keys) { - this.Items.Add(new ListItem(dropdown.DictionaryReplace(_prevalues[key].ToString()), key.ToString())); + this.Items.Add(new ListItem(Dictionary.ReplaceKey(_prevalues[key].ToString()), key.ToString())); } } @@ -67,7 +67,7 @@ namespace umbraco.editorControls { foreach (KeyValuePair item in Prevalues) { - this.Items.Add(new ListItem(dropdown.DictionaryReplace(item.Value), item.Key.ToString())); + this.Items.Add(new ListItem(Dictionary.ReplaceKey(item.Value), item.Key.ToString())); } } @@ -76,26 +76,5 @@ namespace umbraco.editorControls if (_data != null && _data.Value != null) this.SelectedValue = _data.Value.ToString(); } - - static string DictionaryReplace(string text) - { - if (!text.StartsWith("#")) - return text; - else - { - Language lang = Language.GetByCultureCode(System.Threading.Thread.CurrentThread.CurrentCulture.Name); - if (lang != null) - { - if (Dictionary.DictionaryItem.hasKey(text.Substring(1, text.Length - 1))) - { - Dictionary.DictionaryItem di = new Dictionary.DictionaryItem(text.Substring(1, text.Length - 1)); - return di.Value(lang.id); - } - } - - return "[" + text + "]"; - } - } - } } \ No newline at end of file diff --git a/src/umbraco.editorControls/imagecropper/ImageInfo.cs b/src/umbraco.editorControls/imagecropper/ImageInfo.cs index 9207a71ed8..b2fe94365e 100644 --- a/src/umbraco.editorControls/imagecropper/ImageInfo.cs +++ b/src/umbraco.editorControls/imagecropper/ImageInfo.cs @@ -1,9 +1,11 @@ using System; using System.Drawing; using System.IO; +using System.Net; using System.Web; -using umbraco.editorControls.imagecropper; using Umbraco.Core.IO; +//using Umbraco.Core.IO; + namespace umbraco.editorControls.imagecropper { @@ -18,47 +20,34 @@ namespace umbraco.editorControls.imagecropper public string Path { get; set; } public string RelativePath { get; set; } + private readonly MediaFileSystem _fs; + public ImageInfo(string relativePath) { - RelativePath = relativePath; - Path = IOHelper.MapPath(relativePath); - if (File.Exists(Path)) + _fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + + try { - string fileName = Path.Substring(Path.LastIndexOf('\\') + 1); - Name = fileName.Substring(0, fileName.LastIndexOf('.')); + RelativePath = relativePath; - byte[] buffer = null; - - using (FileStream fs = new FileStream(Path, FileMode.Open, FileAccess.Read)) - { - buffer = new byte[fs.Length]; - fs.Read(buffer, 0, (int) fs.Length); - fs.Close(); - } - - try - { - image = Image.FromStream(new MemoryStream(buffer)); - - Width = image.Width; - Height = image.Height; - Aspect = (float) Width/Height; - DateStamp = File.GetLastWriteTime(Path); - } - catch (Exception) - { - Width = 0; - Height = 0; - Aspect = 0; - } + //This get's the IFileSystem's path based on the URL (i.e. /media/blah/blah.jpg ) + Path = _fs.GetRelativePath(relativePath); + image = Image.FromStream(_fs.OpenFile(Path)); + Name = _fs.GetFileName(Path); + DateStamp = _fs.GetLastModified(Path).Date; + Width = image.Width; + Height = image.Height; + Aspect = (float)Width / Height; + } - else + catch (Exception) { Width = 0; Height = 0; Aspect = 0; } + } public bool Exists @@ -66,10 +55,10 @@ namespace umbraco.editorControls.imagecropper get { return Width > 0 && Height > 0; } } - public string Directory - { - get { return Path.Substring(0, Path.LastIndexOf('\\')); } - } + //public string Directory + //{ + // get { return Path.Substring(0, Path.LastIndexOf('\\')); } + //} public void GenerateThumbnails(SaveData saveData, Config config) { @@ -77,18 +66,20 @@ namespace umbraco.editorControls.imagecropper { for (int i = 0; i < config.presets.Count; i++) { - Crop crop = (Crop) saveData.data[i]; - Preset preset = (Preset) config.presets[i]; + Crop crop = (Crop)saveData.data[i]; + Preset preset = (Preset)config.presets[i]; // Crop rectangle bigger than actual image - if(crop.X2 - crop.X > Width || crop.Y2 - crop.Y > Height) + if (crop.X2 - crop.X > Width || crop.Y2 - crop.Y > Height) { crop = preset.Fit(this); } + var tmpName = Name.Substring(0, Name.LastIndexOf('.')); + ImageTransform.Execute( Path, - String.Format("{0}_{1}", Name, preset.Name), + String.Format("{0}_{1}", tmpName, preset.Name), crop.X, crop.Y, crop.X2 - crop.X, diff --git a/src/umbraco.editorControls/imagecropper/ImageManipulation.cs b/src/umbraco.editorControls/imagecropper/ImageManipulation.cs index 9835f7b2cb..0e54b4aca1 100644 --- a/src/umbraco.editorControls/imagecropper/ImageManipulation.cs +++ b/src/umbraco.editorControls/imagecropper/ImageManipulation.cs @@ -2,34 +2,38 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing; +using Umbraco.Core.IO; using System.IO; + namespace umbraco.editorControls.imagecropper { public class ImageTransform { + private static readonly MediaFileSystem _fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + public static void Execute(string sourceFile, string name, int cropX, int cropY, int cropWidth, int cropHeight, int sizeWidth, int sizeHeight, long quality) { - if (!File.Exists(sourceFile)) return; - string path = sourceFile.Substring(0, sourceFile.LastIndexOf('\\')); + if (!_fs.FileExists(sourceFile)) return; + + + string path = string.Empty; + + //http or local filesystem + if (sourceFile.Contains("/")) + path = sourceFile.Substring(0, sourceFile.LastIndexOf('/')); + else + path = sourceFile.Substring(0, sourceFile.LastIndexOf('\\')); // TODO: Make configurable and move to imageInfo //if(File.Exists(String.Format(@"{0}\{1}.jpg", path, name))) return; - byte[] buffer = null; + //Do we need this check as we are always working with images that are already in a folder?? + //DirectoryInfo di = new DirectoryInfo(path); + //if (!di.Exists) di.Create(); - using (FileStream fs = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) - { - buffer = new byte[fs.Length]; - fs.Read(buffer, 0, (int)fs.Length); - fs.Close(); - } - - Image image = Image.FromStream(new MemoryStream(buffer)); - - DirectoryInfo di = new DirectoryInfo(path); - if (!di.Exists) di.Create(); + Image image = Image.FromStream(_fs.OpenFile(sourceFile)); using (Image croppedImage = CropImage(image, new Rectangle(cropX, cropY, cropWidth, cropHeight))) { @@ -57,7 +61,13 @@ namespace umbraco.editorControls.imagecropper EncoderParameters encoderParams = new EncoderParameters(1); encoderParams.Param[0] = qualityParam; - img.Save(path, jpegCodec, encoderParams); + var fileStream = new MemoryStream(); + + img.Save(fileStream, jpegCodec, encoderParams); + fileStream.Position = 0; + _fs.AddFile(path, fileStream, true); + //just to be sure + fileStream.Dispose(); } private static ImageCodecInfo GetEncoderInfo(string mimeType) diff --git a/src/umbraco.editorControls/packages.config b/src/umbraco.editorControls/packages.config index 6471b6a3c4..52f6561bd3 100644 --- a/src/umbraco.editorControls/packages.config +++ b/src/umbraco.editorControls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.editorControls/pagepicker/pagePicker.cs b/src/umbraco.editorControls/pagepicker/pagePicker.cs index 932fe4fb40..22074d11fc 100644 --- a/src/umbraco.editorControls/pagepicker/pagePicker.cs +++ b/src/umbraco.editorControls/pagepicker/pagePicker.cs @@ -22,15 +22,15 @@ namespace umbraco.editorControls { public pagePicker() : base() { } - public pagePicker(interfaces.IData data) : base(data) { } + public pagePicker(IData data) : base(data) { } public override string TreePickerUrl { get { - if (HttpContext.Current != null && HttpContext.Current.Request.QueryString["id"] != null) + if (Context.Request.QueryString["id"] != null) { - return TreeUrlGenerator.GetPickerUrl(Umbraco.Core.Constants.Applications.Content, "content") + "&selected=" + HttpContext.Current.Request.QueryString["id"]; + return TreeUrlGenerator.GetPickerUrl(Umbraco.Core.Constants.Applications.Content, "content") + "&selected=" + Context.Request.QueryString["id"]; } return TreeUrlGenerator.GetPickerUrl(Umbraco.Core.Constants.Applications.Content, "content"); diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj index da428aeede..09d22eef3a 100644 --- a/src/umbraco.editorControls/umbraco.editorControls.csproj +++ b/src/umbraco.editorControls/umbraco.editorControls.csproj @@ -114,7 +114,7 @@ False - ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll System @@ -486,6 +486,7 @@ + diff --git a/src/umbraco.macroRenderings/app.config b/src/umbraco.macroRenderings/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/umbraco.macroRenderings/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.macroRenderings/packages.config b/src/umbraco.macroRenderings/packages.config index 2d70f501ed..dfd94cd216 100644 --- a/src/umbraco.macroRenderings/packages.config +++ b/src/umbraco.macroRenderings/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj b/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj index 3c66e1bf2f..1bbbe4d52b 100644 --- a/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj +++ b/src/umbraco.macroRenderings/umbraco.macroRenderings.csproj @@ -1,4 +1,4 @@ - + Local @@ -106,7 +106,7 @@ False - ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll False @@ -218,6 +218,7 @@ + diff --git a/src/umbraco.providers/UsersMembershipProvider.cs b/src/umbraco.providers/UsersMembershipProvider.cs index 1561b991bc..5d315ccd52 100644 --- a/src/umbraco.providers/UsersMembershipProvider.cs +++ b/src/umbraco.providers/UsersMembershipProvider.cs @@ -55,7 +55,7 @@ namespace umbraco.providers public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null) throw new ArgumentNullException("config"); - if (string.IsNullOrEmpty(name)) name = "UsersMembershipProvider"; + if (string.IsNullOrEmpty(name)) name = UmbracoSettings.DefaultBackofficeProvider; base.Initialize(name, config); } diff --git a/src/umbraco.providers/app.config b/src/umbraco.providers/app.config new file mode 100644 index 0000000000..e72c720717 --- /dev/null +++ b/src/umbraco.providers/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.providers/members/MembershipEventHandler.cs b/src/umbraco.providers/members/MembershipEventHandler.cs index 7d8687f788..28e4b4dbdc 100644 --- a/src/umbraco.providers/members/MembershipEventHandler.cs +++ b/src/umbraco.providers/members/MembershipEventHandler.cs @@ -2,6 +2,7 @@ using System.Web.Security; using Umbraco.Core; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.member; +using Umbraco.Core.Security; namespace umbraco.providers.members { @@ -20,7 +21,7 @@ namespace umbraco.providers.members //This is a bit of a hack to ensure that the member is approved when created since many people will be using // this old api to create members on the front-end and they need to be approved - which is based on whether or not // the Umbraco membership provider is configured. - var provider = Membership.Provider as UmbracoMembershipProvider; + var provider = MembershipProviderExtensions.GetMembersMembershipProvider() as UmbracoMembershipProvider; if (provider != null) { var approvedField = provider.ApprovedPropertyTypeAlias; diff --git a/src/umbraco.providers/members/UmbracoMembershipProvider.cs b/src/umbraco.providers/members/UmbracoMembershipProvider.cs index 8f6a056712..ab3809db0a 100644 --- a/src/umbraco.providers/members/UmbracoMembershipProvider.cs +++ b/src/umbraco.providers/members/UmbracoMembershipProvider.cs @@ -8,10 +8,10 @@ using System.Configuration; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Security; using Umbraco.Core.Services; -using umbraco.BusinessLogic; using System.Security.Cryptography; using System.Web.Util; using System.Collections.Specialized; @@ -21,6 +21,7 @@ using System.Security.Permissions; using System.Runtime.CompilerServices; using Member = umbraco.cms.businesslogic.member.Member; using MemberType = umbraco.cms.businesslogic.member.MemberType; +using User = umbraco.BusinessLogic.User; #endregion @@ -30,7 +31,7 @@ namespace umbraco.providers.members /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) /// [Obsolete("This has been superceded by Umbraco.Web.Security.Providers.MembersMembershipProvider")] - public class UmbracoMembershipProvider : UmbracoMembershipProviderBase, IUmbracoContentTypeMembershipProvider + public class UmbracoMembershipProvider : UmbracoMembershipProviderBase, IUmbracoMemberTypeMembershipProvider { public UmbracoMembershipProvider() { @@ -48,7 +49,9 @@ namespace umbraco.providers.members #region Fields private string _defaultMemberTypeAlias = "Member"; - private string _providerName = Member.UmbracoMemberProviderName; + private string _providerName = Member.UmbracoMemberProviderName; + private volatile bool _hasDefaultMember = false; + private static readonly object Locker = new object(); #endregion @@ -110,7 +113,7 @@ namespace umbraco.providers.members // Intialize values from web.config if (config == null) throw new ArgumentNullException("config"); - if (string.IsNullOrEmpty(name)) name = "UmbracoMembershipProvider"; + if (string.IsNullOrEmpty(name)) name = Constants.Conventions.Member.UmbracoMemberProviderName; base.Initialize(name, config); @@ -118,11 +121,14 @@ namespace umbraco.providers.members // test for membertype (if not specified, choose the first member type available) if (config["defaultMemberTypeAlias"] != null) + { _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; - else if (MemberType.GetAll.Length == 1) - _defaultMemberTypeAlias = MemberType.GetAll[0].Alias; - else - throw new ProviderException("No default MemberType alias is specified in the web.config string. Please add a 'defaultMemberTypeAlias' to the add element in the provider declaration in web.config"); + if (_defaultMemberTypeAlias.IsNullOrWhiteSpace()) + { + throw new ProviderException("No default user type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config"); + } + _hasDefaultMember = true; + } // test for approve status if (config["umbracoApprovePropertyTypeAlias"] != null) @@ -233,7 +239,26 @@ namespace umbraco.providers.members public override string DefaultMemberTypeAlias { - get { return _defaultMemberTypeAlias; } + get + { + if (_hasDefaultMember == false) + { + lock (Locker) + { + if (_hasDefaultMember == false) + { + var types = MemberType.GetAll; + if (types.Length == 1) + _defaultMemberTypeAlias = types[0].Alias; + else + throw new ProviderException("No default MemberType alias is specified in the web.config string. Please add a 'defaultMemberTypeAlias' to the add element in the provider declaration in web.config"); + + _hasDefaultMember = true; + } + } + } + return _defaultMemberTypeAlias; + } } /// @@ -350,7 +375,7 @@ namespace umbraco.providers.members /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); + var byEmail = ApplicationContext.Current.Services.MemberService.FindByEmail(emailToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); var collection = new MembershipUserCollection(); foreach (var m in byEmail) @@ -372,7 +397,7 @@ namespace umbraco.providers.members /// public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByUsername(usernameToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); + var byEmail = ApplicationContext.Current.Services.MemberService.FindByUsername(usernameToMatch, pageIndex, pageSize, out totalRecords, StringPropertyMatchType.Wildcard).ToArray(); var collection = new MembershipUserCollection(); foreach (var m in byEmail) @@ -395,7 +420,7 @@ namespace umbraco.providers.members { var membersList = new MembershipUserCollection(); - var pagedMembers = ApplicationContext.Current.Services.MemberService.GetAllMembers(pageIndex, pageSize, out totalRecords); + var pagedMembers = ApplicationContext.Current.Services.MemberService.GetAll(pageIndex, pageSize, out totalRecords); foreach (var m in pagedMembers) { @@ -412,7 +437,7 @@ namespace umbraco.providers.members /// public override int GetNumberOfUsersOnline() { - return ApplicationContext.Current.Services.MemberService.GetMemberCount(MemberCountType.Online); + return ApplicationContext.Current.Services.MemberService.GetCount(MemberCountType.Online); } /// diff --git a/src/umbraco.providers/members/UmbracoRoleProvider.cs b/src/umbraco.providers/members/UmbracoRoleProvider.cs index bfb8863ed0..f87f960a79 100644 --- a/src/umbraco.providers/members/UmbracoRoleProvider.cs +++ b/src/umbraco.providers/members/UmbracoRoleProvider.cs @@ -14,13 +14,13 @@ using umbraco.cms.businesslogic.member; using System.Collections; #endregion -namespace umbraco.providers.members +namespace umbraco.providers.members { /// /// A role provider for members /// [Obsolete("This has been superceded by Umbraco.Web.Security.Providers.MembersRoleProvider")] - public class UmbracoRoleProvider : RoleProvider + public class UmbracoRoleProvider : RoleProvider { #region @@ -33,11 +33,14 @@ namespace umbraco.providers.members /// /// /// The name of the application to store and retrieve role information for. - public override string ApplicationName { - get { + public override string ApplicationName + { + get + { return _applicationName; } - set { + set + { if (string.IsNullOrEmpty(value)) throw new ProviderException("ApplicationName cannot be empty."); @@ -60,13 +63,15 @@ namespace umbraco.providers.members /// on a provider /// after the provider has already been initialized. /// The name of the provider has a length of zero. - public override void Initialize(string name, NameValueCollection config) { + public override void Initialize(string name, NameValueCollection config) + { // Initialize values from web.config if (config == null) throw new ArgumentNullException("config"); if (name == null || name.Length == 0) name = "UmbracoMemberRoleProvider"; - if (String.IsNullOrEmpty(config["description"])) { + if (String.IsNullOrEmpty(config["description"])) + { config.Remove("description"); config.Add("description", "Umbraco Member Role provider"); } @@ -86,15 +91,20 @@ namespace umbraco.providers.members /// /// A string array of user names to be added to the specified roles. /// A string array of the role names to add the specified user names to. - public override void AddUsersToRoles(string[] usernames, string[] roleNames) { + public override void AddUsersToRoles(string[] usernames, string[] roleNames) + { ArrayList roles = new ArrayList(); foreach (string role in roleNames) - try { + try + { roles.Add(MemberGroup.GetByName(role).Id); - } catch { + } + catch + { throw new ProviderException(String.Format("No role with name '{0}' exists", role)); } - foreach (string username in usernames) { + foreach (string username in usernames) + { Member m = Member.GetMemberFromLoginName(username); foreach (int roleId in roles) m.AddGroup(roleId); @@ -105,7 +115,8 @@ namespace umbraco.providers.members /// Adds a new role to the data source for the configured applicationName. /// /// The name of the role to create. - public override void CreateRole(string roleName) { + public override void CreateRole(string roleName) + { MemberGroup.MakeNew(roleName, User.GetUser(0)); } @@ -117,13 +128,15 @@ namespace umbraco.providers.members /// /// true if the role was successfully deleted; otherwise, false. /// - public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { + public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) + { MemberGroup group = MemberGroup.GetByName(roleName); if (group == null) throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); else if (throwOnPopulatedRole && group.GetMembersAsIds().Length > 0) throw new ProviderException(String.Format("Can't delete role '{0}', there are members assigned to the role", roleName)); - else { + else + { foreach (Member m in group.GetMembers()) m.RemoveGroup(group.Id); group.delete(); @@ -139,12 +152,14 @@ namespace umbraco.providers.members /// /// A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. /// - public override string[] FindUsersInRole(string roleName, string usernameToMatch) { + public override string[] FindUsersInRole(string roleName, string usernameToMatch) + { ArrayList members = new ArrayList(); MemberGroup group = MemberGroup.GetByName(roleName); if (group == null) throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); - else { + else + { foreach (Member m in group.GetMembers(usernameToMatch)) members.Add(m.LoginName); return (string[])members.ToArray(typeof(string)); @@ -157,7 +172,8 @@ namespace umbraco.providers.members /// /// A string array containing the names of all the roles stored in the data source for the configured applicationName. /// - public override string[] GetAllRoles() { + public override string[] GetAllRoles() + { ArrayList roles = new ArrayList(); foreach (MemberGroup mg in MemberGroup.GetAll) roles.Add(mg.Text); @@ -171,15 +187,18 @@ namespace umbraco.providers.members /// /// A string array containing the names of all the roles that the specified user is in for the configured applicationName. /// - public override string[] GetRolesForUser(string username) { + public override string[] GetRolesForUser(string username) + { ArrayList roles = new ArrayList(); Member m = Member.GetMemberFromLoginName(username); - if (m != null) { + if (m != null) + { IDictionaryEnumerator ide = m.Groups.GetEnumerator(); while (ide.MoveNext()) roles.Add(((MemberGroup)ide.Value).Text); return (string[])roles.ToArray(typeof(string)); - } else + } + else throw new ProviderException(String.Format("No member with username '{0}' exists", username)); } @@ -190,12 +209,14 @@ namespace umbraco.providers.members /// /// A string array containing the names of all the users who are members of the specified role for the configured applicationName. /// - public override string[] GetUsersInRole(string roleName) { + public override string[] GetUsersInRole(string roleName) + { ArrayList members = new ArrayList(); MemberGroup group = MemberGroup.GetByName(roleName); if (group == null) throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); - else { + else + { foreach (Member m in group.GetMembers()) members.Add(m.LoginName); return (string[])members.ToArray(typeof(string)); @@ -210,11 +231,13 @@ namespace umbraco.providers.members /// /// true if the specified user is in the specified role for the configured applicationName; otherwise, false. /// - public override bool IsUserInRole(string username, string roleName) { + public override bool IsUserInRole(string username, string roleName) + { Member m = Member.GetMemberFromLoginName(username); if (m == null) throw new ProviderException(String.Format("No user with name '{0}' exists", username)); - else { + else + { MemberGroup mg = MemberGroup.GetByName(roleName); if (mg == null) throw new ProviderException(String.Format("No Membergroup with name '{0}' exists", roleName)); @@ -228,15 +251,20 @@ namespace umbraco.providers.members /// /// A string array of user names to be removed from the specified roles. /// A string array of role names to remove the specified user names from. - public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { + public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) + { ArrayList roles = new ArrayList(); foreach (string role in roleNames) - try { + try + { roles.Add(MemberGroup.GetByName(role).Id); - } catch { + } + catch + { throw new ProviderException(String.Format("No role with name '{0}' exists", role)); } - foreach (string username in usernames) { + foreach (string username in usernames) + { Member m = Member.GetMemberFromLoginName(username); foreach (int roleId in roles) m.RemoveGroup(roleId); @@ -250,9 +278,10 @@ namespace umbraco.providers.members /// /// true if the role name already exists in the data source for the configured applicationName; otherwise, false. /// - public override bool RoleExists(string roleName) { - MemberGroup mg = MemberGroup.GetByName(roleName); - return mg != null; + public override bool RoleExists(string roleName) + { + MemberGroup mg = MemberGroup.GetByName(roleName); + return mg != null; } #endregion diff --git a/src/umbraco.providers/umbraco.providers.csproj b/src/umbraco.providers/umbraco.providers.csproj index e7e5e28b12..b1e7bf48d7 100644 --- a/src/umbraco.providers/umbraco.providers.csproj +++ b/src/umbraco.providers/umbraco.providers.csproj @@ -128,6 +128,9 @@ true + + +