From d94fe7df687ffed58ae96132c71b47da00981237 Mon Sep 17 00:00:00 2001 From: Alain Date: Mon, 5 Jan 2015 18:55:31 +0000 Subject: [PATCH 01/16] Update UserService.cs --- src/Umbraco.Core/Services/UserService.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 1a523835c8..c3dcf846dd 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -672,6 +672,27 @@ namespace Umbraco.Core.Services uow.Commit(); } } + + /// + /// Add a specific section to all users + /// + /// This is useful when a new section is created to allow all users accessing it + /// Alias of the section to add + public void AddSectionToAllUsers(string sectionAlias) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateUserRepository(uow)) + { + var allUsers = repository.GetAll(); + foreach (var user in allUsers.Where(u => !u.AllowedSections.Contains(sectionAlias))) + { + //now add the section for each user and commit + user.AddAllowedSection(sectionAlias); + repository.AddOrUpdate(user); + } + uow.Commit(); + } + } /// /// Get permissions set for a user and optional node ids @@ -745,4 +766,4 @@ namespace Umbraco.Core.Services /// public static event TypedEventHandler> DeletedUserType; } -} \ No newline at end of file +} From d06ff5f108281d9e31152be676cc2ecba78da29e Mon Sep 17 00:00:00 2001 From: Alain Date: Mon, 5 Jan 2015 19:02:32 +0000 Subject: [PATCH 02/16] Update IUserService.cs --- src/Umbraco.Core/Services/IUserService.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 559bf100d0..2c78bb62cc 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -53,6 +53,13 @@ namespace Umbraco.Core.Services /// Alias of the section to remove void DeleteSectionFromAllUsers(string sectionAlias); + /// + /// Add a specific section to all users + /// + /// This is useful when a new section is created to allow all users accessing it + /// Alias of the section to add + void AddSectionToAllUsers(string sectionAlias); + /// /// Get permissions set for a user and optional node ids /// @@ -117,4 +124,4 @@ namespace Umbraco.Core.Services #endregion } -} \ No newline at end of file +} From dea78c5d13129e1c7c81142c8e47bf93afb84ed2 Mon Sep 17 00:00:00 2001 From: Alain Date: Mon, 5 Jan 2015 19:04:59 +0000 Subject: [PATCH 03/16] Update UserServiceTests.cs --- .../Services/UserServiceTests.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 2a099c9001..c4fcbd8e88 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -451,6 +451,24 @@ namespace Umbraco.Tests.Services } + [Test] + public void Can_Add_Section_To_All_Users() + { + var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); + + var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType); + var user2 = ServiceContext.UserService.CreateUserWithIdentity("test2", "test2@test.com", userType); + + //now add the section to all users + ServiceContext.UserService.AddSectionToAllUsers("test"); + + //assert + var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); + var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + Assert.IsFalse(result1.AllowedSections.Contains("test")); + Assert.IsFalse(result2.AllowedSections.Contains("test")); + } + [Test] public void Get_By_Profile_Username() { @@ -512,4 +530,4 @@ namespace Umbraco.Tests.Services Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(0)); } } -} \ No newline at end of file +} From 93ca536b149e486f6f8450140cee9f6834b7ed2f Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 5 Jan 2015 21:20:50 +0100 Subject: [PATCH 04/16] Added noindex, nofollow to default page to discourage search engines indexing the back-office --- src/Umbraco.Web.UI/umbraco/Views/Default.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml index a7ef52979d..bef809a593 100644 --- a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml @@ -20,6 +20,7 @@ + Umbraco From d235340ec6a434ccd573605f758b012a7062eb34 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 5 Jan 2015 22:23:05 +0100 Subject: [PATCH 05/16] Populated NodeCount property for tag queries retrieving tags across all types --- .../Models/TaggableObjectTypes.cs | 1 + .../Persistence/Repositories/TagRepository.cs | 32 +++++++++++++------ src/Umbraco.Core/Services/TagService.cs | 9 +----- .../Repositories/TagRepositoryTest.cs | 8 ++++- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Core/Models/TaggableObjectTypes.cs b/src/Umbraco.Core/Models/TaggableObjectTypes.cs index be019410e5..ae54bc02bd 100644 --- a/src/Umbraco.Core/Models/TaggableObjectTypes.cs +++ b/src/Umbraco.Core/Models/TaggableObjectTypes.cs @@ -5,6 +5,7 @@ /// public enum TaggableObjectTypes { + All, Content, Media, Member diff --git a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs index 344d9f4c43..ac39d9556e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs @@ -168,8 +168,6 @@ namespace Umbraco.Core.Persistence.Repositories public IEnumerable GetTaggedEntitiesByTagGroup(TaggableObjectTypes objectType, string tagGroup) { - var nodeObjectType = GetNodeObjectType(objectType); - var sql = new Sql() .Select("cmsTagRelationship.nodeId, cmsPropertyType.Alias, cmsPropertyType.id as propertyTypeId, cmsTags.tag, cmsTags.id as tagId, cmsTags." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("group")) .From() @@ -181,17 +179,21 @@ namespace Umbraco.Core.Persistence.Repositories .On(left => left.Id, right => right.PropertyTypeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) - .Where(dto => dto.NodeObjectType == nodeObjectType) .Where(dto => dto.Group == tagGroup); + if (objectType != TaggableObjectTypes.All) + { + var nodeObjectType = GetNodeObjectType(objectType); + sql = sql + .Where(dto => dto.NodeObjectType == nodeObjectType); + } + return CreateTaggedEntityCollection( ApplicationContext.Current.DatabaseContext.Database.Fetch(sql)); } public IEnumerable GetTaggedEntitiesByTag(TaggableObjectTypes objectType, string tag, string tagGroup = null) { - var nodeObjectType = GetNodeObjectType(objectType); - var sql = new Sql() .Select("cmsTagRelationship.nodeId, cmsPropertyType.Alias, cmsPropertyType.id as propertyTypeId, cmsTags.tag, cmsTags.id as tagId, cmsTags." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("group")) .From() @@ -203,9 +205,15 @@ namespace Umbraco.Core.Persistence.Repositories .On(left => left.Id, right => right.PropertyTypeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) - .Where(dto => dto.NodeObjectType == nodeObjectType) .Where(dto => dto.Tag == tag); + if (objectType != TaggableObjectTypes.All) + { + var nodeObjectType = GetNodeObjectType(objectType); + sql = sql + .Where(dto => dto.NodeObjectType == nodeObjectType); + } + if (tagGroup.IsNullOrWhiteSpace() == false) { sql = sql.Where(dto => dto.Group == tagGroup); @@ -233,8 +241,6 @@ namespace Umbraco.Core.Persistence.Repositories public IEnumerable GetTagsForEntityType(TaggableObjectTypes objectType, string group = null) { - var nodeObjectType = GetNodeObjectType(objectType); - var sql = GetTagsQuerySelect(true); sql = ApplyRelationshipJoinToTagsQuery(sql); @@ -243,8 +249,14 @@ namespace Umbraco.Core.Persistence.Repositories .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(dto => dto.NodeObjectType == nodeObjectType); + .On(left => left.NodeId, right => right.NodeId); + + if (objectType != TaggableObjectTypes.All) + { + var nodeObjectType = GetNodeObjectType(objectType); + sql = sql + .Where(dto => dto.NodeObjectType == nodeObjectType); + } sql = ApplyGroupFilterToTagsQuery(sql, group); diff --git a/src/Umbraco.Core/Services/TagService.cs b/src/Umbraco.Core/Services/TagService.cs index cf477a125b..a1339ce360 100644 --- a/src/Umbraco.Core/Services/TagService.cs +++ b/src/Umbraco.Core/Services/TagService.cs @@ -133,14 +133,7 @@ namespace Umbraco.Core.Services { using (var repository = _repositoryFactory.CreateTagRepository(_uowProvider.GetUnitOfWork())) { - if (tagGroup.IsNullOrWhiteSpace()) - { - return repository.GetAll(); - } - - var query = Query.Builder.Where(x => x.Group == tagGroup); - var definitions = repository.GetByQuery(query); - return definitions; + return repository.GetTagsForEntityType(TaggableObjectTypes.All, tagGroup); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index bd35197193..c3524580dd 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -658,14 +658,20 @@ namespace Umbraco.Tests.Persistence.Repositories new[] { new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"} + new Tag {Text = "tag4", Group = "test1"} }, false); var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content).ToArray(); var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media).ToArray(); + var result3 = repository.GetTagsForEntityType(TaggableObjectTypes.All).ToArray(); Assert.AreEqual(3, result1.Count()); Assert.AreEqual(2, result2.Count()); + Assert.AreEqual(4, result3.Count()); + + Assert.AreEqual(1, result1.Single(x => x.Text == "tag1").NodeCount); + Assert.AreEqual(2, result3.Single(x => x.Text == "tag1").NodeCount); + Assert.AreEqual(1, result3.Single(x => x.Text == "tag4").NodeCount); } } From a378d0c202497f591fb43ff62a1635a3e8f61b4c Mon Sep 17 00:00:00 2001 From: Alain Date: Mon, 5 Jan 2015 21:30:22 +0000 Subject: [PATCH 06/16] Update UserServiceTests.cs --- src/Umbraco.Tests/Services/UserServiceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index c4fcbd8e88..a1591b44f8 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -465,8 +465,8 @@ namespace Umbraco.Tests.Services //assert var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); - Assert.IsFalse(result1.AllowedSections.Contains("test")); - Assert.IsFalse(result2.AllowedSections.Contains("test")); + Assert.IsTrue(result1.AllowedSections.Contains("test")); + Assert.IsTrue(result2.AllowedSections.Contains("test")); } [Test] From 91b599b213457f4539695fbecf114d8f73fdec66 Mon Sep 17 00:00:00 2001 From: Alain Date: Tue, 6 Jan 2015 12:14:13 +0000 Subject: [PATCH 07/16] Update IUserService.cs --- src/Umbraco.Core/Services/IUserService.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 2c78bb62cc..5a77956931 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -54,12 +54,13 @@ namespace Umbraco.Core.Services void DeleteSectionFromAllUsers(string sectionAlias); /// - /// Add a specific section to all users + /// Add a specific section to all users or those specified as parameters /// - /// This is useful when a new section is created to allow all users accessing it + /// This is useful when a new section is created to allow specific users accessing it /// Alias of the section to add - void AddSectionToAllUsers(string sectionAlias); - + /// Specifiying nothing will add the section to all user + void AddSectionToAllUsers(string sectionAlias, params int[] userIds); + /// /// Get permissions set for a user and optional node ids /// From cc61c34cdff6a7745f9c2f80063123d8222afc67 Mon Sep 17 00:00:00 2001 From: Alain Date: Tue, 6 Jan 2015 12:15:32 +0000 Subject: [PATCH 08/16] Update UserService.cs --- src/Umbraco.Core/Services/UserService.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index c3dcf846dd..9be1ce0d11 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -674,17 +674,26 @@ namespace Umbraco.Core.Services } /// - /// Add a specific section to all users + /// Add a specific section to all users or those specified as parameters /// - /// This is useful when a new section is created to allow all users accessing it + /// This is useful when a new section is created to allow specific users accessing it /// Alias of the section to add - public void AddSectionToAllUsers(string sectionAlias) + /// Specifiying nothing will add the section to all user + public void AddSectionToAllUsers(string sectionAlias, params int[] userIds) { var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) { - var allUsers = repository.GetAll(); - foreach (var user in allUsers.Where(u => !u.AllowedSections.Contains(sectionAlias))) + IEnumerable users; + if (userIds.Any()) + { + users = repository.GetAll(userIds); + } + else + { + users = repository.GetAll(); + } + foreach (var user in users.Where(u => !u.AllowedSections.InvariantContains(sectionAlias))) { //now add the section for each user and commit user.AddAllowedSection(sectionAlias); @@ -692,7 +701,7 @@ namespace Umbraco.Core.Services } uow.Commit(); } - } + } /// /// Get permissions set for a user and optional node ids From f3b23a15c995c60408d59da3e9d1b0a64d4288c2 Mon Sep 17 00:00:00 2001 From: Alain Date: Tue, 6 Jan 2015 12:18:08 +0000 Subject: [PATCH 09/16] Update UserServiceTests.cs --- .../Services/UserServiceTests.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index a1591b44f8..f3100aa133 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -458,17 +458,36 @@ namespace Umbraco.Tests.Services var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType); var user2 = ServiceContext.UserService.CreateUserWithIdentity("test2", "test2@test.com", userType); + var user3 = ServiceContext.UserService.CreateUserWithIdentity("test3", "test3@test.com", userType); + var user4 = ServiceContext.UserService.CreateUserWithIdentity("test4", "test4@test.com", userType); + + //now add the section to specific users + ServiceContext.UserService.AddSectionToAllUsers("test", (int)user1.Id, (int)user2.Id); + + //assert + var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); + var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + var result3 = ServiceContext.UserService.GetUserById((int)user3.Id); + var result4 = ServiceContext.UserService.GetUserById((int)user4.Id); + Assert.IsTrue(result1.AllowedSections.Contains("test")); + Assert.IsTrue(result2.AllowedSections.Contains("test")); + Assert.IsFalse(result3.AllowedSections.Contains("test")); + Assert.IsFalse(result4.AllowedSections.Contains("test")); //now add the section to all users ServiceContext.UserService.AddSectionToAllUsers("test"); //assert - var result1 = ServiceContext.UserService.GetUserById((int)user1.Id); - var result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + result1 = ServiceContext.UserService.GetUserById((int)user1.Id); + result2 = ServiceContext.UserService.GetUserById((int)user2.Id); + result3 = ServiceContext.UserService.GetUserById((int)user3.Id); + result4 = ServiceContext.UserService.GetUserById((int)user4.Id); Assert.IsTrue(result1.AllowedSections.Contains("test")); Assert.IsTrue(result2.AllowedSections.Contains("test")); + Assert.IsTrue(result3.AllowedSections.Contains("test")); + Assert.IsTrue(result4.AllowedSections.Contains("test")); } - + [Test] public void Get_By_Profile_Username() { From 341239648d0b6051339a798275fbf8f3acbbfc29 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 09:17:22 +1100 Subject: [PATCH 10/16] bumps version --- build/UmbracoVersion.txt | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 85a02bb894..654fc8d0eb 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,2 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.2.1 \ No newline at end of file +7.2.2 \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index f7163bfe7f..dfc8d6931e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.2.1"); + private static readonly Version Version = new Version("7.2.2"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 69ff45cfe8..3cffa798c0 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2551,7 +2551,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" True 7210 / - http://localhost:7210 + http://localhost:7220 False False From d4f28fcd851adf0e9c1600294c81b7867d4bf5e6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 10:39:00 +1100 Subject: [PATCH 11/16] fixes a couple of tests --- src/Umbraco.Core/Services/LocalizedTextService.cs | 13 +++++++++---- .../Services/LocalizedTextServiceFileSources.cs | 14 +++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/LocalizedTextService.cs b/src/Umbraco.Core/Services/LocalizedTextService.cs index f5c2cc033a..c9d05f3a3b 100644 --- a/src/Umbraco.Core/Services/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/LocalizedTextService.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Xml; using System.Xml.Linq; using System.Xml.XPath; +using Umbraco.Core.Logging; namespace Umbraco.Core.Services { @@ -80,7 +81,8 @@ namespace Umbraco.Core.Services { if (_xmlSource.ContainsKey(culture) == false) { - throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture); + return result; } //convert all areas + keys to a single key with a '/' @@ -103,7 +105,8 @@ namespace Umbraco.Core.Services { if (_dictionarySource.ContainsKey(culture) == false) { - throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture); + return result; } //convert all areas + keys to a single key with a '/' @@ -137,7 +140,8 @@ namespace Umbraco.Core.Services { if (_dictionarySource.ContainsKey(culture) == false) { - throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture); + return "[" + key + "]"; } var cultureSource = _dictionarySource[culture]; @@ -174,7 +178,8 @@ namespace Umbraco.Core.Services { if (_xmlSource.ContainsKey(culture) == false) { - throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + LogHelper.Warn("The culture specified {0} was not found in any configured sources for this service", () => culture); + return "[" + key + "]"; } var cultureSource = _xmlSource[culture].Value; diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs index 147a3cda31..30e43a694c 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceFileSources.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Xml.Linq; using Umbraco.Core.Cache; +using Umbraco.Core.Logging; namespace Umbraco.Core.Services { @@ -20,7 +21,15 @@ namespace Umbraco.Core.Services if (cache == null) throw new ArgumentNullException("cache"); if (fileSourceFolder == null) throw new ArgumentNullException("fileSourceFolder"); _cache = cache; - _fileSourceFolder = fileSourceFolder; + + if (fileSourceFolder.Exists == false) + { + LogHelper.Warn("The folder does not exist: {0}, therefore no sources will be discovered", () => fileSourceFolder.FullName); + } + else + { + _fileSourceFolder = fileSourceFolder; + } } /// @@ -30,6 +39,9 @@ namespace Umbraco.Core.Services public IDictionary> GetXmlSources() { var result = new Dictionary>(); + + if (_fileSourceFolder == null) return result; + foreach (var fileInfo in _fileSourceFolder.GetFiles("*.xml")) { var localCopy = fileInfo; From 282550f402f3262bb19744d966dda85013aa768e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 13:17:44 +1100 Subject: [PATCH 12/16] Fixes: U4-6080 Server side email validation doesn't occur on existing members when they are saved --- .../CoreStrings/StringExtensionsTests.cs | 2 +- .../CoreStrings/StringValidationTests.cs | 28 ++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Web/Editors/MemberController.cs | 1 - ...ershipProviderValidationFilterAttribute.cs | 129 ----------------- src/Umbraco.Web/Umbraco.Web.csproj | 1 - .../WebApi/Binders/MemberBinder.cs | 134 ++++++++++++++++++ 7 files changed, 164 insertions(+), 132 deletions(-) create mode 100644 src/Umbraco.Tests/CoreStrings/StringValidationTests.cs delete mode 100644 src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs diff --git a/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs b/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs index c86ee01100..43e5d0fcf9 100644 --- a/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs @@ -7,7 +7,7 @@ using Umbraco.Core.ObjectResolution; namespace Umbraco.Tests.CoreStrings { - [TestFixture] + [TestFixture] public class StringExtensionsTests { [SetUp] diff --git a/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs b/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs new file mode 100644 index 0000000000..c1f8b92438 --- /dev/null +++ b/src/Umbraco.Tests/CoreStrings/StringValidationTests.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using NUnit.Framework; + +namespace Umbraco.Tests.CoreStrings +{ + [TestFixture] + public class StringValidationTests + { + [Test] + public void Validate_Email_Address() + { + var foo = new EmailAddressAttribute(); + + Assert.IsTrue(foo.IsValid("someone@somewhere.com")); + Assert.IsTrue(foo.IsValid("someone@somewhere.co.uk")); + Assert.IsTrue(foo.IsValid("someone+tag@somewhere.net")); + Assert.IsTrue(foo.IsValid("futureTLD@somewhere.fooo")); + + Assert.IsTrue(foo.IsValid("abc@xyz.financial")); + + Assert.IsFalse(foo.IsValid("fdsa")); + Assert.IsFalse(foo.IsValid("fdsa@")); + Assert.IsFalse(foo.IsValid("fdsa@fdsa")); + Assert.IsFalse(foo.IsValid("fdsa@fdsa.")); + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index b6f562f106..60e57f91f0 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -168,6 +168,7 @@ + diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index a696ccf0b1..30320b278e 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -230,7 +230,6 @@ namespace Umbraco.Web.Editors /// /// [FileUploadCleanupFilter] - [MembershipProviderValidationFilter] public MemberDisplay PostSave( [ModelBinder(typeof(MemberBinder))] MemberSave contentItem) diff --git a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs b/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs deleted file mode 100644 index aa95af3531..0000000000 --- a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Net; -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Filters; -using System.Web.Security; -using Umbraco.Core; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Editors -{ - /// - /// This validates the submitted data in regards to the current membership provider - /// - internal class MembershipProviderValidationFilterAttribute : ActionFilterAttribute - { - public override void OnActionExecuting(HttpActionContext actionContext) - { - base.OnActionExecuting(actionContext); - - //default provider! - var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - - var contentItem = (MemberSave) actionContext.ActionArguments["contentItem"]; - - var validEmail = ValidateUniqueEmail(contentItem, membershipProvider, actionContext); - if (validEmail == false) - { - actionContext.ModelState.AddPropertyError( - new ValidationResult("Email address is already in use", new[] {"value"}), - string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - } - - var validLogin = ValidateUniqueLogin(contentItem, membershipProvider, actionContext); - if (validLogin == false) - { - actionContext.ModelState.AddPropertyError( - new ValidationResult("Username is already in use", new[] { "value" }), - string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - } - } - - internal bool ValidateUniqueLogin(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext) - { - if (contentItem == null) throw new ArgumentNullException("contentItem"); - if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); - - int totalRecs; - var existingByName = membershipProvider.FindUsersByName(contentItem.Username.Trim(), 0, int.MaxValue, out totalRecs); - switch (contentItem.Action) - { - case ContentSaveAction.Save: - - //ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ? - if (contentItem.PersistedContent.Username.InvariantEquals(contentItem.Username.Trim()) == false) - { - //they are changing their login name - if (existingByName.Cast().Select(x => x.UserName) - .Any(x => x == contentItem.Username.Trim())) - { - //the user cannot use this login - return false; - } - } - break; - case ContentSaveAction.SaveNew: - //check if the user's login already exists - if (existingByName.Cast().Select(x => x.UserName) - .Any(x => x == contentItem.Username.Trim())) - { - //the user cannot use this login - return false; - } - break; - default: - //we don't support this for members - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - return true; - } - - internal bool ValidateUniqueEmail(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext) - { - if (contentItem == null) throw new ArgumentNullException("contentItem"); - if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); - - if (membershipProvider.RequiresUniqueEmail == false) - { - return true; - } - - int totalRecs; - var existingByEmail = membershipProvider.FindUsersByEmail(contentItem.Email.Trim(), 0, int.MaxValue, out totalRecs); - switch (contentItem.Action) - { - case ContentSaveAction.Save: - //ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ? - if (contentItem.PersistedContent.Email.InvariantEquals(contentItem.Email.Trim()) == false) - { - //they are changing their email - if (existingByEmail.Cast().Select(x => x.Email) - .Any(x => x.InvariantEquals(contentItem.Email.Trim()))) - { - //the user cannot use this email - return false; - } - } - break; - case ContentSaveAction.SaveNew: - //check if the user's email already exists - if (existingByEmail.Cast().Select(x => x.Email) - .Any(x => x.InvariantEquals(contentItem.Email.Trim()))) - { - //the user cannot use this email - return false; - } - break; - default: - //we don't support this for members - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - return true; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 21ac991bbc..2dec8c27f6 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -397,7 +397,6 @@ - diff --git a/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs b/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs index 3893167a73..49669fd427 100644 --- a/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; using System.Web.Security; @@ -12,6 +15,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.WebApi.Filters; using System.Linq; using Umbraco.Core.Models.Membership; +using Umbraco.Web; namespace Umbraco.Web.WebApi.Binders { @@ -194,6 +198,52 @@ namespace Umbraco.Web.WebApi.Binders /// internal class MemberValidationHelper : ContentItemValidationHelper { + /// + /// We need to manually validate a few things here like email and login to make sure they are valid and aren't duplicates + /// + /// + /// + /// + protected override bool ValidatePropertyData(ContentItemBasic postedItem, HttpActionContext actionContext) + { + var memberSave = (MemberSave)postedItem; + + if (memberSave.Username.IsNullOrWhiteSpace()) + { + actionContext.ModelState.AddPropertyError( + new ValidationResult("Invalid user name", new[] { "value" }), + string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + if (memberSave.Email.IsNullOrWhiteSpace() || new EmailAddressAttribute().IsValid(memberSave.Email) == false) + { + actionContext.ModelState.AddPropertyError( + new ValidationResult("Invalid email", new[] { "value" }), + string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + //default provider! + var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + + var validEmail = ValidateUniqueEmail(memberSave, membershipProvider, actionContext); + if (validEmail == false) + { + actionContext.ModelState.AddPropertyError( + new ValidationResult("Email address is already in use", new[] { "value" }), + string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + var validLogin = ValidateUniqueLogin(memberSave, membershipProvider, actionContext); + if (validLogin == false) + { + actionContext.ModelState.AddPropertyError( + new ValidationResult("Username is already in use", new[] { "value" }), + string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + return base.ValidatePropertyData(postedItem, actionContext); + } + protected override bool ValidateProperties(ContentItemBasic postedItem, HttpActionContext actionContext) { var propertiesToValidate = postedItem.Properties.ToList(); @@ -206,6 +256,90 @@ namespace Umbraco.Web.WebApi.Binders return ValidateProperties(propertiesToValidate.ToArray(), postedItem.PersistedContent.Properties.ToArray(), actionContext); } + + internal bool ValidateUniqueLogin(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext) + { + if (contentItem == null) throw new ArgumentNullException("contentItem"); + if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + + int totalRecs; + var existingByName = membershipProvider.FindUsersByName(contentItem.Username.Trim(), 0, int.MaxValue, out totalRecs); + switch (contentItem.Action) + { + case ContentSaveAction.Save: + + //ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ? + if (contentItem.PersistedContent.Username.InvariantEquals(contentItem.Username.Trim()) == false) + { + //they are changing their login name + if (existingByName.Cast().Select(x => x.UserName) + .Any(x => x == contentItem.Username.Trim())) + { + //the user cannot use this login + return false; + } + } + break; + case ContentSaveAction.SaveNew: + //check if the user's login already exists + if (existingByName.Cast().Select(x => x.UserName) + .Any(x => x == contentItem.Username.Trim())) + { + //the user cannot use this login + return false; + } + break; + default: + //we don't support this for members + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + return true; + } + + internal bool ValidateUniqueEmail(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext) + { + if (contentItem == null) throw new ArgumentNullException("contentItem"); + if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + + if (membershipProvider.RequiresUniqueEmail == false) + { + return true; + } + + int totalRecs; + var existingByEmail = membershipProvider.FindUsersByEmail(contentItem.Email.Trim(), 0, int.MaxValue, out totalRecs); + switch (contentItem.Action) + { + case ContentSaveAction.Save: + //ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ? + if (contentItem.PersistedContent.Email.InvariantEquals(contentItem.Email.Trim()) == false) + { + //they are changing their email + if (existingByEmail.Cast().Select(x => x.Email) + .Any(x => x.InvariantEquals(contentItem.Email.Trim()))) + { + //the user cannot use this email + return false; + } + } + break; + case ContentSaveAction.SaveNew: + //check if the user's email already exists + if (existingByEmail.Cast().Select(x => x.Email) + .Any(x => x.InvariantEquals(contentItem.Email.Trim()))) + { + //the user cannot use this email + return false; + } + break; + default: + //we don't support this for members + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + return true; + } } } } \ No newline at end of file From 322bd4e41e4ef004daf48df85fad3dc9abac8fcc Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 14:32:39 +1100 Subject: [PATCH 13/16] Fixes: U4-5717 Member email validation does not allow valid domains (Umbraco 7.1.4) --- .../validation/valemail.directive.js | 44 +++++++++++++++++++ .../views/propertyeditors/email/email.html | 5 ++- .../unit/common/directives/val-email.spec.js | 34 ++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/directives/val-email.spec.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js new file mode 100644 index 0000000000..7f3e3a75c2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js @@ -0,0 +1,44 @@ +/** + * @ngdoc directive + * @name umbraco.directives.directive:valEmail + * @restrict A + * @description A custom directive to validate an email address string, this is required because angular's default validator is incorrect. + **/ +function valEmail(valEmailExpression) { + + return { + require: 'ngModel', + restrict: "A", + link: function (scope, elm, attrs, ctrl) { + + var patternValidator = function (viewValue) { + //NOTE: we don't validate on empty values, use required validator for that + if (!viewValue || valEmailExpression.EMAIL_REGEXP.test(viewValue)) { + // it is valid + ctrl.$setValidity('valEmail', true); + //assign a message to the validator + ctrl.errorMsg = ""; + return viewValue; + } + else { + // it is invalid, return undefined (no model update) + ctrl.$setValidity('valEmail', false); + //assign a message to the validator + ctrl.errorMsg = "Invalid email"; + return undefined; + } + }; + + ctrl.$formatters.push(patternValidator); + ctrl.$parsers.push(patternValidator); + } + }; +} + +angular.module('umbraco.directives') + .directive("valEmail", valEmail) + .factory('valEmailExpression', function() { + return { + EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html index f93b4c7a9a..0081d6ac26 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html @@ -1,13 +1,14 @@ 
- Required - Invalid email + Invalid email
diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/directives/val-email.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/directives/val-email.spec.js new file mode 100644 index 0000000000..265689988d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/directives/val-email.spec.js @@ -0,0 +1,34 @@ +describe('valEmail directive tests', function() { + + var valEmailExpression; + + beforeEach(module('umbraco')); + + beforeEach(inject(function ($injector) { + //TODO: I have no idea why this doesn't work!!?? it freakin should + //valEmailExpression = $injector.get('valEmailExpression'); + + //in the meantime, i've had to hard code the regex statement here + valEmailExpression = { + EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i + }; + + })); + + describe('EMAIL_REGEXP', function () { + /* global EMAIL_REGEXP: false */ + it('should validate email', function () { + expect(valEmailExpression.EMAIL_REGEXP.test('a@b.com')).toBe(true); + expect(valEmailExpression.EMAIL_REGEXP.test('a@b.museum')).toBe(true); + expect(valEmailExpression.EMAIL_REGEXP.test('a@B.c')).toBe(true); + expect(valEmailExpression.EMAIL_REGEXP.test('a@.b.c')).toBe(false); + expect(valEmailExpression.EMAIL_REGEXP.test('a@-b.c')).toBe(false); + expect(valEmailExpression.EMAIL_REGEXP.test('a@b-.c')).toBe(false); + expect(valEmailExpression.EMAIL_REGEXP.test('a@3b.c')).toBe(true); + expect(valEmailExpression.EMAIL_REGEXP.test('a@b')).toBe(true); + expect(valEmailExpression.EMAIL_REGEXP.test('abc@xyz.financial')).toBe(true); + + }); + }); + +}); \ No newline at end of file From dc846e7ff6f5e222d13d91b9f7fd69783b46ecc6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 15:02:58 +1100 Subject: [PATCH 14/16] Fixes installer email validation. Ensures all validation directives are registered under the correct module: umbraco.directives.validation, then we load this module in the installer so we can use the correct email validation directive. --- .../common/directives/validation/nodirtycheck.directive.js | 2 +- .../common/directives/validation/valHighlight.directive.js | 2 +- .../src/common/directives/validation/valemail.directive.js | 2 +- .../common/directives/validation/valformmanager.directive.js | 2 +- .../common/directives/validation/valpropertymsg.directive.js | 2 +- .../src/common/directives/validation/valregex.directive.js | 2 +- .../src/common/directives/validation/valserver.directive.js | 2 +- .../common/directives/validation/valserverfield.directive.js | 2 +- .../src/common/directives/validation/valtab.directive.js | 2 +- .../common/directives/validation/valtogglemsg.directive.js | 2 +- src/Umbraco.Web.UI.Client/src/install.loader.js | 5 +++-- src/Umbraco.Web.UI.Client/src/installer/steps/user.html | 2 +- src/Umbraco.Web.UI/Umbraco/js/install.loader.js | 5 +++-- 13 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js index 74c007dfbc..f027d7a12f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/nodirtycheck.directive.js @@ -15,4 +15,4 @@ function noDirtyCheck() { } }; } -angular.module('umbraco.directives').directive("noDirtyCheck", noDirtyCheck); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js index b276c8c931..fdcf768947 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js @@ -27,4 +27,4 @@ function valHighlight($timeout) { } }; } -angular.module('umbraco.directives').directive("valHighlight", valHighlight); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valHighlight", valHighlight); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js index 7f3e3a75c2..88ffd6f0fa 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valemail.directive.js @@ -35,7 +35,7 @@ function valEmail(valEmailExpression) { }; } -angular.module('umbraco.directives') +angular.module('umbraco.directives.validation') .directive("valEmail", valEmail) .factory('valEmailExpression', function() { return { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index d20feb0379..1bd4d409e7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -105,4 +105,4 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not } }; } -angular.module('umbraco.directives').directive("valFormManager", valFormManager); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valFormManager", valFormManager); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index d37f4c5271..a8d546bc36 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -182,4 +182,4 @@ function valPropertyMsg(serverValidationManager) { } }; } -angular.module('umbraco.directives').directive("valPropertyMsg", valPropertyMsg); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valPropertyMsg", valPropertyMsg); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js index e98fb06c98..651c0a54c7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js @@ -61,4 +61,4 @@ function valRegex() { } }; } -angular.module('umbraco.directives').directive("valRegex", valRegex); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valRegex", valRegex); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js index c1bcc4fd38..6225485073 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js @@ -91,4 +91,4 @@ function valServer(serverValidationManager) { } }; } -angular.module('umbraco.directives').directive("valServer", valServer); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valServer", valServer); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js index da79253c2f..9a077615df 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js @@ -51,4 +51,4 @@ function valServerField(serverValidationManager) { } }; } -angular.module('umbraco.directives').directive("valServerField", valServerField); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valServerField", valServerField); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js index 0216fe14d7..cd6dc51eca 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js @@ -35,4 +35,4 @@ function valTab() { } }; } -angular.module('umbraco.directives').directive("valTab", valTab); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valTab", valTab); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js index 5a57754524..cdcfbcfe2a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js @@ -84,4 +84,4 @@ function valToggleMsg(serverValidationManager) { * @requires formController * @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ? **/ -angular.module('umbraco.directives').directive("valToggleMsg", valToggleMsg); \ No newline at end of file +angular.module('umbraco.directives.validation').directive("valToggleMsg", valToggleMsg); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/install.loader.js b/src/Umbraco.Web.UI.Client/src/install.loader.js index 66f00f742c..5415a106e1 100644 --- a/src/Umbraco.Web.UI.Client/src/install.loader.js +++ b/src/Umbraco.Web.UI.Client/src/install.loader.js @@ -7,10 +7,11 @@ LazyLoad.js( [ 'lib/angular/1.1.5/angular-mocks.js', 'lib/angular/1.1.5/angular-sanitize.min.js', 'lib/underscore/underscore-min.js', - 'js/umbraco.installer.js' + 'js/umbraco.installer.js', + 'js/umbraco.directives.js' ], function () { jQuery(document).ready(function () { - angular.bootstrap(document, ['ngSanitize', 'umbraco.install']); + angular.bootstrap(document, ['ngSanitize', 'umbraco.install', 'umbraco.directives.validation']); }); } ); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html index 8202593782..2cd50fe208 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html @@ -18,7 +18,7 @@
- + Your email will be used as your login
diff --git a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js index 66f00f742c..5415a106e1 100644 --- a/src/Umbraco.Web.UI/Umbraco/js/install.loader.js +++ b/src/Umbraco.Web.UI/Umbraco/js/install.loader.js @@ -7,10 +7,11 @@ LazyLoad.js( [ 'lib/angular/1.1.5/angular-mocks.js', 'lib/angular/1.1.5/angular-sanitize.min.js', 'lib/underscore/underscore-min.js', - 'js/umbraco.installer.js' + 'js/umbraco.installer.js', + 'js/umbraco.directives.js' ], function () { jQuery(document).ready(function () { - angular.bootstrap(document, ['ngSanitize', 'umbraco.install']); + angular.bootstrap(document, ['ngSanitize', 'umbraco.install', 'umbraco.directives.validation']); }); } ); \ No newline at end of file From d6f47366d47aebe6d28c43902ff49e652a87f110 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 15:11:27 +1100 Subject: [PATCH 15/16] fixes how bytes are read with utf8 BOM for templates and master pages, fixes unit test --- .../Repositories/TemplateRepository.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index c21b43cdaf..26e6ec3469 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -358,15 +358,13 @@ namespace Umbraco.Core.Persistence.Repositories private void PopulateViewTemplate(ITemplate template, string fileName) { - string content = string.Empty; - string path = string.Empty; + string content; + var path = string.Empty; using (var stream = _viewsFileSystem.OpenFile(fileName)) + using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { - byte[] bytes = new byte[stream.Length]; - stream.Position = 0; - stream.Read(bytes, 0, (int)stream.Length); - content = Encoding.UTF8.GetString(bytes); + content = reader.ReadToEnd(); } template.Path = _viewsFileSystem.GetRelativePath(fileName); @@ -380,15 +378,13 @@ namespace Umbraco.Core.Persistence.Repositories private void PopulateMasterpageTemplate(ITemplate template, string fileName) { - string content = string.Empty; - string path = string.Empty; - + string content; + var path = string.Empty; + using (var stream = _masterpagesFileSystem.OpenFile(fileName)) + using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { - byte[] bytes = new byte[stream.Length]; - stream.Position = 0; - stream.Read(bytes, 0, (int)stream.Length); - content = Encoding.UTF8.GetString(bytes); + content = reader.ReadToEnd(); } template.Path = _masterpagesFileSystem.GetRelativePath(fileName); From 76ab09bfc8c79aea8e9d20a58f8d07cf1c42d17a Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jan 2015 15:36:17 +1100 Subject: [PATCH 16/16] Fixes remaining tests --- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 11 +++--- .../XmlPublishedCache/PublishedMediaCache.cs | 30 +++++++++------- src/UmbracoExamine/BaseUmbracoIndexer.cs | 5 +-- src/UmbracoExamine/DeletePolicyTracker.cs | 4 +-- src/UmbracoExamine/UmbracoExamineSearcher.cs | 5 +-- src/umbraco.businesslogic/ui.cs | 36 +++++++++++++++++++ 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index cf58f420ca..77a9fd2dba 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -142,12 +142,15 @@ namespace Umbraco.Tests.UmbracoExamine { var s = (IndexSearcher)_searcher.GetSearcher(); + + //first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine! - var r = IndexReader.Open(s.GetIndexReader().Directory(), false); + var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content); - var delCount = r.DeleteDocuments(contentTerm); - r.Commit(); - r.Close(); + var writer = _indexer.GetIndexWriter(); + writer.DeleteDocuments(contentTerm); + writer.Commit(); + //make sure the content is gone. This is done with lucene APIs, not examine! var collector = new AllHitsCollector(false, true); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 6edb9d7960..f47c280d6d 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -145,18 +145,24 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var eMgr = GetExamineManagerSafe(); if (eMgr != null) { - try - { - //by default use the InternalSearcher - return eMgr.SearchProviderCollection["InternalSearcher"]; - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - } + try + { + //by default use the InternalSearcher + return eMgr.SearchProviderCollection["InternalSearcher"]; + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } + catch (NullReferenceException) + { + //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore + // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null + // reference error will occur because the examine settings are null. + } } return null; } diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index aa0a5c0b63..48fcb85699 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -202,8 +202,9 @@ namespace UmbracoExamine //if temp local storage is configured use that, otherwise return the default if (_localTempStorageHelper.LuceneDirectory != null) { - return new IndexWriter(GetLuceneDirectory(), IndexingAnalyzer, - DeletePolicyTracker.Current.GetPolicy(IndexSetName), + var directory = GetLuceneDirectory(); + return new IndexWriter(directory, IndexingAnalyzer, + DeletePolicyTracker.Current.GetPolicy(directory), IndexWriter.MaxFieldLength.UNLIMITED); } diff --git a/src/UmbracoExamine/DeletePolicyTracker.cs b/src/UmbracoExamine/DeletePolicyTracker.cs index d002cc108d..3edcd12a24 100644 --- a/src/UmbracoExamine/DeletePolicyTracker.cs +++ b/src/UmbracoExamine/DeletePolicyTracker.cs @@ -14,9 +14,9 @@ namespace UmbracoExamine get { return Instance; } } - public IndexDeletionPolicy GetPolicy(string indexSetName) + public IndexDeletionPolicy GetPolicy(Lucene.Net.Store.Directory directory) { - var resolved = _directories.GetOrAdd(indexSetName, s => new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy())); + var resolved = _directories.GetOrAdd(directory.GetLockID(), s => new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy())); return resolved; } } diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs index 437e5c5220..7d5c8b7776 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs @@ -170,9 +170,10 @@ namespace UmbracoExamine protected override IndexReader OpenNewReader() { + var directory = GetLuceneDirectory(); return IndexReader.Open( - GetLuceneDirectory(), - DeletePolicyTracker.Current.GetPolicy(IndexSetName), + directory, + DeletePolicyTracker.Current.GetPolicy(directory), true); } diff --git a/src/umbraco.businesslogic/ui.cs b/src/umbraco.businesslogic/ui.cs index e6bfbb2435..5f2579d9ec 100644 --- a/src/umbraco.businesslogic/ui.cs +++ b/src/umbraco.businesslogic/ui.cs @@ -38,6 +38,8 @@ namespace umbraco [Obsolete("Get the current culture/language from the currently logged in IUser, the IUser object is available on most Umbraco base classes and on the UmbracoContext")] public static string Culture(User u) { + if (ApplicationContext.Current == null) return string.Empty; + var found = UserExtensions.GetUserCulture(u.Language, ApplicationContext.Current.Services.TextService); return found == null ? string.Empty : found.Name; } @@ -46,6 +48,8 @@ namespace umbraco [Obsolete("Get the current culture/language from the currently logged in IUser, the IUser object is available on most Umbraco base classes and on the UmbracoContext")] internal static string Culture(IUser u) { + if (ApplicationContext.Current == null) return string.Empty; + var found = u.GetUserCulture(ApplicationContext.Current.Services.TextService); return found == null ? string.Empty : found.Name; } @@ -94,11 +98,15 @@ namespace umbraco /// public static string Text(string Key, User u) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize(Key, GetCultureFromUserLanguage(GetLanguage(u))); } internal static string Text(string key, IUser u) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize(key, GetCultureFromUserLanguage(GetLanguage(u))); } @@ -109,6 +117,8 @@ namespace umbraco /// public static string Text(string Key) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize(Key, GetCultureFromUserLanguage(GetLanguage())); } @@ -121,6 +131,8 @@ namespace umbraco /// public static string Text(string Area, string Key, User u) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", Area, Key), GetCultureFromUserLanguage(GetLanguage(u))); @@ -128,6 +140,8 @@ namespace umbraco public static string Text(string area, string key, IUser u) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage(u))); @@ -141,6 +155,8 @@ namespace umbraco /// public static string Text(string Area, string Key) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", Area, Key), GetCultureFromUserLanguage(GetLanguage())); @@ -156,6 +172,8 @@ namespace umbraco /// public static string Text(string Area, string Key, string[] Variables, User u) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", Area, Key), GetCultureFromUserLanguage(GetLanguage(u)), @@ -164,6 +182,8 @@ namespace umbraco internal static string Text(string area, string key, string[] variables) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage()), @@ -172,6 +192,8 @@ namespace umbraco internal static string Text(string area, string key, string[] variables, IUser u) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage(u)), @@ -188,6 +210,8 @@ namespace umbraco /// public static string Text(string Area, string Key, string Variable, User u) { + if (ApplicationContext.Current == null) return "[" + Key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", Area, Key), GetCultureFromUserLanguage(GetLanguage(u)), @@ -196,6 +220,8 @@ namespace umbraco internal static string Text(string area, string key, string variable) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage()), @@ -204,6 +230,8 @@ namespace umbraco internal static string Text(string area, string key, string variable, IUser u) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage(u)), @@ -228,6 +256,8 @@ namespace umbraco /// public static string GetText(string area, string key) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage())); @@ -242,6 +272,8 @@ namespace umbraco /// public static string GetText(string area, string key, string[] variables) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage()), @@ -257,6 +289,8 @@ namespace umbraco /// public static string GetText(string area, string key, string variable) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage()), @@ -274,6 +308,8 @@ namespace umbraco /// This is the underlying call for all Text/GetText method calls public static string GetText(string area, string key, string[] variables, string language) { + if (ApplicationContext.Current == null) return "[" + key + "]"; + return ApplicationContext.Current.Services.TextService.Localize( string.Format("{0}/{1}", area, key), GetCultureFromUserLanguage(GetLanguage()),