From 6b205bd725279b86a0309edd93fe9896106bd434 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 11:45:41 +0100 Subject: [PATCH 001/100] Updates from PR#127 from Lars-Erik. Had to manually copy in the changes because the PR was closed. Adds Import of Dictionary Items to the PackagingService along with tests and a fix for the DictionaryRepository. --- .../Repositories/DictionaryRepository.cs | 2 +- src/Umbraco.Core/Services/PackagingService.cs | 68 ++++++++++ .../Repositories/DictionaryRepositoryTest.cs | 30 ++++- .../Services/Importing/Dictionary-Package.xml | 32 +++++ .../Importing/ImportResources.Designer.cs | 28 +++- .../Services/Importing/ImportResources.resx | 3 + .../Services/Importing/PackageImportTests.cs | 126 +++++++++++++++++- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../businesslogic/Packager/Installer.cs | 14 +- 9 files changed, 290 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 0b456cda91..eee020eee7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -180,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories } else { - translation.Id = Convert.ToInt32(Database.Insert(dto)); + translation.Id = Convert.ToInt32(Database.Insert(textDto)); translation.Key = entity.Key; } } diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 150bf23da3..1836a52ab6 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -837,6 +837,74 @@ namespace Umbraco.Core.Services #endregion #region Dictionary Items + public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList) + { + var languages = _localizationService.GetAllLanguages().ToList(); + return ImportDictionaryItems(dictionaryItemElementList, languages); + } + + private IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, List languages) + { + var items = new List(); + foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages)); + return items; + } + + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages) + { + var items = new List(); + + IDictionaryItem dictionaryItem; + var key = dictionaryItemElement.Attribute("Key").Value; + if (_localizationService.DictionaryItemExists(key)) + dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); + else + dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages); + _localizationService.Save(dictionaryItem); + items.Add(dictionaryItem); + items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages)); + return items; + } + + private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) + { + var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); + var translations = dictionaryItem.Translations.ToList(); + foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) + AddDictionaryTranslation(translations, valueElement, languages); + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages) + { + var dictionaryItem = new DictionaryItem(key); + var translations = new List(); + + foreach (var valueElement in dictionaryItemElement.Elements("Value")) + AddDictionaryTranslation(translations, valueElement, languages); + + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) + { + return translations.All(t => + String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, StringComparison.InvariantCultureIgnoreCase) != 0 + ); + } + + private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) + { + var languageId = valueElement.Attribute("LanguageCultureAlias").Value; + var language = languages.SingleOrDefault(l => l.IsoCode == languageId); + if (language == null) + return; + var translation = new DictionaryTranslation(language, valueElement.Value); + translations.Add(translation); + } #endregion #region Files diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index 554c0f7ec4..e8e25068db 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -180,7 +180,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [NUnit.Framework.Ignore] [Test] public void Can_Perform_Update_On_DictionaryRepository() { @@ -209,6 +208,35 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Perform_Update_WithNewTranslation_On_DictionaryRepository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var languageRepository = new LanguageRepository(unitOfWork); + var repository = new DictionaryRepository(unitOfWork, languageRepository); + + var languageNo = new Language("nb-NO") { CultureName = "nb-NO" }; + ServiceContext.LocalizationService.Save(languageNo); + + // Act + var item = repository.Get(1); + var translations = item.Translations.ToList(); + translations.Add(new DictionaryTranslation(languageNo, "Les mer")); + item.Translations = translations; + + repository.AddOrUpdate(item); + unitOfWork.Commit(); + + var dictionaryItem = repository.Get(1); + + // Assert + Assert.That(dictionaryItem, Is.Not.Null); + Assert.That(dictionaryItem.Translations.Count(), Is.EqualTo(3)); + Assert.That(dictionaryItem.Translations.Single(t => t.Language.IsoCode == "nb-NO").Value, Is.EqualTo("Les mer")); + } + [Test] public void Can_Perform_Delete_On_DictionaryRepository() { diff --git a/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml new file mode 100644 index 0000000000..7c5cf87856 --- /dev/null +++ b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml @@ -0,0 +1,32 @@ + + + + + + Dictionary-Package + 1.0 + MIT license + http://not.available + + 3 + 0 + 0 + + + + Test + http://not.available + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs b/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs index 9b0017ff5e..693a6a576b 100644 --- a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs +++ b/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18213 +// Runtime Version:4.0.30319.18051 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -88,6 +88,32 @@ namespace Umbraco.Tests.Services.Importing { } } + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Dictionary-Package</name> + /// <version>1.0</version> + /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> + /// <url>http://not.available</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>Test</name> + /// <website>http://not.available</w [rest of string was truncated]";. + /// + internal static string Dictionary_Package { + get { + return ResourceManager.GetString("Dictionary_Package", resourceCulture); + } + } + /// /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> ///<umbPackage> diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.resx b/src/Umbraco.Tests/Services/Importing/ImportResources.resx index 26f4589221..aebb452083 100644 --- a/src/Umbraco.Tests/Services/Importing/ImportResources.resx +++ b/src/Umbraco.Tests/Services/Importing/ImportResources.resx @@ -142,4 +142,7 @@ checkboxlist-content-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + dictionary-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index b4301fe835..1e13712458 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -2,8 +2,8 @@ using System.Linq; using System.Xml.Linq; using NUnit.Framework; +using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; -using umbraco.editorControls.MultiNodeTreePicker; namespace Umbraco.Tests.Services.Importing { @@ -375,5 +375,129 @@ namespace Umbraco.Tests.Services.Importing Assert.That(allTemplates.Count(), Is.EqualTo(numberOfTemplates)); Assert.That(allTemplates.First(x => x.Alias == "umbHomepage").Content, Contains.Substring("THIS HAS BEEN UPDATED!")); } + + [Test] + public void PackagingService_Can_Import_DictionaryItems() + { + // Arrange + const string expectedEnglishParentValue = "ParentValue"; + const string expectedNorwegianParentValue = "ForelderVerdi"; + const string expectedEnglishChildValue = "ChildValue"; + const string expectedNorwegianChildValue = "BarnVerdi"; + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); + + AddLanguages(); + + // Act + ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + + // Assert + AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); + AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO"); + AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB"); + AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO"); + } + + [Test] + public void PackagingService_WhenExistingDictionaryKey_ImportsNewChildren() + { + // Arrange + const string expectedEnglishParentValue = "ExistingParentValue"; + const string expectedNorwegianParentValue = "EksisterendeForelderVerdi"; + const string expectedEnglishChildValue = "ChildValue"; + const string expectedNorwegianChildValue = "BarnVerdi"; + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); + + AddLanguages(); + AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue); + + // Act + ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + + // Assert + AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); + AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO"); + AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB"); + AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO"); + } + + [Test] + public void PackagingService_WhenExistingDictionaryKey_OnlyAddsNewLanguages() + { + // Arrange + const string expectedEnglishParentValue = "ExistingParentValue"; + const string expectedNorwegianParentValue = "ForelderVerdi"; + const string expectedEnglishChildValue = "ChildValue"; + const string expectedNorwegianChildValue = "BarnVerdi"; + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); + + AddLanguages(); + AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue); + + // Act + ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + + // Assert + AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); + AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO"); + AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB"); + AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO"); + } + + private void AddLanguages() + { + var norwegian = new Core.Models.Language("nb-NO"); + var english = new Core.Models.Language("en-GB"); + ServiceContext.LocalizationService.Save(norwegian, 0); + ServiceContext.LocalizationService.Save(english, 0); + } + + private void AssertDictionaryItem(string key, string expectedValue, string cultureCode) + { + Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(key), "DictionaryItem key does not exist"); + var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(key); + var translation = dictionaryItem.Translations.SingleOrDefault(i => i.Language.IsoCode == cultureCode); + Assert.IsNotNull(translation, "Translation to {0} was not added", cultureCode); + var value = translation.Value; + Assert.That(value, Is.EqualTo(expectedValue), "Translation value was not set"); + } + + private void AddExistingEnglishParentDictionaryItem(string expectedEnglishParentValue) + { + var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList(); + var englishLanguage = languages.Single(l => l.IsoCode == "en-GB"); + ServiceContext.LocalizationService.Save( + new DictionaryItem("Parent") + { + Translations = new List + { + new DictionaryTranslation(englishLanguage, expectedEnglishParentValue), + } + } + ); + } + + private void AddExistingEnglishAndNorwegianParentDictionaryItem(string expectedEnglishParentValue, string expectedNorwegianParentValue) + { + var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList(); + var englishLanguage = languages.Single(l => l.IsoCode == "en-GB"); + var norwegianLanguage = languages.Single(l => l.IsoCode == "nb-NO"); + ServiceContext.LocalizationService.Save( + new DictionaryItem("Parent") + { + Translations = new List + { + new DictionaryTranslation(englishLanguage, expectedEnglishParentValue), + new DictionaryTranslation(norwegianLanguage, expectedNorwegianParentValue), + } + } + ); + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 7493eac540..db4fb63e84 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -553,6 +553,7 @@ Designer + diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index dc73fd9f10..31bebe9bb8 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -333,18 +333,12 @@ namespace umbraco.cms.businesslogic.packager #endregion #region Dictionary items - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) + var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); + if (dictionaryItemsElement != null) { - Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); - - if (newDi != null) - { - insPack.Data.DictionaryItems.Add(newDi.id.ToString()); - //saveNeeded = true; - } + var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); + insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString())); } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } #endregion #region Macros From 6e344f335e7dcb5229f3e7be1b9e743acfdc7dd2 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 13:08:33 +0100 Subject: [PATCH 002/100] Adding Export of DictionaryItems including test --- src/Umbraco.Core/Services/PackagingService.cs | 36 ++++++++ .../Services/PackagingServiceTests.cs | 87 +++++++++++++------ 2 files changed, 95 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 1836a52ab6..faf8a967a3 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading; +using System.Xml; using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -837,6 +838,40 @@ namespace Umbraco.Core.Services #endregion #region Dictionary Items + + public XElement Export(IEnumerable dictionaryItem, bool includeChildren = true) + { + var xml = new XElement("DictionaryItems"); + foreach (var item in dictionaryItem) + { + xml.Add(Export(item, includeChildren)); + } + return xml; + } + + private XElement Export(IDictionaryItem dictionaryItem, bool includeChildren) + { + var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey)); + foreach (var translation in dictionaryItem.Translations) + { + xml.Add(new XElement("Value", + new XAttribute("LanguageId", translation.Language.Id), + new XAttribute("LanguageCultureAlias", translation.Language.IsoCode), + new XCData(translation.Value))); + } + + if (includeChildren) + { + var children = _localizationService.GetDictionaryItemChildren(dictionaryItem.Key); + foreach (var child in children) + { + xml.Add(Export(child, true)); + } + } + + return xml; + } + public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList) { var languages = _localizationService.GetAllLanguages().ToList(); @@ -905,6 +940,7 @@ namespace Umbraco.Core.Services var translation = new DictionaryTranslation(language, valueElement.Value); translations.Add(translation); } + #endregion #region Files diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index abdfb2f9f3..2abf808d04 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -1,41 +1,72 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Tests.Services.Importing; using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Services { - //[TestFixture] - //public class PackagingServiceTests : BaseServiceTest - //{ - // [Test] - // public void Export_Content() - // { - // var yesNo = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.TrueFalse)); - // var txtField = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.Textbox)); + [TestFixture] + public class PackagingServiceTests : BaseServiceTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } - // var contentWithDataType = MockedContentTypes.CreateSimpleContentType( - // "test", - // "Test", - // new PropertyTypeCollection( - // new PropertyType[] - // { - // new PropertyType(new DataTypeDefinition(-1, txtField.Id) - // { - // Name = "Testing Textfield", DatabaseType = DataTypeDatabaseType.Ntext - // }), - // new PropertyType(new DataTypeDefinition(-1, yesNo.Id) - // { - // Name = "Testing intfield", DatabaseType = DataTypeDatabaseType.Integer - // }) - // })); + [TearDown] + public override void TearDown() + { + base.TearDown(); + } - // var content = MockedContent.CreateSimpleContent(contentWithDataType); - // content.Name = "Test"; + [Test] + public void PackagingService_Can_Export_DictionaryItems() + { + // Arrange + CreateTestData(); + var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent"); - // var exported = ServiceContext.PackagingService.Export(content); + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); - // } - //} + // Act + var xml = ServiceContext.PackagingService.Export(new []{dictionaryItem}); + + // Assert + Assert.That(xml.ToString(), Is.EqualTo(dictionaryItemsElement.ToString())); + } + + public void CreateTestData() + { + var languageNbNo = new Language("nb-NO") { CultureName = "nb-NO" }; + ServiceContext.LocalizationService.Save(languageNbNo); + + var languageEnGb = new Language("en-GB") { CultureName = "en-GB" }; + ServiceContext.LocalizationService.Save(languageEnGb); + + var parentItem = new DictionaryItem("Parent"); + var parentTranslations = new List + { + new DictionaryTranslation(languageNbNo, "ForelderVerdi"), + new DictionaryTranslation(languageEnGb, "ParentValue") + }; + parentItem.Translations = parentTranslations; + ServiceContext.LocalizationService.Save(parentItem); + + var childItem = new DictionaryItem(parentItem.Key, "Child"); + var childTranslations = new List + { + new DictionaryTranslation(languageNbNo, "BarnVerdi"), + new DictionaryTranslation(languageEnGb, "ChildValue") + }; + childItem.Translations = childTranslations; + ServiceContext.LocalizationService.Save(childItem); + } + } } \ No newline at end of file From 880636c5a6f1c3bb16e9c8905295eb760dbf2cd2 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 14:47:31 +0100 Subject: [PATCH 003/100] Adding languages xml to existing xml file for testing purposes --- src/Umbraco.Core/Models/DictionaryItem.cs | 1 - src/Umbraco.Core/Models/Language.cs | 1 - src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml | 4 ++++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs index db3f47ba13..c670a75108 100644 --- a/src/Umbraco.Core/Models/DictionaryItem.cs +++ b/src/Umbraco.Core/Models/DictionaryItem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models { diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index 6986fae027..ef8ebbf931 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models { diff --git a/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml index 7c5cf87856..6fa5a3d15f 100644 --- a/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml +++ b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml @@ -29,4 +29,8 @@ + + + + \ No newline at end of file From 33b99073f016bf8e0fb57c64882afa3c4c185d4e Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 14:48:31 +0100 Subject: [PATCH 004/100] Moving events out of the repository scope. Adding Get Language by Iso Code method --- .../Services/ILocalizationService.cs | 11 +++++- .../Services/LocalizationService.cs | 38 +++++++++++++------ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index 82a9319884..5c3bc965ec 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -81,9 +81,16 @@ namespace Umbraco.Core.Services /// /// Gets a by its culture code /// - /// Culture Code + /// Culture Code - also refered to as the Friendly name /// - ILanguage GetLanguageByCultureCode(string culture); + ILanguage GetLanguageByCultureCode(string cultureName); + + /// + /// Gets a by its iso code + /// + /// Iso Code of the language (ie. en-US) + /// + ILanguage GetLanguageByIsoCode(string isoCode); /// /// Gets all available languages diff --git a/src/Umbraco.Core/Services/LocalizationService.cs b/src/Umbraco.Core/Services/LocalizationService.cs index 47284eb1ad..b7c0cb3b55 100644 --- a/src/Umbraco.Core/Services/LocalizationService.cs +++ b/src/Umbraco.Core/Services/LocalizationService.cs @@ -146,10 +146,10 @@ namespace Umbraco.Core.Services { repository.AddOrUpdate(dictionaryItem); uow.Commit(); - - SavedDictionaryItem.RaiseEvent(new SaveEventArgs(dictionaryItem, false), this); } + SavedDictionaryItem.RaiseEvent(new SaveEventArgs(dictionaryItem, false), this); + Audit.Add(AuditTypes.Save, "Save DictionaryItem performed by user", userId, dictionaryItem.Id); } @@ -170,10 +170,10 @@ namespace Umbraco.Core.Services //NOTE: The recursive delete is done in the repository repository.Delete(dictionaryItem); uow.Commit(); - - DeletedDictionaryItem.RaiseEvent(new DeleteEventArgs(dictionaryItem, false), this); } + DeletedDictionaryItem.RaiseEvent(new DeleteEventArgs(dictionaryItem, false), this); + Audit.Add(AuditTypes.Delete, "Delete DictionaryItem performed by user", userId, dictionaryItem.Id); } @@ -193,13 +193,29 @@ namespace Umbraco.Core.Services /// /// Gets a by its culture code /// - /// Culture Code + /// Culture Name - also refered to as the Friendly name /// - public ILanguage GetLanguageByCultureCode(string culture) + public ILanguage GetLanguageByCultureCode(string cultureName) { using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.CultureName == culture); + var query = Query.Builder.Where(x => x.CultureName == cultureName); + var items = repository.GetByQuery(query); + + return items.FirstOrDefault(); + } + } + + /// + /// Gets a by its iso code + /// + /// Iso Code of the language (ie. en-US) + /// + public ILanguage GetLanguageByIsoCode(string isoCode) + { + using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.IsoCode == isoCode); var items = repository.GetByQuery(query); return items.FirstOrDefault(); @@ -234,10 +250,10 @@ namespace Umbraco.Core.Services { repository.AddOrUpdate(language); uow.Commit(); - - SavedLanguage.RaiseEvent(new SaveEventArgs(language, false), this); } + SavedLanguage.RaiseEvent(new SaveEventArgs(language, false), this); + Audit.Add(AuditTypes.Save, "Save Language performed by user", userId, language.Id); } @@ -257,10 +273,10 @@ namespace Umbraco.Core.Services //NOTE: There isn't any constraints in the db, so possible references aren't deleted repository.Delete(language); uow.Commit(); - - DeletedLanguage.RaiseEvent(new DeleteEventArgs(language, false), this); } + DeletedLanguage.RaiseEvent(new DeleteEventArgs(language, false), this); + Audit.Add(AuditTypes.Delete, "Delete Language performed by user", userId, language.Id); } From f86eb4b74149d926159419435c732c6ebad40f55 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 14:48:55 +0100 Subject: [PATCH 005/100] Adds import and export of languages along with tests --- src/Umbraco.Core/Services/PackagingService.cs | 43 ++++++++++++++++++- .../Services/Importing/PackageImportTests.cs | 19 ++++++++ .../Services/PackagingServiceTests.cs | 29 ++++++++++--- 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index faf8a967a3..b68d85501c 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -849,7 +849,7 @@ namespace Umbraco.Core.Services return xml; } - private XElement Export(IDictionaryItem dictionaryItem, bool includeChildren) + public XElement Export(IDictionaryItem dictionaryItem, bool includeChildren) { var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey)); foreach (var translation in dictionaryItem.Translations) @@ -947,6 +947,47 @@ namespace Umbraco.Core.Services #endregion #region Languages + + public XElement Export(IEnumerable languages) + { + var xml = new XElement("Languages"); + foreach (var language in languages) + { + xml.Add(Export(language)); + } + return xml; + } + + public XElement Export(ILanguage language) + { + var xml = new XElement("Language", + new XAttribute("Id", language.Id), + new XAttribute("CultureAlias", language.IsoCode), + new XAttribute("FriendlyName", language.CultureName)); + return xml; + } + + public IEnumerable ImportLanguages(XElement languageElementList) + { + var list = new List(); + foreach (var languageElement in languageElementList.Elements("Language")) + { + var isoCode = languageElement.Attribute("CultureAlias").Value; + var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); + if (existingLanguage == null) + { + var langauge = new Language(isoCode) + { + CultureName = languageElement.Attribute("FriendlyName").Value + }; + _localizationService.Save(langauge); + list.Add(langauge); + } + } + + return list; + } + #endregion #region Macros diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index 1e13712458..5a412966f3 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -450,6 +450,25 @@ namespace Umbraco.Tests.Services.Importing AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO"); } + [Test] + public void PackagingService_Can_Import_Languages() + { + // Arrange + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var LanguageItemsElement = newPackageXml.Elements("Languages").First(); + + // Act + var languages = ServiceContext.PackagingService.ImportLanguages(LanguageItemsElement); + var allLanguages = ServiceContext.LocalizationService.GetAllLanguages(); + + // Assert + Assert.That(languages.Any(x => x.HasIdentity == false), Is.False); + foreach (var language in languages) + { + Assert.That(allLanguages.Any(x => x.IsoCode == language.IsoCode), Is.True); + } + } + private void AddLanguages() { var norwegian = new Core.Models.Language("nb-NO"); diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 2abf808d04..22c41ee669 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Tests.Services.Importing; -using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Services { @@ -29,7 +26,7 @@ namespace Umbraco.Tests.Services public void PackagingService_Can_Export_DictionaryItems() { // Arrange - CreateTestData(); + CreateDictionaryData(); var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent"); var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); @@ -42,7 +39,27 @@ namespace Umbraco.Tests.Services Assert.That(xml.ToString(), Is.EqualTo(dictionaryItemsElement.ToString())); } - public void CreateTestData() + [Test] + public void PackagingService_Can_Export_Languages() + { + // Arrange + var languageNbNo = new Language("nb-NO") { CultureName = "Norwegian" }; + ServiceContext.LocalizationService.Save(languageNbNo); + + var languageEnGb = new Language("en-GB") { CultureName = "English (United Kingdom)" }; + ServiceContext.LocalizationService.Save(languageEnGb); + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var languageItemsElement = newPackageXml.Elements("Languages").First(); + + // Act + var xml = ServiceContext.PackagingService.Export(new[] { languageNbNo, languageEnGb }); + + // Assert + Assert.That(xml.ToString(), Is.EqualTo(languageItemsElement.ToString())); + } + + private void CreateDictionaryData() { var languageNbNo = new Language("nb-NO") { CultureName = "nb-NO" }; ServiceContext.LocalizationService.Save(languageNbNo); From 2467b5ee3bf228a51ffa13b3ddf4d2455f95e13d Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 14:53:27 +0100 Subject: [PATCH 006/100] Removing some internal experimental stuff that isnt used --- src/Umbraco.Core/Services/PackagingService.cs | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index b68d85501c..d770642418 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -56,49 +56,7 @@ namespace Umbraco.Core.Services _importedContentTypes = new Dictionary(); } - - #region Generic export methods - - internal void ExportToFile(string absoluteFilePath, string nodeType, int id) - { - XElement xml = null; - - if (nodeType.Equals("content", StringComparison.InvariantCultureIgnoreCase)) - { - var content = _contentService.GetById(id); - xml = Export(content); - } - - if (nodeType.Equals("media", StringComparison.InvariantCultureIgnoreCase)) - { - var media = _mediaService.GetById(id); - xml = Export(media); - } - - if (nodeType.Equals("contenttype", StringComparison.InvariantCultureIgnoreCase)) - { - var contentType = _contentTypeService.GetContentType(id); - xml = Export(contentType); - } - - if (nodeType.Equals("mediatype", StringComparison.InvariantCultureIgnoreCase)) - { - var mediaType = _contentTypeService.GetMediaType(id); - xml = Export(mediaType); - } - - if (nodeType.Equals("datatype", StringComparison.InvariantCultureIgnoreCase)) - { - var dataType = _dataTypeService.GetDataTypeDefinitionById(id); - xml = Export(dataType); - } - - if (xml != null) - xml.Save(absoluteFilePath); - } - - #endregion - + #region Content /// From d963f5cd7d59ea3919b01c85cad4b6323441bd52 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 14:57:27 +0100 Subject: [PATCH 007/100] Updating a few methods that are made public in 7.0.2 --- src/Umbraco.Core/Services/PackagingService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index d770642418..ca3a31870e 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -65,7 +65,7 @@ namespace Umbraco.Core.Services /// Content to export /// Optional parameter indicating whether to include descendents /// containing the xml representation of the Content object - internal XElement Export(IContent content, bool deep = false) + public XElement Export(IContent content, bool deep = false) { //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck(); @@ -676,7 +676,7 @@ namespace Umbraco.Core.Services /// /// /// - internal XElement Export(IEnumerabledataTypeDefinitions) + public XElement Export(IEnumerabledataTypeDefinitions) { var container = new XElement("DataTypes"); foreach (var d in dataTypeDefinitions) @@ -686,7 +686,7 @@ namespace Umbraco.Core.Services return container; } - internal XElement Export(IDataTypeDefinition dataTypeDefinition) + public XElement Export(IDataTypeDefinition dataTypeDefinition) { var prevalues = new XElement("PreValues"); From 955573256dd47925f42f6a6eb84522503876b9ab Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Fri, 10 Jan 2014 15:11:07 +0100 Subject: [PATCH 008/100] Updating the legacy Installer class to use the new methods in the PackagingService --- src/Umbraco.Core/Services/PackagingService.cs | 3 + .../businesslogic/Packager/Installer.cs | 118 +----------------- 2 files changed, 8 insertions(+), 113 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index ca3a31870e..816865d807 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -1195,5 +1195,8 @@ namespace Umbraco.Core.Services } #endregion + + #region Stylesheets + #endregion } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index 31bebe9bb8..102a74b2fc 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -300,36 +300,18 @@ namespace umbraco.cms.businesslogic.packager foreach (var dataTypeDefinition in dataTypeDefinitions) { insPack.Data.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; } } - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//DataType")) - { - cms.businesslogic.datatype.DataTypeDefinition newDtd = cms.businesslogic.datatype.DataTypeDefinition.Import(n); - - if (newDtd != null) - { - insPack.Data.DataTypes.Add(newDtd.Id.ToString()); - saveNeeded = true; - } - }*/ - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } #endregion #region Languages - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//Language")) + var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); + if (languageItemsElement != null) { - language.Language newLang = language.Language.Import(n); - - if (newLang != null) - { - insPack.Data.Languages.Add(newLang.id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } + var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); + insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString())); } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } + #endregion #region Dictionary items @@ -364,56 +346,8 @@ namespace umbraco.cms.businesslogic.packager foreach (var template in templates) { insPack.Data.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; } } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) - { - var t = Template.Import(n, currentUser); - - insPack.Data.Templates.Add(t.Id.ToString()); - - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - - //NOTE: SD: I'm pretty sure the only thing the below script does is ensure that the Master template Id is set - // in the database, but this is also duplicating the saving of the design content since the above Template.Import - // already does this. I've left this for now because I'm not sure the reprocussions of removing it but seems there - // is a lot of excess database calls happening here. - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) - { - string master = xmlHelper.GetNodeValue(n.SelectSingleNode("Master")); - Template t = Template.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Alias"))); - if (master.Trim() != "") - { - var masterTemplate = Template.GetByAlias(master); - if (masterTemplate != null) - { - t.MasterTemplate = Template.GetByAlias(master).Id; - //SD: This appears to always just save an empty template because the design isn't set yet - // this fixes an issue now that we have MVC because if there is an empty template and MVC is - // the default, it will create a View not a master page and then the system will try to route via - // MVC which means that the package will not work anymore. - // The code below that imports the templates should suffice because it's actually importing - // template data not just blank data. - - //if (UmbracoSettings.UseAspNetMasterPages) - // t.SaveMasterPageFile(t.Design); - } - } - // Master templates can only be generated when their master is known - if (UmbracoSettings.UseAspNetMasterPages) - { - t.ImportDesign(xmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); - t.SaveMasterPageFile(t.Design); - } - }*/ #endregion #region DocumentTypes @@ -432,44 +366,6 @@ namespace umbraco.cms.businesslogic.packager //saveNeeded = true; } } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) - { - ImportDocumentType(n, currentUser, false); - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - - // Add documenttype structure - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) - { - DocumentType dt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias"))); - if (dt != null) - { - ArrayList allowed = new ArrayList(); - foreach (XmlNode structure in n.SelectNodes("Structure/DocumentType")) - { - DocumentType dtt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(structure)); - if (dtt != null) - allowed.Add(dtt.Id); - } - - int[] adt = new int[allowed.Count]; - for (int i = 0; i < allowed.Count; i++) - adt[i] = (int)allowed[i]; - dt.AllowedChildContentTypeIDs = adt; - dt.Save(); - //PPH we log the document type install here. - insPack.Data.Documenttypes.Add(dt.Id.ToString()); - saveNeeded = true; - } - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; }*/ #endregion #region Stylesheets @@ -492,10 +388,6 @@ namespace umbraco.cms.businesslogic.packager var firstContentItem = content.First(); insPack.Data.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); } - /*foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) - { - insPack.Data.ContentNodeId = cms.businesslogic.web.Document.Import(-1, currentUser, n).ToString(); - }*/ #endregion #region Package Actions From 06defee41045d93b02c6337f9d838991715013ab Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 13 Jan 2014 13:05:25 +1100 Subject: [PATCH 009/100] Completed new NotificationService, moved Diff to Core --- .../Services/INotificationService.cs | 25 +- .../Services/NotificationService.cs | 313 ++++++++++- src/Umbraco.Core/Services/ServiceContext.cs | 12 + src/Umbraco.Core/Strings/Diff.cs | 510 ++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../businesslogic/workflow/Diff.cs | 3 +- .../businesslogic/workflow/Notification.cs | 2 +- 7 files changed, 857 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Core/Strings/Diff.cs diff --git a/src/Umbraco.Core/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs index 801424d3b0..4be6d17f0d 100644 --- a/src/Umbraco.Core/Services/INotificationService.cs +++ b/src/Umbraco.Core/Services/INotificationService.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Web; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; @@ -17,9 +19,15 @@ namespace Umbraco.Core.Services /// Sends the notifications for the specified user regarding the specified node and action. /// /// - /// + /// /// - void SendNotifications(IEntity entity, IUser user, IAction action); + /// + /// + /// + /// + void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http, + Func createSubject, + Func createBody); /// /// Gets the notifications for the user @@ -28,6 +36,17 @@ namespace Umbraco.Core.Services /// IEnumerable GetUserNotifications(IUser user); + /// + /// Gets the notifications for the user based on the specified node path + /// + /// + /// + /// + /// + /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors) + /// + IEnumerable GetUserNotifications(IUser user, string path); + /// /// Returns the notifications for an entity /// diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index 31c534470b..609b6316d8 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -1,10 +1,19 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Mail; +using System.Text; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Strings; using umbraco.interfaces; namespace Umbraco.Core.Services @@ -12,21 +21,59 @@ namespace Umbraco.Core.Services internal class NotificationService : INotificationService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly IUserService _userService; + private readonly IContentService _contentService; - public NotificationService(IDatabaseUnitOfWorkProvider provider) + public NotificationService(IDatabaseUnitOfWorkProvider provider, IUserService userService, IContentService contentService) { _uowProvider = provider; + _userService = userService; + _contentService = contentService; } /// /// Sends the notifications for the specified user regarding the specified node and action. /// /// - /// + /// /// - public void SendNotifications(IEntity entity, IUser user, IAction action) + /// + /// + /// + /// + /// + /// Currently this will only work for Content entities! + /// + public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http, + Func createSubject, + Func createBody) { - throw new NotImplementedException(); + if ((entity is IContent) == false) + { + throw new NotSupportedException(); + } + var content = (IContent) entity; + + int totalUsers; + var allUsers = _userService.GetAllMembers(0, int.MaxValue, out totalUsers); + foreach (var u in allUsers) + { + if (u.IsApproved == false) continue; + var userNotifications = GetUserNotifications(u, content.Path).ToArray(); + var notificationForAction = userNotifications.FirstOrDefault(x => x.Action == action); + if (notificationForAction != null) + { + try + { + SendNotification(operatingUser, u, content, actionName, http, createSubject, createBody); + LogHelper.Debug(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email)); + } + catch (Exception ex) + { + LogHelper.Error("An error occurred sending notification", ex); + } + } + } } /// @@ -41,6 +88,26 @@ namespace Umbraco.Core.Services return repository.GetUserNotifications(user); } + /// + /// Gets the notifications for the user based on the specified node path + /// + /// + /// + /// + /// + /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors) + /// + public IEnumerable GetUserNotifications(IUser user, string path) + { + var userNotifications = GetUserNotifications(user).ToArray(); + var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); + var result = pathParts + .Select(part => userNotifications.FirstOrDefault(x => x.EntityId.ToString(CultureInfo.InvariantCulture) == part)) + .Where(notification => notification != null) + .ToList(); + return result; + } + /// /// Deletes notifications by entity /// @@ -99,5 +166,243 @@ namespace Umbraco.Core.Services var repository = new NotificationsRepository(uow); return repository.CreateNotification(user, entity, action); } + + #region private methods + + /// + /// Sends the notification + /// + /// + /// + /// + /// The action readable name - currently an action is just a single letter, this is the name associated with the letter + /// + /// Callback to create the mail subject + /// Callback to create the mail body + private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, string actionName, HttpContextBase http, + Func createSubject, + Func createBody) + { + if (performingUser == null) throw new ArgumentNullException("performingUser"); + if (mailingUser == null) throw new ArgumentNullException("mailingUser"); + if (content == null) throw new ArgumentNullException("content"); + if (http == null) throw new ArgumentNullException("http"); + if (createSubject == null) throw new ArgumentNullException("createSubject"); + if (createBody == null) throw new ArgumentNullException("createBody"); + + // retrieve previous version of the document + var versions = _contentService.GetVersions(content.Id).ToArray(); + + int versionCount = (versions.Length > 1) ? (versions.Length - 2) : (versions.Length - 1); + var oldDoc = _contentService.GetByVersion(versions[versionCount].Version); + //var oldDoc = new Document(documentObject.Id, versions[versionCount].Version); + + // build summary + var summary = new StringBuilder(); + var props = content.Properties.ToArray(); + foreach (var p in props) + { + var newText = p.Value != null ? p.Value.ToString() : ""; + var oldText = newText; + + // check if something was changed and display the changes otherwise display the fields + if (oldDoc.Properties.Contains(p.PropertyType.Alias)) + { + var oldProperty = oldDoc.Properties[p.PropertyType.Alias]; + oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : ""; + + // replace html with char equivalent + ReplaceHtmlSymbols(ref oldText); + ReplaceHtmlSymbols(ref newText); + } + + + // make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary + // TODO: We should probably allow more than just tinymce?? + if ((p.PropertyType.DataTypeId == Guid.Parse(Constants.PropertyEditors.TinyMCEv3) || p.PropertyType.DataTypeId == Guid.Parse(Constants.PropertyEditors.TinyMCE)) + && string.CompareOrdinal(oldText, newText) != 0) + { + summary.Append(""); + summary.Append(" Note: "); + summary.Append( + " Red for deleted characters Yellow for inserted characters"); + summary.Append(""); + summary.Append(""); + summary.Append(" New " + + p.PropertyType.Name + ""); + summary.Append("" + + ReplaceLinks(CompareText(oldText, newText, true, false, "", string.Empty), http.Request) + + ""); + summary.Append(""); + summary.Append(""); + summary.Append(" Old " + + p.PropertyType.Name + ""); + summary.Append("" + + ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request) + + ""); + summary.Append(""); + } + else + { + summary.Append(""); + summary.Append("" + + p.PropertyType.Name + ""); + summary.Append("" + newText + ""); + summary.Append(""); + } + summary.Append( + " "); + } + + string protocol = GlobalSettings.UseSSL ? "https" : "http"; + + + string[] subjectVars = { + http.Request.ServerVariables["SERVER_NAME"] + ":" + + http.Request.Url.Port + + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + actionName, + content.Name + }; + string[] bodyVars = { + mailingUser.Name, + actionName, + content.Name, + performingUser.Name, + http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + content.Id.ToString(CultureInfo.InvariantCulture), summary.ToString(), + string.Format("{2}://{0}/{1}", + http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port, + //TODO: RE-enable this so we can have a nice url + /*umbraco.library.NiceUrl(documentObject.Id))*/ + content.Id + ".aspx", + protocol) + + }; + + // create the mail message + var mail = new MailMessage(UmbracoSettings.NotificationEmailSender, mailingUser.Email); + + // populate the message + mail.Subject = createSubject(mailingUser, subjectVars); + //mail.Subject = ui.Text("notifications", "mailSubject", subjectVars, mailingUser); + if (UmbracoSettings.NotificationDisableHtmlEmail) + { + mail.IsBodyHtml = false; + //mail.Body = ui.Text("notifications", "mailBody", bodyVars, mailingUser); + mail.Body = createBody(mailingUser, bodyVars); + } + else + { + mail.IsBodyHtml = true; + mail.Body = + @" + + +" + createBody(mailingUser, bodyVars); + //ui.Text("notifications", "mailBodyHtml", bodyVars, mailingUser) + ""; + } + + // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here + // adding the server name to make sure we don't replace external links + if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false) + { + string serverName = http.Request.ServerVariables["SERVER_NAME"]; + mail.Body = mail.Body.Replace( + string.Format("http://{0}", serverName), + string.Format("https://{0}", serverName)); + } + + // send it + var sender = new SmtpClient(); + sender.Send(mail); + } + + private static string ReplaceLinks(string text, HttpRequestBase request) + { + string domain = GlobalSettings.UseSSL ? "https://" : "http://"; + domain += request.ServerVariables["SERVER_NAME"] + ":" + request.Url.Port + "/"; + text = text.Replace("href=\"/", "href=\"" + domain); + text = text.Replace("src=\"/", "src=\"" + domain); + return text; + } + + /// + /// Replaces the HTML symbols with the character equivalent. + /// + /// The old string. + private static void ReplaceHtmlSymbols(ref string oldString) + { + oldString = oldString.Replace(" ", " "); + oldString = oldString.Replace("’", "'"); + oldString = oldString.Replace("&", "&"); + oldString = oldString.Replace("“", "“"); + oldString = oldString.Replace("”", "”"); + oldString = oldString.Replace(""", "\""); + } + + /// + /// Compares the text. + /// + /// The old text. + /// The new text. + /// if set to true [display inserted text]. + /// if set to true [display deleted text]. + /// The inserted style. + /// The deleted style. + /// + private static string CompareText(string oldText, string newText, bool displayInsertedText, + bool displayDeletedText, string insertedStyle, string deletedStyle) + { + var sb = new StringBuilder(); + var diffs = Diff.DiffText1(oldText, newText); + + int pos = 0; + for (int n = 0; n < diffs.Length; n++) + { + Diff.Item it = diffs[n]; + + // write unchanged chars + while ((pos < it.StartB) && (pos < newText.Length)) + { + sb.Append(newText[pos]); + pos++; + } // while + + // write deleted chars + if (displayDeletedText && it.DeletedA > 0) + { + sb.Append(deletedStyle); + for (int m = 0; m < it.DeletedA; m++) + { + sb.Append(oldText[it.StartA + m]); + } // for + sb.Append(""); + } + + // write inserted chars + if (displayInsertedText && pos < it.StartB + it.InsertedB) + { + sb.Append(insertedStyle); + while (pos < it.StartB + it.InsertedB) + { + sb.Append(newText[pos]); + pos++; + } // while + sb.Append(""); + } // if + } // while + + // write rest of unchanged chars + while (pos < newText.Length) + { + sb.Append(newText[pos]); + pos++; + } // while + + return sb.ToString(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index ffce7b42ba..c57081f51a 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -28,6 +28,7 @@ namespace Umbraco.Core.Services //private Lazy _sectionService; //private Lazy _macroService; private Lazy _memberTypeService; + private Lazy _notificationService; /// /// public ctor - will generally just be used for unit testing @@ -98,6 +99,9 @@ namespace Umbraco.Core.Services var provider = dbUnitOfWorkProvider; var fileProvider = fileUnitOfWorkProvider; + if (_notificationService == null) + _notificationService = new Lazy(() => new NotificationService(provider, _userService.Value, _contentService.Value)); + if (_serverRegistrationService == null) _serverRegistrationService = new Lazy(() => new ServerRegistrationService(provider, repositoryFactory.Value)); @@ -148,6 +152,14 @@ namespace Umbraco.Core.Services } + /// + /// Gets the + /// + internal INotificationService NotificationService + { + get { return _notificationService.Value; } + } + /// /// Gets the /// diff --git a/src/Umbraco.Core/Strings/Diff.cs b/src/Umbraco.Core/Strings/Diff.cs new file mode 100644 index 0000000000..ed381d4f6f --- /dev/null +++ b/src/Umbraco.Core/Strings/Diff.cs @@ -0,0 +1,510 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Strings +{ + /// + /// This Class implements the Difference Algorithm published in + /// "An O(ND) Difference Algorithm and its Variations" by Eugene Myers + /// Algorithmica Vol. 1 No. 2, 1986, p 251. + /// + /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents + /// each line is converted into a (hash) number. See DiffText(). + /// + /// diff.cs: A port of the algorithm to C# + /// Copyright (c) by Matthias Hertel, http://www.mathertel.de + /// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx + /// + internal class Diff + { + /// Data on one input file being compared. + /// + internal class DiffData + { + + /// Number of elements (lines). + internal int Length; + + /// Buffer of numbers that will be compared. + internal int[] Data; + + /// + /// Array of booleans that flag for modified data. + /// This is the result of the diff. + /// This means deletedA in the first Data or inserted in the second Data. + /// + internal bool[] Modified; + + /// + /// Initialize the Diff-Data buffer. + /// + /// reference to the buffer + internal DiffData(int[] initData) + { + Data = initData; + Length = initData.Length; + Modified = new bool[Length + 2]; + } // DiffData + + } // class DiffData + + /// details of one difference. + public struct Item + { + /// Start Line number in Data A. + public int StartA; + /// Start Line number in Data B. + public int StartB; + + /// Number of changes in Data A. + public int DeletedA; + /// Number of changes in Data B. + public int InsertedB; + } // Item + + /// + /// Shortest Middle Snake Return Data + /// + private struct Smsrd + { + internal int X, Y; + // internal int u, v; // 2002.09.20: no need for 2 points + } + + /// + /// Find the difference in 2 texts, comparing by textlines. + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffText(string textA, string textB) + { + return (DiffText(textA, textB, false, false, false)); + } // DiffText + + /// + /// Find the difference in 2 texts, comparing by textlines. + /// This method uses the DiffInt internally by 1st converting the string into char codes + /// then uses the diff int method + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffText1(string textA, string textB) + { + return DiffInt(DiffCharCodes(textA, false), DiffCharCodes(textB, false)); + } + + + /// + /// Find the difference in 2 text documents, comparing by textlines. + /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents + /// each line is converted into a (hash) number. This hash-value is computed by storing all + /// textlines into a common hashtable so i can find dublicates in there, and generating a + /// new number each time a new textline is inserted. + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done. + /// When set to true, all whitespace characters are converted to a single space character before the comparation is done. + /// When set to true, all characters are converted to their lowercase equivivalence before the comparation is done. + /// Returns a array of Items that describe the differences. + public static Item[] DiffText(string textA, string textB, bool trimSpace, bool ignoreSpace, bool ignoreCase) + { + // prepare the input-text and convert to comparable numbers. + var h = new Hashtable(textA.Length + textB.Length); + + // The A-Version of the data (original data) to be compared. + var dataA = new DiffData(DiffCodes(textA, h, trimSpace, ignoreSpace, ignoreCase)); + + // The B-Version of the data (modified data) to be compared. + var dataB = new DiffData(DiffCodes(textB, h, trimSpace, ignoreSpace, ignoreCase)); + + h = null; // free up hashtable memory (maybe) + + var max = dataA.Length + dataB.Length + 1; + // vector for the (0,0) to (x,y) search + var downVector = new int[2 * max + 2]; + // vector for the (u,v) to (N,M) search + var upVector = new int[2 * max + 2]; + + Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector); + + Optimize(dataA); + Optimize(dataB); + return CreateDiffs(dataA, dataB); + } // DiffText + + + /// + /// Diffs the char codes. + /// + /// A text. + /// if set to true [ignore case]. + /// + private static int[] DiffCharCodes(string aText, bool ignoreCase) + { + if (ignoreCase) + aText = aText.ToUpperInvariant(); + + var codes = new int[aText.Length]; + + for (int n = 0; n < aText.Length; n++) + codes[n] = (int)aText[n]; + + return (codes); + } // DiffCharCodes + + /// + /// If a sequence of modified lines starts with a line that contains the same content + /// as the line that appends the changes, the difference sequence is modified so that the + /// appended line and not the starting line is marked as modified. + /// This leads to more readable diff sequences when comparing text files. + /// + /// A Diff data buffer containing the identified changes. + private static void Optimize(DiffData data) + { + var startPos = 0; + while (startPos < data.Length) + { + while ((startPos < data.Length) && (data.Modified[startPos] == false)) + startPos++; + int endPos = startPos; + while ((endPos < data.Length) && (data.Modified[endPos] == true)) + endPos++; + + if ((endPos < data.Length) && (data.Data[startPos] == data.Data[endPos])) + { + data.Modified[startPos] = false; + data.Modified[endPos] = true; + } + else + { + startPos = endPos; + } // if + } // while + } // Optimize + + + /// + /// Find the difference in 2 arrays of integers. + /// + /// A-version of the numbers (usualy the old one) + /// B-version of the numbers (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffInt(int[] arrayA, int[] arrayB) + { + // The A-Version of the data (original data) to be compared. + var dataA = new DiffData(arrayA); + + // The B-Version of the data (modified data) to be compared. + var dataB = new DiffData(arrayB); + + var max = dataA.Length + dataB.Length + 1; + // vector for the (0,0) to (x,y) search + var downVector = new int[2 * max + 2]; + // vector for the (u,v) to (N,M) search + var upVector = new int[2 * max + 2]; + + Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector); + return CreateDiffs(dataA, dataB); + } // Diff + + + /// + /// This function converts all textlines of the text into unique numbers for every unique textline + /// so further work can work only with simple numbers. + /// + /// the input text + /// This extern initialized hashtable is used for storing all ever used textlines. + /// ignore leading and trailing space characters + /// + /// + /// a array of integers. + private static int[] DiffCodes(string aText, IDictionary h, bool trimSpace, bool ignoreSpace, bool ignoreCase) + { + // get all codes of the text + var lastUsedCode = h.Count; + + // strip off all cr, only use lf as textline separator. + aText = aText.Replace("\r", ""); + var lines = aText.Split('\n'); + + var codes = new int[lines.Length]; + + for (int i = 0; i < lines.Length; ++i) + { + string s = lines[i]; + if (trimSpace) + s = s.Trim(); + + if (ignoreSpace) + { + s = Regex.Replace(s, "\\s+", " "); // TODO: optimization: faster blank removal. + } + + if (ignoreCase) + s = s.ToLower(); + + object aCode = h[s]; + if (aCode == null) + { + lastUsedCode++; + h[s] = lastUsedCode; + codes[i] = lastUsedCode; + } + else + { + codes[i] = (int)aCode; + } // if + } // for + return (codes); + } // DiffCodes + + + /// + /// This is the algorithm to find the Shortest Middle Snake (SMS). + /// + /// sequence A + /// lower bound of the actual range in DataA + /// upper bound of the actual range in DataA (exclusive) + /// sequence B + /// lower bound of the actual range in DataB + /// upper bound of the actual range in DataB (exclusive) + /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + /// a MiddleSnakeData record containing x,y and u,v + private static Smsrd Sms(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + int max = dataA.Length + dataB.Length + 1; + + int downK = lowerA - lowerB; // the k-line to start the forward search + int upK = upperA - upperB; // the k-line to start the reverse search + + int delta = (upperA - lowerA) - (upperB - lowerB); + bool oddDelta = (delta & 1) != 0; + + // The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based + // and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor + int downOffset = max - downK; + int upOffset = max - upK; + + int maxD = ((upperA - lowerA + upperB - lowerB) / 2) + 1; + + // Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB)); + + // init vectors + downVector[downOffset + downK + 1] = lowerA; + upVector[upOffset + upK - 1] = upperA; + + for (int d = 0; d <= maxD; d++) + { + + // Extend the forward path. + Smsrd ret; + for (int k = downK - d; k <= downK + d; k += 2) + { + // Debug.Write(0, "SMS", "extend forward path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == downK - d) + { + x = downVector[downOffset + k + 1]; // down + } + else + { + x = downVector[downOffset + k - 1] + 1; // a step to the right + if ((k < downK + d) && (downVector[downOffset + k + 1] >= x)) + x = downVector[downOffset + k + 1]; // down + } + y = x - k; + + // find the end of the furthest reaching forward D-path in diagonal k. + while ((x < upperA) && (y < upperB) && (dataA.Data[x] == dataB.Data[y])) + { + x++; y++; + } + downVector[downOffset + k] = x; + + // overlap ? + if (oddDelta && (upK - d < k) && (k < upK + d)) + { + if (upVector[upOffset + k] <= downVector[downOffset + k]) + { + ret.X = downVector[downOffset + k]; + ret.Y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } // if + } // if + + } // for k + + // Extend the reverse path. + for (int k = upK - d; k <= upK + d; k += 2) + { + // Debug.Write(0, "SMS", "extend reverse path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == upK + d) + { + x = upVector[upOffset + k - 1]; // up + } + else + { + x = upVector[upOffset + k + 1] - 1; // left + if ((k > upK - d) && (upVector[upOffset + k - 1] < x)) + x = upVector[upOffset + k - 1]; // up + } // if + y = x - k; + + while ((x > lowerA) && (y > lowerB) && (dataA.Data[x - 1] == dataB.Data[y - 1])) + { + x--; y--; // diagonal + } + upVector[upOffset + k] = x; + + // overlap ? + if (!oddDelta && (downK - d <= k) && (k <= downK + d)) + { + if (upVector[upOffset + k] <= downVector[downOffset + k]) + { + ret.X = downVector[downOffset + k]; + ret.Y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } // if + } // if + + } // for k + + } // for D + + throw new ApplicationException("the algorithm should never come here."); + } // SMS + + + /// + /// This is the divide-and-conquer implementation of the longes common-subsequence (LCS) + /// algorithm. + /// The published algorithm passes recursively parts of the A and B sequences. + /// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant. + /// + /// sequence A + /// lower bound of the actual range in DataA + /// upper bound of the actual range in DataA (exclusive) + /// sequence B + /// lower bound of the actual range in DataB + /// upper bound of the actual range in DataB (exclusive) + /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + private static void Lcs(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + // Debug.Write(2, "LCS", String.Format("Analyse the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB)); + + // Fast walkthrough equal lines at the start + while (lowerA < upperA && lowerB < upperB && dataA.Data[lowerA] == dataB.Data[lowerB]) + { + lowerA++; lowerB++; + } + + // Fast walkthrough equal lines at the end + while (lowerA < upperA && lowerB < upperB && dataA.Data[upperA - 1] == dataB.Data[upperB - 1]) + { + --upperA; --upperB; + } + + if (lowerA == upperA) + { + // mark as inserted lines. + while (lowerB < upperB) + dataB.Modified[lowerB++] = true; + + } + else if (lowerB == upperB) + { + // mark as deleted lines. + while (lowerA < upperA) + dataA.Modified[lowerA++] = true; + + } + else + { + // Find the middle snakea and length of an optimal path for A and B + Smsrd smsrd = Sms(dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector); + // Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y)); + + // The path is from LowerX to (x,y) and (x,y) to UpperX + Lcs(dataA, lowerA, smsrd.X, dataB, lowerB, smsrd.Y, downVector, upVector); + Lcs(dataA, smsrd.X, upperA, dataB, smsrd.Y, upperB, downVector, upVector); // 2002.09.20: no need for 2 points + } + } // LCS() + + + /// Scan the tables of which lines are inserted and deleted, + /// producing an edit script in forward order. + /// + /// dynamic array + private static Item[] CreateDiffs(DiffData dataA, DiffData dataB) + { + ArrayList a = new ArrayList(); + Item aItem; + Item[] result; + + int lineA = 0; + int lineB = 0; + while (lineA < dataA.Length || lineB < dataB.Length) + { + if ((lineA < dataA.Length) && (!dataA.Modified[lineA]) + && (lineB < dataB.Length) && (!dataB.Modified[lineB])) + { + // equal lines + lineA++; + lineB++; + + } + else + { + // maybe deleted and/or inserted lines + int startA = lineA; + int startB = lineB; + + while (lineA < dataA.Length && (lineB >= dataB.Length || dataA.Modified[lineA])) + // while (LineA < DataA.Length && DataA.modified[LineA]) + lineA++; + + while (lineB < dataB.Length && (lineA >= dataA.Length || dataB.Modified[lineB])) + // while (LineB < DataB.Length && DataB.modified[LineB]) + lineB++; + + if ((startA < lineA) || (startB < lineB)) + { + // store a new difference-item + aItem = new Item(); + aItem.StartA = startA; + aItem.StartB = startB; + aItem.DeletedA = lineA - startA; + aItem.InsertedB = lineB - startB; + a.Add(aItem); + } // if + } // if + } // while + + result = new Item[a.Count]; + a.CopyTo(result); + + return (result); + } + + } // class Diff + + +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index de971f6269..4c26172f17 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -789,6 +789,7 @@ + diff --git a/src/umbraco.cms/businesslogic/workflow/Diff.cs b/src/umbraco.cms/businesslogic/workflow/Diff.cs index e4e4d505a9..39cbc1d2c3 100644 --- a/src/umbraco.cms/businesslogic/workflow/Diff.cs +++ b/src/umbraco.cms/businesslogic/workflow/Diff.cs @@ -18,7 +18,7 @@ namespace umbraco.cms.businesslogic.workflow /// Copyright (c) by Matthias Hertel, http://www.mathertel.de /// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx /// - + [Obsolete("This class will be removed from the codebase in the future")] public class Diff { @@ -489,6 +489,7 @@ namespace umbraco.cms.businesslogic.workflow /// Data on one input file being compared. /// + [Obsolete("This class will be removed from the codebase in the future, logic has moved to the Core project")] internal class DiffData { diff --git a/src/umbraco.cms/businesslogic/workflow/Notification.cs b/src/umbraco.cms/businesslogic/workflow/Notification.cs index 29fc8ed319..e70077d118 100644 --- a/src/umbraco.cms/businesslogic/workflow/Notification.cs +++ b/src/umbraco.cms/businesslogic/workflow/Notification.cs @@ -71,7 +71,7 @@ namespace umbraco.cms.businesslogic.workflow } } - ///TODO: Include update with html mail notification and document contents + //TODO: Include update with html mail notification and document contents private static void SendNotification(User performingUser, User mailingUser, Document documentObject, IAction action) { // retrieve previous version of the document From 44bc365fddff47ad67f223d5fab7b9d1c39b0c67 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 13 Jan 2014 13:50:30 +1100 Subject: [PATCH 010/100] Moves all notification sending logic to the notification service, improves performance during any action when notifications are to be sent, ensures emails are sent out async to not block up the current request. --- .../Services/NotificationService.cs | 45 ++-- .../businesslogic/utilities/Diff.cs | 2 + .../businesslogic/workflow/Notification.cs | 212 +----------------- 3 files changed, 41 insertions(+), 218 deletions(-) diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index 609b6316d8..4ddde70e86 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Net.Mail; using System.Text; +using System.Threading; using System.Web; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -53,6 +54,8 @@ namespace Umbraco.Core.Services throw new NotSupportedException(); } var content = (IContent) entity; + //we'll lazily get these if we need to send notifications + IContent[] allVersions = null; int totalUsers; var allUsers = _userService.GetAllMembers(0, int.MaxValue, out totalUsers); @@ -63,9 +66,15 @@ namespace Umbraco.Core.Services var notificationForAction = userNotifications.FirstOrDefault(x => x.Action == action); if (notificationForAction != null) { + //lazy load versions if notifications are required + if (allVersions == null) + { + allVersions = _contentService.GetVersions(entity.Id).ToArray(); + } + try { - SendNotification(operatingUser, u, content, actionName, http, createSubject, createBody); + SendNotification(operatingUser, u, content, allVersions, actionName, http, createSubject, createBody); LogHelper.Debug(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email)); } catch (Exception ex) @@ -101,10 +110,7 @@ namespace Umbraco.Core.Services { var userNotifications = GetUserNotifications(user).ToArray(); var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); - var result = pathParts - .Select(part => userNotifications.FirstOrDefault(x => x.EntityId.ToString(CultureInfo.InvariantCulture) == part)) - .Where(notification => notification != null) - .ToList(); + var result = userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList(); return result; } @@ -175,27 +181,25 @@ namespace Umbraco.Core.Services /// /// /// + /// /// The action readable name - currently an action is just a single letter, this is the name associated with the letter /// /// Callback to create the mail subject /// Callback to create the mail body - private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, string actionName, HttpContextBase http, + private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IContent[] allVersions, string actionName, HttpContextBase http, Func createSubject, Func createBody) { if (performingUser == null) throw new ArgumentNullException("performingUser"); if (mailingUser == null) throw new ArgumentNullException("mailingUser"); if (content == null) throw new ArgumentNullException("content"); + if (allVersions == null) throw new ArgumentNullException("allVersions"); if (http == null) throw new ArgumentNullException("http"); if (createSubject == null) throw new ArgumentNullException("createSubject"); if (createBody == null) throw new ArgumentNullException("createBody"); - // retrieve previous version of the document - var versions = _contentService.GetVersions(content.Id).ToArray(); - - int versionCount = (versions.Length > 1) ? (versions.Length - 2) : (versions.Length - 1); - var oldDoc = _contentService.GetByVersion(versions[versionCount].Version); - //var oldDoc = new Document(documentObject.Id, versions[versionCount].Version); + int versionCount = (allVersions.Length > 1) ? (allVersions.Length - 2) : (allVersions.Length - 1); + var oldDoc = _contentService.GetByVersion(allVersions[versionCount].Version); // build summary var summary = new StringBuilder(); @@ -313,9 +317,20 @@ namespace Umbraco.Core.Services string.Format("https://{0}", serverName)); } - // send it - var sender = new SmtpClient(); - sender.Send(mail); + + // send it asynchronously, we don't want to got up all of the request time to send emails! + ThreadPool.QueueUserWorkItem(state => + { + try + { + var sender = new SmtpClient(); + sender.Send(mail); + } + catch (Exception ex) + { + LogHelper.Error("An error occurred sending notification", ex); + } + }); } private static string ReplaceLinks(string text, HttpRequestBase request) diff --git a/src/umbraco.cms/businesslogic/utilities/Diff.cs b/src/umbraco.cms/businesslogic/utilities/Diff.cs index 62397f2495..3aa159de7e 100644 --- a/src/umbraco.cms/businesslogic/utilities/Diff.cs +++ b/src/umbraco.cms/businesslogic/utilities/Diff.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Text; using System.Text.RegularExpressions; +//TODO: We've alraedy moved most of this logic to Core.Strings - need to review this as it has slightly more functionality but should be moved to core and obsoleted! + namespace umbraco.cms.businesslogic.utilities { /// /// This Class implements the Difference Algorithm published in diff --git a/src/umbraco.cms/businesslogic/workflow/Notification.cs b/src/umbraco.cms/businesslogic/workflow/Notification.cs index e70077d118..4ebd48e7f2 100644 --- a/src/umbraco.cms/businesslogic/workflow/Notification.cs +++ b/src/umbraco.cms/businesslogic/workflow/Notification.cs @@ -74,135 +74,16 @@ namespace umbraco.cms.businesslogic.workflow //TODO: Include update with html mail notification and document contents private static void SendNotification(User performingUser, User mailingUser, Document documentObject, IAction action) { - // retrieve previous version of the document - DocumentVersionList[] versions = documentObject.GetVersions(); - int versionCount = (versions.Length > 1) ? (versions.Length - 2) : (versions.Length - 1); - var oldDoc = new Document(documentObject.Id, versions[versionCount].Version); + var nService = ApplicationContext.Current.Services.NotificationService; + var pUser = ApplicationContext.Current.Services.UserService.GetById(performingUser.Id); - // build summary - var summary = new StringBuilder(); - var props = documentObject.GenericProperties; - foreach (Property p in props) - { - // check if something was changed and display the changes otherwise display the fields - Property oldProperty = oldDoc.getProperty(p.PropertyType.Alias); - string oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : ""; - string newText = p.Value != null ? p.Value.ToString() : ""; - - // replace html with char equivalent - ReplaceHtmlSymbols(ref oldText); - ReplaceHtmlSymbols(ref newText); - - // make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary - //TODO PPH: Had to change this, as a reference to the editorcontrols is not allowed, so a string comparison is the only way, this should be a DIFF or something instead.. - if (p.PropertyType.DataTypeDefinition.DataType.ToString() == - "umbraco.editorControls.tinymce.TinyMCEDataType" && - string.CompareOrdinal(oldText, newText) != 0) - { - summary.Append(""); - summary.Append(" Note: "); - summary.Append( - " Red for deleted characters Yellow for inserted characters"); - summary.Append(""); - summary.Append(""); - summary.Append(" New " + - p.PropertyType.Name + ""); - summary.Append("" + - ReplaceLinks(CompareText(oldText, newText, true, false, - "", string.Empty)) + - ""); - summary.Append(""); - summary.Append(""); - summary.Append(" Old " + - oldProperty.PropertyType.Name + ""); - summary.Append("" + - ReplaceLinks(CompareText(newText, oldText, true, false, - "", string.Empty)) + - ""); - summary.Append(""); - } - else - { - summary.Append(""); - summary.Append("" + - p.PropertyType.Name + ""); - summary.Append("" + newText + ""); - summary.Append(""); - } - summary.Append( - " "); - } - - string protocol = GlobalSettings.UseSSL ? "https" : "http"; - - - string[] subjectVars = { - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + - IOHelper.ResolveUrl(SystemDirectories.Umbraco), ui.Text(action.Alias) - , - documentObject.Text - }; - string[] bodyVars = { - mailingUser.Name, ui.Text(action.Alias), documentObject.Text, performingUser.Name, - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + - IOHelper.ResolveUrl(SystemDirectories.Umbraco), - documentObject.Id.ToString(), summary.ToString(), - String.Format("{2}://{0}/{1}", - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port, - /*umbraco.library.NiceUrl(documentObject.Id))*/ - documentObject.Id + ".aspx", - protocol) - //TODO: PPH removed the niceURL reference... cms.dll cannot reference the presentation project... - //TODO: This should be moved somewhere else.. - }; - - // create the mail message - var mail = new MailMessage(UmbracoSettings.NotificationEmailSender, mailingUser.Email); - - // populate the message - mail.Subject = ui.Text("notifications", "mailSubject", subjectVars, mailingUser); - if (UmbracoSettings.NotificationDisableHtmlEmail) - { - mail.IsBodyHtml = false; - mail.Body = ui.Text("notifications", "mailBody", bodyVars, mailingUser); - } - else - { - mail.IsBodyHtml = true; - mail.Body = - @" - - -" + - ui.Text("notifications", "mailBodyHtml", bodyVars, mailingUser) + ""; - } - - // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here - // adding the server name to make sure we don't replace external links - if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false) - { - string serverName = HttpContext.Current.Request.ServerVariables["SERVER_NAME"]; - mail.Body = mail.Body.Replace( - string.Format("http://{0}", serverName), - string.Format("https://{0}", serverName)); - } - - // send it - var sender = new SmtpClient(); - sender.Send(mail); - } - - private static string ReplaceLinks(string text) - { - string domain = GlobalSettings.UseSSL ? "https://" : "http://"; - domain += HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + "/"; - text = text.Replace("href=\"/", "href=\"" + domain); - text = text.Replace("src=\"/", "src=\"" + domain); - return text; + nService.SendNotifications( + pUser, documentObject.Content, action.Letter.ToString(CultureInfo.InvariantCulture), ui.Text(action.Alias), + new HttpContextWrapper(HttpContext.Current), + (user, strings) => ui.Text("notifications", "mailSubject", strings, mailingUser), + (user, strings) => UmbracoSettings.NotificationDisableHtmlEmail + ? ui.Text("notifications", "mailBody", strings, mailingUser) + : ui.Text("notifications", "mailBodyHtml", strings, mailingUser)); } /// @@ -327,80 +208,5 @@ namespace umbraco.cms.businesslogic.workflow MakeNew(user, node, c); } - /// - /// Replaces the HTML symbols with the character equivalent. - /// - /// The old string. - private static void ReplaceHtmlSymbols(ref string oldString) - { - oldString = oldString.Replace(" ", " "); - oldString = oldString.Replace("’", "'"); - oldString = oldString.Replace("&", "&"); - oldString = oldString.Replace("“", ""); - oldString = oldString.Replace("”", ""); - oldString = oldString.Replace(""", "\""); - } - - /// - /// Compares the text. - /// - /// The old text. - /// The new text. - /// if set to true [display inserted text]. - /// if set to true [display deleted text]. - /// The inserted style. - /// The deleted style. - /// - private static string CompareText(string oldText, string newText, bool displayInsertedText, - bool displayDeletedText, string insertedStyle, string deletedStyle) - { - var sb = new StringBuilder(); - Diff.Item[] diffs = Diff.DiffText1(oldText, newText); - - int pos = 0; - for (int n = 0; n < diffs.Length; n++) - { - Diff.Item it = diffs[n]; - - // write unchanged chars - while ((pos < it.StartB) && (pos < newText.Length)) - { - sb.Append(newText[pos]); - pos++; - } // while - - // write deleted chars - if (displayDeletedText && it.deletedA > 0) - { - sb.Append(deletedStyle); - for (int m = 0; m < it.deletedA; m++) - { - sb.Append(oldText[it.StartA + m]); - } // for - sb.Append(""); - } - - // write inserted chars - if (displayInsertedText && pos < it.StartB + it.insertedB) - { - sb.Append(insertedStyle); - while (pos < it.StartB + it.insertedB) - { - sb.Append(newText[pos]); - pos++; - } // while - sb.Append(""); - } // if - } // while - - // write rest of unchanged chars - while (pos < newText.Length) - { - sb.Append(newText[pos]); - pos++; - } // while - - return sb.ToString(); - } } } \ No newline at end of file From aebc30c7a399206e1eaa7045ff31310ad260f0bb Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 13 Jan 2014 14:02:50 +1100 Subject: [PATCH 011/100] Removes the version specific info from the CDF and Examine references in all proj files. --- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 6 +++--- src/Umbraco.Web.UI/config/ClientDependency.config | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 ++-- src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj | 4 ++-- .../UmbracoExamine.PDF.Azure.csproj | 4 ++-- src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj | 2 +- src/UmbracoExamine/UmbracoExamine.csproj | 2 +- src/umbraco.cms/umbraco.cms.csproj | 2 +- src/umbraco.controls/umbraco.controls.csproj | 2 +- src/umbraco.editorControls/umbraco.editorControls.csproj | 2 +- src/umbraco.macroRenderings/umbraco.macroRenderings.csproj | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index db4fb63e84..ac240834d5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -48,7 +48,7 @@ 4 - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index f993e4fce4..2af0a7cf5d 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -101,15 +101,15 @@ {07fbc26b-2927-4a22-8d96-d644c667fecc} UmbracoExamine - + False ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll - + False ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index acd5d07260..6c1ff881cf 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -10,7 +10,7 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - + @@ -35,6 +35,7 @@ + From e36b3d46e82e7b133dcc83ef8a5f2ab4aad534e6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 11:43:56 +1100 Subject: [PATCH 023/100] Fixes: U4-4039 @Umbraco.Field("writerName") etc. don't work any more --- src/Umbraco.Web/umbraco.presentation/item.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index 5a6a1b944f..d2a2aada5a 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -79,7 +79,7 @@ namespace umbraco else { //check for published content and get its value using that - if (publishedContent != null) + if (publishedContent != null && publishedContent.HasProperty(_fieldName)) { var pval = publishedContent.GetPropertyValue(_fieldName); var rval = pval == null ? string.Empty : pval.ToString(); @@ -94,11 +94,22 @@ namespace umbraco //now we check if the value is still empty and if so we'll check useIfEmpty if (string.IsNullOrEmpty(_fieldContent)) { + //if useIfEmpty is true if (string.IsNullOrEmpty(helper.FindAttribute(attributes, "useIfEmpty")) == false) { - if (elements[helper.FindAttribute(attributes, "useIfEmpty")] != null && string.IsNullOrEmpty(elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString()) == false) + var altFieldName = helper.FindAttribute(attributes, "useIfEmpty"); + + //check for published content and get its value using that + if (publishedContent != null && publishedContent.HasProperty(altFieldName)) { - _fieldContent = elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString().Trim(); + var pval = publishedContent.GetPropertyValue(altFieldName); + var rval = pval == null ? string.Empty : pval.ToString(); + _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; + } + else if (elements[altFieldName] != null && string.IsNullOrEmpty(elements[altFieldName].ToString()) == false) + { + //get the vaue the legacy way (this will not parse locallinks, etc... since that is handled with ipublishedcontent) + _fieldContent = elements[altFieldName].ToString().Trim(); } } } From 7d57521ad842a9e28fedd5963a0790e257556d12 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 11:53:41 +1100 Subject: [PATCH 024/100] Fixes some thread safety issues with old membership API --- src/umbraco.cms/businesslogic/member/Member.cs | 1 + src/umbraco.cms/businesslogic/member/MemberType.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index 9bb8638773..b10bd3c4ad 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -212,6 +212,7 @@ namespace umbraco.cms.businesslogic.member /// The umbraco usercontext /// The email of the user /// The new member + [MethodImpl(MethodImplOptions.Synchronized)] public static Member MakeNew(string Name, string LoginName, string Email, MemberType mbt, User u) { if (mbt == null) throw new ArgumentNullException("mbt"); diff --git a/src/umbraco.cms/businesslogic/member/MemberType.cs b/src/umbraco.cms/businesslogic/member/MemberType.cs index e5ff610cc2..0a0612dc83 100644 --- a/src/umbraco.cms/businesslogic/member/MemberType.cs +++ b/src/umbraco.cms/businesslogic/member/MemberType.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Xml; using Umbraco.Core.Logging; using umbraco.cms.businesslogic.propertytype; @@ -236,6 +237,7 @@ namespace umbraco.cms.businesslogic.member /// /// The name of the MemberType /// Creator of the MemberType + [MethodImpl(MethodImplOptions.Synchronized)] public static MemberType MakeNew(User u, string Text) { int ParentId = -1; From eedd10933217f1385896370b47695d9c8cb01eb6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 12:38:48 +1100 Subject: [PATCH 025/100] Temp fix for: U4-3968 Umbraco 7.01 - Membership - Public Access - Add Single User protection - crash --- src/Umbraco.Core/Models/MemberType.cs | 24 +++++++++++++++++++ src/umbraco.cms/businesslogic/ContentType.cs | 11 +++++++-- .../businesslogic/member/MemberType.cs | 5 +++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index c361891fa0..b2e3a4dc89 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Models { //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId private IDictionary> _memberTypePropertyTypes; + private string _alias; public MemberType(int parentId) : base(parentId) { @@ -27,6 +28,29 @@ namespace Umbraco.Core.Models } private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + + /// + /// The Alias of the ContentType + /// + [DataMember] + public override string Alias + { + get { return _alias; } + set + { + //NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of + // "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips + // leading underscores which we don't want in this case. + // see : http://issues.umbraco.org/issue/U4-3968 + + SetPropertyValueAndDetectChanges(o => + { + _alias = value == "_umbracoSystemDefaultProtectType" ? value : value.ToSafeAlias(); + return _alias; + }, _alias, AliasSelector); + } + } /// /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. diff --git a/src/umbraco.cms/businesslogic/ContentType.cs b/src/umbraco.cms/businesslogic/ContentType.cs index 71d676f54d..1f14459c40 100644 --- a/src/umbraco.cms/businesslogic/ContentType.cs +++ b/src/umbraco.cms/businesslogic/ContentType.cs @@ -199,10 +199,17 @@ namespace umbraco.cms.businesslogic /// The Alias of the ContentType /// The Iconurl of Contents of this ContentType protected static void Create(int NodeId, string Alias, string IconUrl) + { + Create(NodeId, Alias, IconUrl, true); + } + + internal static void Create(int nodeId, string alias, string iconUrl, bool formatAlias) { SqlHelper.ExecuteNonQuery( - "Insert into cmsContentType (nodeId,alias,icon) values (" + NodeId + ",'" + helpers.Casing.SafeAliasWithForcingCheck(Alias) + - "','" + IconUrl + "')"); + "Insert into cmsContentType (nodeId,alias,icon) values (" + + nodeId + ",'" + + (formatAlias ? helpers.Casing.SafeAliasWithForcingCheck(alias) : alias) + + "','" + iconUrl + "')"); } /// diff --git a/src/umbraco.cms/businesslogic/member/MemberType.cs b/src/umbraco.cms/businesslogic/member/MemberType.cs index e5ff610cc2..d19461fe2e 100644 --- a/src/umbraco.cms/businesslogic/member/MemberType.cs +++ b/src/umbraco.cms/businesslogic/member/MemberType.cs @@ -243,7 +243,10 @@ namespace umbraco.cms.businesslogic.member Guid uniqueId = Guid.NewGuid(); CMSNode n = CMSNode.MakeNew(ParentId, _objectType, u.Id, level, Text, uniqueId); - ContentType.Create(n.Id, Text, ""); + ContentType.Create(n.Id, Text, "", + //don't format the alias if it equals this. See: http://issues.umbraco.org/issue/U4-3968 + Text != "_umbracoSystemDefaultProtectType"); + MemberType mt = new MemberType(n.Id); mt.IconUrl = "member.gif"; NewEventArgs e = new NewEventArgs(); From 86c66511dd0b5645146bf766233fb453aadf8c44 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 14:17:28 +1100 Subject: [PATCH 026/100] changes all calls from Member.InUmbracoMemberMode() to Membership.Provider.IsUmbracoMembershipProvider() and obsoletes the InUmbracoMemberMode --- src/Umbraco.Core/Constants-Conventions.cs | 2 ++ .../Security/MembershipProviderExtensions.cs | 24 +++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../umbraco/Trees/loadMemberTypes.cs | 4 ++- .../umbraco/Trees/loadMembers.cs | 6 +++-- .../umbraco/create/member.ascx.cs | 2 +- .../umbraco/create/memberTasks.cs | 3 ++- .../umbraco/dialogs/protectPage.aspx.cs | 27 ++++++++++--------- .../umbraco/members/EditMember.aspx.cs | 9 ++++--- .../umbraco/members/MemberSearch.ascx.cs | 5 ++-- .../umbraco/members/ViewMembers.aspx.cs | 6 +++-- .../businesslogic/member/Member.cs | 4 ++- 12 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 src/Umbraco.Core/Security/MembershipProviderExtensions.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index ffa84f025e..c2b5fa9f54 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -255,6 +255,8 @@ namespace Umbraco.Core /// MemberType alias for default member type. /// public const string Member = "Member"; + + public const string SystemDefaultProtectType = "_umbracoSystemDefaultProtectType"; } /// diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs new file mode 100644 index 0000000000..daa4623747 --- /dev/null +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Security; +using Umbraco.Core.Security; + +namespace Umbraco.Core.Security +{ + internal static class MembershipProviderExtensions + { + /// + /// Returns true if the provider specified is a built-in Umbraco membership provider + /// + /// + /// + public static bool IsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (membershipProvider is UmbracoMembershipProviderBase); + } + + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4c26172f17..90bf089d0f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -741,6 +741,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs index 9452b990e6..19ccefe64d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMemberTypes.cs @@ -5,8 +5,10 @@ using System.Data; using System.IO; using System.Text; using System.Web; +using System.Web.Security; using System.Xml; using System.Configuration; +using Umbraco.Core.Security; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.businesslogic; @@ -39,7 +41,7 @@ namespace umbraco protected override void CreateRootNode(ref XmlTreeNode rootNode) { // only show member types if we're using umbraco members on the website - if (Member.InUmbracoMemberMode()) + if (Membership.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 26e0a59b40..dee3940b96 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs @@ -5,8 +5,10 @@ using System.Data; using System.IO; using System.Text; using System.Web; +using System.Web.Security; using System.Xml; using System.Configuration; +using Umbraco.Core.Security; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.businesslogic; @@ -156,7 +158,7 @@ function openContentItem(id) { } else { - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { foreach (Member m in Member.getMemberFromFirstLetter(letter.ToCharArray()[0])) { @@ -220,7 +222,7 @@ function openContentItem(id) { } //Add folder named "Others", only supported by umbraco - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { XmlElement treeElementOther = Tree.CreateElement("tree"); treeElementOther.SetAttribute("menu", ""); 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 e60411bda6..4a611add0f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/member.ascx.cs @@ -23,7 +23,7 @@ namespace umbraco.cms.presentation.create.controls protected void Page_Load(object sender, System.EventArgs e) { sbmt.Text = ui.Text("create"); - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { nameLiteral.Text = ui.Text("name"); memberChooser.Attributes.Add("style", "padding-top: 10px"); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs index c3a5c92737..c60c8474eb 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/memberTasks.cs @@ -2,6 +2,7 @@ using System; using System.Data; using System.Globalization; using System.Web.Security; +using Umbraco.Core.Security; using umbraco.BusinessLogic; using umbraco.DataLayer; using umbraco.BasePages; @@ -75,7 +76,7 @@ namespace umbraco string email = nameAndMail.Length > 0 ? nameAndMail[1] : ""; string password = nameAndMail.Length > 1 ? nameAndMail[2] : ""; string loginName = nameAndMail.Length > 2 ? nameAndMail[3] : ""; - if (Member.InUmbracoMemberMode() && TypeID != -1) + if (Membership.Provider.IsUmbracoMembershipProvider() && TypeID != -1) { var dt = new MemberType(TypeID); var provider = (providers.members.UmbracoMembershipProvider) Membership.Provider; 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 59a6ba54c3..50a4a033e3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -3,11 +3,13 @@ using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Windows.Forms.VisualStyles; +using Umbraco.Core; using umbraco.cms.businesslogic.member; using umbraco.cms.businesslogic.web; using umbraco.controls; using umbraco.cms.helpers; using umbraco.BasePages; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.dialogs { @@ -183,11 +185,11 @@ namespace umbraco.presentation.umbraco.dialogs if (e.CommandName == "simple") { - MembershipUser member = Membership.GetUser(simpleLogin.Text); + var member = Membership.GetUser(simpleLogin.Text); if (member == null) { // this needs to work differently depending on umbraco members or external membership provider - if (!cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider() == false) { member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text); } @@ -195,19 +197,18 @@ namespace umbraco.presentation.umbraco.dialogs { try { - if ( - cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType") == null) + if (cms.businesslogic.member.MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); + cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } } catch { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); + cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } // create member - Member mem = cms.businesslogic.member.Member.MakeNew(simpleLogin.Text, "", cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType"), base.getUser()); + Member mem = cms.businesslogic.member.Member.MakeNew(simpleLogin.Text, "", cms.businesslogic.member.MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType), UmbracoUser); // working around empty password restriction for Umbraco Member Mode mem.Password = simplePassword.Text; member = Membership.GetUser(simpleLogin.Text); @@ -215,6 +216,8 @@ namespace umbraco.presentation.umbraco.dialogs } else { + //Membership.Provider.ChangePassword(member.UserName, ) + // change password if it's not empty if (string.IsNullOrWhiteSpace(simplePassword.Text) == false) { @@ -225,11 +228,11 @@ namespace umbraco.presentation.umbraco.dialogs // Create or find a memberGroup string simpleRoleName = "__umbracoRole_" + simpleLogin.Text; - if (!Roles.RoleExists(simpleRoleName)) + if (Roles.RoleExists(simpleRoleName) == false) { Roles.CreateRole(simpleRoleName); } - if (!Roles.IsUserInRole(member.UserName, simpleRoleName)) + if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false) { Roles.AddUserToRole(member.UserName, simpleRoleName); } @@ -240,13 +243,13 @@ namespace umbraco.presentation.umbraco.dialogs } else if (e.CommandName == "advanced") { - cms.businesslogic.web.Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); + Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); foreach (ListItem li in _memberGroups.Items) if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",") > -1) - cms.businesslogic.web.Access.AddMembershipRoleToDocument(pageId, li.Value); + Access.AddMembershipRoleToDocument(pageId, li.Value); else - cms.businesslogic.web.Access.RemoveMembershipRoleFromDocument(pageId, li.Value); + Access.RemoveMembershipRoleFromDocument(pageId, li.Value); } feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text, null) + "

" + ui.Text("closeThisWindow") + ""; 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 096958e901..600b769c6a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs @@ -5,6 +5,7 @@ using System.Web.UI.Design.WebControls; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using Umbraco.Core.IO; +using Umbraco.Core.Security; using Umbraco.Web; using Umbraco.Web.Security; using umbraco.cms.businesslogic.member; @@ -63,7 +64,7 @@ namespace umbraco.cms.presentation.members MemberPasswordTxt.Controls.Add(passwordChanger); MemberPasswordTxt.Controls.Add(validatorContainer); - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { _document = new Member(int.Parse(Request.QueryString["id"])); _member = Membership.GetUser(_document.LoginName, false); @@ -163,7 +164,7 @@ namespace umbraco.cms.presentation.members p.addProperty(ui.Text("membergroup"), _memberGroups); - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { _contentControl.tpProp.Controls.Add(p); _contentControl.Save += new System.EventHandler(tmp_save); @@ -279,7 +280,7 @@ namespace umbraco.cms.presentation.members else { // hide validation summaries - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { foreach (uicontrols.TabPage tp in _contentControl.GetPanels()) { @@ -295,7 +296,7 @@ namespace umbraco.cms.presentation.members //update the membership provider UpdateMembershipProvider(_member); - if (Member.InUmbracoMemberMode()) + if (Membership.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 c165310b3c..bc95326f49 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs @@ -6,6 +6,7 @@ using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; using umbraco.cms.businesslogic.member; using System.Web.Security; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.members { @@ -13,7 +14,7 @@ namespace umbraco.presentation.umbraco.members { protected void Page_Load(object sender, EventArgs e) { - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) ButtonSearch.Text = ui.Text("search"); } @@ -22,7 +23,7 @@ namespace umbraco.presentation.umbraco.members { resultsPane.Visible = true; - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { var query = searchQuery.Text.ToLower(); var internalSearcher = UmbracoContext.Current.InternalMemberSearchProvider; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs index 271f71e8b9..e26847b1e7 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Web; +using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; +using Umbraco.Core.Security; namespace umbraco.presentation.members { public partial class ViewMembers : BasePages.UmbracoEnsuredPage { @@ -20,7 +22,7 @@ namespace umbraco.presentation.members { private void bindRp() { string _letter = Request.QueryString["letter"]; if (!string.IsNullOrEmpty(_letter)) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { if (_letter == "#") { @@ -41,7 +43,7 @@ namespace umbraco.presentation.members { public void bindMember(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { cms.businesslogic.member.Member mem = (cms.businesslogic.member.Member)e.Item.DataItem; Literal _name = (Literal)e.Item.FindControl("lt_name"); diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index b10bd3c4ad..ef26845304 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -20,6 +20,7 @@ using System.Web.Security; using System.Text; using System.Security.Cryptography; using System.Linq; +using Umbraco.Core.Security; namespace umbraco.cms.businesslogic.member { @@ -363,9 +364,10 @@ namespace umbraco.cms.businesslogic.member return new Member(tmpId); } + [Obsolete("Use MembershipProviderExtensions.IsUmbracoMembershipProvider instead")] public static bool InUmbracoMemberMode() { - return Membership.Provider.Name == UmbracoMemberProviderName; + return Membership.Provider.IsUmbracoMembershipProvider(); } public static bool IsUsingUmbracoRoles() From 563867cd8b40d2b8367d8e0b1760b5034862c528 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 16:21:00 +1100 Subject: [PATCH 027/100] Temp fix for: U4-3968 Umbraco 7.01 - Membership - Public Access - Add Single User protection - crash, fixes protectPage so that it uses the membership provider to make changes, updates the UI logic so that if you choose an existing user it will you about it, fixes it so you cannot arbitrarily change a member's password. --- src/Umbraco.Core/Models/MemberType.cs | 24 + .../Security/MembershipProviderExtensions.cs | 5 + .../umbraco/dialogs/protectPage.aspx | 17 +- src/Umbraco.Web/Umbraco.Web.csproj | 17 +- .../umbraco/controls/passwordChanger.ascx.cs | 4 +- .../umbraco/dialogs/protectPage.aspx | 170 ------- .../umbraco/dialogs/protectPage.aspx.cs | 418 +++++++++++++++--- .../dialogs/protectPage.aspx.designer.cs | 295 ------------ src/umbraco.cms/businesslogic/ContentType.cs | 11 +- .../businesslogic/member/MemberType.cs | 5 +- 10 files changed, 411 insertions(+), 555 deletions(-) delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index 2b32681392..01a3d57738 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Models { //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId private IDictionary> _memberTypePropertyTypes; + private string _alias; public MemberType(int parentId) : base(parentId) { @@ -27,6 +28,29 @@ namespace Umbraco.Core.Models } private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + + ///

+ /// The Alias of the ContentType + /// + [DataMember] + public override string Alias + { + get { return _alias; } + set + { + //NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of + // "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips + // leading underscores which we don't want in this case. + // see : http://issues.umbraco.org/issue/U4-3968 + + SetPropertyValueAndDetectChanges(o => + { + _alias = value == "_umbracoSystemDefaultProtectType" ? value : value.ToSafeAlias(); + return _alias; + }, _alias, AliasSelector); + } + } /// /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index daa4623747..85db27ac97 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -20,5 +20,10 @@ namespace Umbraco.Core.Security return (membershipProvider is UmbracoMembershipProviderBase); } + public static UmbracoMembershipProviderBase AsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (UmbracoMembershipProviderBase)membershipProvider; + } + } } diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx index 19edd39dfc..8f57f60b7c 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx +++ b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx @@ -1,7 +1,6 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="protectPage.aspx.cs" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> +<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - -
-

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

-

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

-
-
- - -
-

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

-

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

- - -

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

-
- -
-
-

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

-
- - - - -

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

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

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

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

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

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

-
- - -
- - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs index 50a4a033e3..e301ae3de8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -1,9 +1,11 @@ -using System; +using System; +using System.Globalization; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Windows.Forms.VisualStyles; using Umbraco.Core; +using Umbraco.Core.Logging; using umbraco.cms.businesslogic.member; using umbraco.cms.businesslogic.web; using umbraco.controls; @@ -24,11 +26,16 @@ namespace umbraco.presentation.umbraco.dialogs } - protected System.Web.UI.WebControls.Literal jsShowWindow; + protected Literal jsShowWindow; protected DualSelectbox _memberGroups = new DualSelectbox(); protected ContentPicker loginPagePicker = new ContentPicker(); protected ContentPicker errorPagePicker = new ContentPicker(); + override protected void OnInit(EventArgs e) + { + base.OnInit(e); + } + protected void selectMode(object sender, EventArgs e) { p_mode.Visible = false; @@ -48,11 +55,11 @@ namespace umbraco.presentation.umbraco.dialogs } } - protected void Page_Load(object sender, System.EventArgs e) + protected void Page_Load(object sender, EventArgs e) { // Check for editing int documentId = int.Parse(helper.Request("nodeId")); - cms.businesslogic.web.Document documentObject = new cms.businesslogic.web.Document(documentId); + var documentObject = new Document(documentId); jsShowWindow.Text = ""; ph_errorpage.Controls.Add(errorPagePicker); @@ -63,12 +70,12 @@ namespace umbraco.presentation.umbraco.dialogs pp_loginPage.Text = ui.Text("paLoginPage"); pp_errorPage.Text = ui.Text("paErrorPage"); - pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", base.getUser()); - pane_pages.Text = ui.Text("publicAccess", "paSelectPages", base.getUser()); - pane_simple.Text = ui.Text("publicAccess", "paSimple", base.getUser()); - pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", base.getUser()); + pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", UmbracoUser); + pane_pages.Text = ui.Text("publicAccess", "paSelectPages", UmbracoUser); + pane_simple.Text = ui.Text("publicAccess", "paSimple", UmbracoUser); + pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", UmbracoUser); - if (!IsPostBack) + if (IsPostBack == false) { if (Access.IsProtected(documentId, documentObject.Path) && Access.GetProtectionType(documentId) != ProtectionType.NotProtected) { @@ -80,23 +87,27 @@ namespace umbraco.presentation.umbraco.dialogs int loginPage = Access.GetLoginPage(documentObject.Path); try { - Document loginPageObj = new Document(loginPage); + var loginPageObj = new Document(loginPage); if (loginPageObj != null) { - loginPagePicker.Value = loginPage.ToString(); + loginPagePicker.Value = loginPage.ToString(CultureInfo.InvariantCulture); } - Document errorPageObj = new Document(errorPage); - errorPagePicker.Value = errorPage.ToString(); + var errorPageObj = new Document(errorPage); + errorPagePicker.Value = errorPage.ToString(CultureInfo.InvariantCulture); } - catch + catch (Exception ex) { + LogHelper.Error("An error occurred initializing the protect page editor", ex); } if (Access.GetProtectionType(documentId) == ProtectionType.Simple) { - MembershipUser m = Access.GetAccessingMembershipUser(documentId); - simpleLogin.Text = m.UserName; + 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"; @@ -116,17 +127,17 @@ namespace umbraco.presentation.umbraco.dialogs // Load up membergrouops _memberGroups.ID = "Membergroups"; _memberGroups.Width = 175; - string selectedGroups = ""; - string[] _roles = Roles.GetAllRoles(); + var selectedGroups = ""; + var roles = Roles.GetAllRoles(); - if (_roles.Length > 0) + if (roles.Length > 0) { - foreach (string role in _roles) + foreach (string role in roles) { ListItem li = new ListItem(role, role); - if (!IsPostBack) + if (IsPostBack == false) { - if (cms.businesslogic.web.Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) + if (Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) selectedGroups += role + ","; } _memberGroups.Items.Add(li); @@ -147,26 +158,14 @@ namespace umbraco.presentation.umbraco.dialogs // Put user code to initialize the page here } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) + protected void ChangeOnClick(object sender, EventArgs e) { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); + SimpleLoginNameValidator.IsValid = true; + SimpleLoginLabel.Visible = false; + simpleLogin.Visible = true; + pp_pass.Visible = true; } - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - protected void protect_Click(object sender, CommandEventArgs e) { if (string.IsNullOrEmpty(errorPagePicker.Value)) @@ -175,55 +174,52 @@ namespace umbraco.presentation.umbraco.dialogs if (string.IsNullOrEmpty(loginPagePicker.Value)) cv_loginPage.IsValid = false; + //reset + SimpleLoginNameValidator.IsValid = true; if (Page.IsValid) { int pageId = int.Parse(helper.Request("nodeId")); - p_buttons.Visible = false; - pane_advanced.Visible = false; - pane_simple.Visible = false; - + if (e.CommandName == "simple") { var member = Membership.GetUser(simpleLogin.Text); 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) { - member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text); + member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text, tempEmail); } else { - try + //if it's the umbraco membership provider, then we need to tell it what member type to create it with + if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) { - if (cms.businesslogic.member.MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) - { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); - } + MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } - catch + var provider = Membership.Provider.AsUmbracoMembershipProvider(); + MembershipCreateStatus status; + member = provider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, + simpleLogin.Text, simplePassword.Text, tempEmail, null, null, true, null, out status); + if (status != MembershipCreateStatus.Success) { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); + SimpleLoginNameValidator.IsValid = false; + SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; + return; } - - // create member - Member mem = cms.businesslogic.member.Member.MakeNew(simpleLogin.Text, "", cms.businesslogic.member.MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType), UmbracoUser); - // working around empty password restriction for Umbraco Member Mode - mem.Password = simplePassword.Text; - member = Membership.GetUser(simpleLogin.Text); } } - else + else if (pp_pass.Visible) { - //Membership.Provider.ChangePassword(member.UserName, ) - - // change password if it's not empty - if (string.IsNullOrWhiteSpace(simplePassword.Text) == false) - { - var mem = Member.GetMemberFromLoginName(simpleLogin.Text); - mem.Password = simplePassword.Text; - } + SimpleLoginNameValidator.IsValid = false; + SimpleLoginLabel.Visible = true; + SimpleLoginLabel.Text = simpleLogin.Text; + simpleLogin.Visible = false; + pp_pass.Visible = false; + return; } // Create or find a memberGroup @@ -246,7 +242,7 @@ namespace umbraco.presentation.umbraco.dialogs Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); foreach (ListItem li in _memberGroups.Items) - if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",") > -1) + if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",", StringComparison.Ordinal) > -1) Access.AddMembershipRoleToDocument(pageId, li.Value); else Access.RemoveMembershipRoleFromDocument(pageId, li.Value); @@ -254,6 +250,10 @@ namespace umbraco.presentation.umbraco.dialogs feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text, null) + "

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

+ /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// feedback control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback feedback; + + /// + /// p_mode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_mode; + + /// + /// pane_chooseMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_chooseMode; + + /// + /// rb_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_simple; + + /// + /// rb_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_advanced; + + /// + /// p_noGroupsFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; + + /// + /// bt_selectMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_selectMode; + + /// + /// pane_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_simple; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// pp_login control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_login; + + /// + /// simpleLogin control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simpleLogin; + + /// + /// pp_pass control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_pass; + + /// + /// simplePassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simplePassword; + + /// + /// pane_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_advanced; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// groupsSelector control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; + + /// + /// p_buttons control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_buttons; + + /// + /// pane_pages control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_pages; + + /// + /// pp_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; + + /// + /// ph_loginpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; + + /// + /// cv_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; + + /// + /// pp_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; + + /// + /// ph_errorpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; + + /// + /// cv_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; + + /// + /// bt_protect control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_protect; + + /// + /// bt_buttonRemoveProtection control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; + + /// + /// errorId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; + + /// + /// loginId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; + + /// + /// js control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder js; + + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs deleted file mode 100644 index 6ed66eea24..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs +++ /dev/null @@ -1,295 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.4200 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class protectPage { - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// feedback control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback feedback; - - /// - /// p_mode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_mode; - - /// - /// pane_chooseMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_chooseMode; - - /// - /// rb_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_simple; - - /// - /// rb_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_advanced; - - /// - /// p_noGroupsFound control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; - - /// - /// bt_selectMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_selectMode; - - /// - /// pane_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_simple; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// pp_login control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_login; - - /// - /// simpleLogin control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simpleLogin; - - /// - /// pp_pass control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_pass; - - /// - /// simplePassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simplePassword; - - /// - /// pane_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_advanced; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// groupsSelector control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; - - /// - /// p_buttons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_buttons; - - /// - /// pane_pages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_pages; - - /// - /// pp_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; - - /// - /// ph_loginpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; - - /// - /// cv_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; - - /// - /// pp_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; - - /// - /// ph_errorpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; - - /// - /// cv_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; - - /// - /// bt_protect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_protect; - - /// - /// bt_buttonRemoveProtection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; - - /// - /// errorId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; - - /// - /// loginId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; - - /// - /// js control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder js; - } -} diff --git a/src/umbraco.cms/businesslogic/ContentType.cs b/src/umbraco.cms/businesslogic/ContentType.cs index 71d676f54d..1f14459c40 100644 --- a/src/umbraco.cms/businesslogic/ContentType.cs +++ b/src/umbraco.cms/businesslogic/ContentType.cs @@ -199,10 +199,17 @@ namespace umbraco.cms.businesslogic /// The Alias of the ContentType /// The Iconurl of Contents of this ContentType protected static void Create(int NodeId, string Alias, string IconUrl) + { + Create(NodeId, Alias, IconUrl, true); + } + + internal static void Create(int nodeId, string alias, string iconUrl, bool formatAlias) { SqlHelper.ExecuteNonQuery( - "Insert into cmsContentType (nodeId,alias,icon) values (" + NodeId + ",'" + helpers.Casing.SafeAliasWithForcingCheck(Alias) + - "','" + IconUrl + "')"); + "Insert into cmsContentType (nodeId,alias,icon) values (" + + nodeId + ",'" + + (formatAlias ? helpers.Casing.SafeAliasWithForcingCheck(alias) : alias) + + "','" + iconUrl + "')"); } /// diff --git a/src/umbraco.cms/businesslogic/member/MemberType.cs b/src/umbraco.cms/businesslogic/member/MemberType.cs index 0a0612dc83..449531315c 100644 --- a/src/umbraco.cms/businesslogic/member/MemberType.cs +++ b/src/umbraco.cms/businesslogic/member/MemberType.cs @@ -245,7 +245,10 @@ namespace umbraco.cms.businesslogic.member Guid uniqueId = Guid.NewGuid(); CMSNode n = CMSNode.MakeNew(ParentId, _objectType, u.Id, level, Text, uniqueId); - ContentType.Create(n.Id, Text, ""); + ContentType.Create(n.Id, Text, "", + //don't format the alias if it equals this. See: http://issues.umbraco.org/issue/U4-3968 + Text != "_umbracoSystemDefaultProtectType"); + MemberType mt = new MemberType(n.Id); mt.IconUrl = "member.gif"; NewEventArgs e = new NewEventArgs(); From bf24e101d39bffe48c9ccc64a7e84f92a5450be9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 16:53:32 +1100 Subject: [PATCH 028/100] fixes some logic with the protect page if it's already protected --- .../umbraco/dialogs/protectPage.aspx.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 e042fef381..c914b9350b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -183,7 +183,9 @@ namespace umbraco.presentation.umbraco.dialogs if (e.CommandName == "simple") { - var member = Membership.GetUser(simpleLogin.Text); + var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; + + var member = Membership.GetUser(memberLogin); if (member == null) { var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com"; @@ -191,7 +193,7 @@ namespace umbraco.presentation.umbraco.dialogs // this needs to work differently depending on umbraco members or external membership provider if (Membership.Provider.IsUmbracoMembershipProvider() == false) { - member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text, tempEmail); + member = Membership.CreateUser(memberLogin, simplePassword.Text, tempEmail); } else { @@ -203,11 +205,12 @@ namespace umbraco.presentation.umbraco.dialogs var provider = Membership.Provider.AsUmbracoMembershipProvider(); MembershipCreateStatus status; member = provider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, - simpleLogin.Text, simplePassword.Text, tempEmail, null, null, true, null, out status); + memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); if (status != MembershipCreateStatus.Success) { SimpleLoginNameValidator.IsValid = false; SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; + SimpleLoginNameValidator.Text = "Could not create user: " + status; return; } } @@ -216,14 +219,14 @@ namespace umbraco.presentation.umbraco.dialogs { SimpleLoginNameValidator.IsValid = false; SimpleLoginLabel.Visible = true; - SimpleLoginLabel.Text = simpleLogin.Text; + SimpleLoginLabel.Text = memberLogin; simpleLogin.Visible = false; pp_pass.Visible = false; return; } // Create or find a memberGroup - string simpleRoleName = "__umbracoRole_" + simpleLogin.Text; + var simpleRoleName = "__umbracoRole_" + member.UserName; if (Roles.RoleExists(simpleRoleName) == false) { Roles.CreateRole(simpleRoleName); From 000c2637d6cdbe5fd302f6e03e8d4cb1555fad59 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 16:54:32 +1100 Subject: [PATCH 029/100] fixes view state and err msg --- src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx | 2 +- .../umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx index 8f57f60b7c..c9c325f8ba 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx +++ b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx @@ -110,7 +110,7 @@

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

- +

Member name already exists, click Change to use a different name or Update to continue

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 e301ae3de8..4bfb0ee7ad 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -208,6 +208,7 @@ namespace umbraco.presentation.umbraco.dialogs { SimpleLoginNameValidator.IsValid = false; SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; + SimpleLoginNameValidator.Text = "Could not create user: " + status; return; } } From 0632d73e32fd5918409e8be69e4350ebbdc81279 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 14 Jan 2014 16:56:38 +1100 Subject: [PATCH 030/100] fixes view state and err styling for protect page --- src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx index 5aa7dd86a7..b19dc8374d 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx +++ b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx @@ -119,8 +119,8 @@

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

- -

Member name already exists, click Change to use a different name or Update to continue

+ +

Member name already exists, click Change to use a different name or Update to continue

From 5c55d6a6e3ad22aafe7d989fd40a7a6f699d8610 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 14 Jan 2014 13:49:20 +0100 Subject: [PATCH 031/100] U4-4045 The "Empty" partial view snippet should have @inherits --- .../Umbraco/PartialViews/Templates/EmptyTemplate.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml index e69de29bb2..dda3a17f51 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml @@ -0,0 +1 @@ +@inherits Umbraco.Web.Macros.PartialViewMacroPage \ No newline at end of file From 03090e85fd04be5e8bb4af2023168c9fdfea5ea5 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 14 Jan 2014 13:51:08 +0100 Subject: [PATCH 032/100] U4-4046 Rename "empty template" to "empty" --- ...thCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} | 0 .../Templates/{EmptyTemplate.cshtml => Empty.cshtml} | 0 ...thCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} | 0 .../PartialViews/Templates/{EmptyTemplate.cshtml => Empty.cshtml} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/{EmptyTemplate (ForUseWithCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/{EmptyTemplate.cshtml => Empty.cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/{EmptyTemplate (ForUseWithCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/{EmptyTemplate.cshtml => Empty.cshtml} (100%) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml From ba2fa2bff4bd3a60160ec11231bf772de47dcd92 Mon Sep 17 00:00:00 2001 From: perploug Date: Tue, 14 Jan 2014 14:33:11 +0100 Subject: [PATCH 033/100] Legacy dialogs missing conversion, language + partial view --- .../Umbraco/create/PartialView.ascx | 4 ++-- .../umbraco/create/PartialViewMacro.ascx | 2 +- .../umbraco/create/language.ascx | 23 ++++++++++++------- .../umbraco/create/language.ascx | 22 +++++++++++------- .../umbraco/create/language.ascx.cs | 2 ++ .../umbraco/create/language.ascx.designer.cs | 21 +++++++++++++++-- 6 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx index bf10210713..b0cc4e634d 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx @@ -8,7 +8,7 @@ * - + Clean @@ -22,5 +22,5 @@ <%=umbraco.ui.Text("cancel")%> - + diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx index 9dc16377eb..7a4372d7c9 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx @@ -9,7 +9,7 @@ * - + diff --git a/src/Umbraco.Web.UI/umbraco/create/language.ascx b/src/Umbraco.Web.UI/umbraco/create/language.ascx index 272b618d1d..feffae0a34 100644 --- a/src/Umbraco.Web.UI/umbraco/create/language.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/language.ascx @@ -1,11 +1,18 @@ <%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> - +<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -
<%=umbraco.ui.Text("choose")%> <%=umbraco.ui.Text("language")%>:
- -
+ -
-<%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -
+ + + + * + + + + + <%=umbraco.ui.Text("cancel")%> + + + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx index 272b618d1d..c0d359b612 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx @@ -1,11 +1,17 @@ <%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> - +<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -
<%=umbraco.ui.Text("choose")%> <%=umbraco.ui.Text("language")%>:
- -
+ + + + + + * + + + + + <%=umbraco.ui.Text("cancel")%> + + -
-<%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs index 176fc1e46f..017b7fe9b4 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs @@ -23,7 +23,9 @@ namespace umbraco.cms.presentation.create.controls { // get all existing languages + pp1.Text = ui.Text("choose") + " " + ui.Text("language"); sbmt.Text = ui.Text("create"); + SortedList sortedCultures = new SortedList(); Cultures.Items.Clear(); Cultures.Items.Add(new ListItem(ui.Text("choose") + "...", "")); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs index 40ed65c41f..d3945627fd 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.4200 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,6 +12,15 @@ namespace umbraco.cms.presentation.create.controls { public partial class language { + /// + /// pp1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp1; + /// /// Cultures control. /// @@ -22,6 +30,15 @@ namespace umbraco.cms.presentation.create.controls { /// protected global::System.Web.UI.WebControls.DropDownList Cultures; + /// + /// RequiredFieldValidator1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; + /// /// sbmt control. /// From 0c5ebb26746340485f404707b0c3574709a49e0c Mon Sep 17 00:00:00 2001 From: perploug Date: Tue, 14 Jan 2014 14:40:00 +0100 Subject: [PATCH 034/100] Fixes umbPhotoGrid Filtering --- .../src/common/directives/html/umbphotofolder.directive.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js index 142bea84f5..244893fdc1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js @@ -34,15 +34,14 @@ angular.module("umbraco.directives.html") var fixedRowWidth = Math.max(element.width(), minWidth); scope.containerStyle = { width: fixedRowWidth + "px" }; - scope.rows = umbPhotoFolderHelper.buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin); if (attrs.filterBy) { scope.$watch(attrs.filterBy, function(newVal, oldVal) { - if (newVal !== oldVal) { + if (newVal && newVal !== oldVal) { var p = $filter('filter')(photos, newVal, false); scope.baseline = 0; - var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, 400); + var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin); scope.rows = m; } }); From 3df44e299060870f5cb2e8d084119f2a2ababc07 Mon Sep 17 00:00:00 2001 From: perploug Date: Tue, 14 Jan 2014 14:59:55 +0100 Subject: [PATCH 035/100] treesource editor changes to remove the search field --- .../src/views/prevalueeditors/treesource.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html index 080be5cc58..0e375cc106 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html @@ -13,6 +13,7 @@
  • + {{node.name}}
  • @@ -26,7 +27,7 @@ - + \ No newline at end of file From 9e9b94b1d5f7dac09251f33ad36efd733660b37e Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Tue, 14 Jan 2014 14:50:57 +0100 Subject: [PATCH 036/100] Correcting raiseEvents parameter for dictionary items --- src/Umbraco.Core/Services/PackagingService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 3dc7d8b61e..f1ba56b049 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -1001,7 +1001,7 @@ namespace Umbraco.Core.Services { var items = new List(); foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) - items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages)); + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents)); if (raiseEvents) DictionaryItemImported.RaiseEvent(new SaveEventArgs(items, false), this); @@ -1009,7 +1009,7 @@ namespace Umbraco.Core.Services return items; } - private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages) + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, bool raiseEvents) { var items = new List(); @@ -1021,7 +1021,7 @@ namespace Umbraco.Core.Services dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages); _localizationService.Save(dictionaryItem); items.Add(dictionaryItem); - items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, true)); + items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents)); return items; } From 7b1fd5378fb4ca1197338aa177bf6e8bb3d532b2 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Tue, 14 Jan 2014 15:03:39 +0100 Subject: [PATCH 037/100] Correcting the naming of events in the RelationService --- src/Umbraco.Core/Services/RelationService.cs | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 2f82af9fc9..8ae7d80745 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -337,7 +337,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } @@ -365,7 +365,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } @@ -428,7 +428,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); } /// @@ -447,7 +447,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationTypeSaved.RaiseEvent(new SaveEventArgs(relationType, false), this); + SavedRelationType.RaiseEvent(new SaveEventArgs(relationType, false), this); } /// @@ -466,7 +466,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationDeleted.RaiseEvent(new DeleteEventArgs(relation, false), this); + DeletedRelation.RaiseEvent(new DeleteEventArgs(relation, false), this); } /// @@ -485,7 +485,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationTypeDeleted.RaiseEvent(new DeleteEventArgs(relationType, false), this); + DeletedRelationType.RaiseEvent(new DeleteEventArgs(relationType, false), this); } /// @@ -508,7 +508,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationDeleted.RaiseEvent(new DeleteEventArgs(relations, false), this); + DeletedRelation.RaiseEvent(new DeleteEventArgs(relations, false), this); } #region Private Methods @@ -537,7 +537,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a Relation is Deleted /// - public static event TypedEventHandler> RelationDeleted; + public static event TypedEventHandler> DeletedRelation; /// /// Occurs before Saving a Relation @@ -547,7 +547,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a Relation is Saved /// - public static event TypedEventHandler> RelationSaved; + public static event TypedEventHandler> SavedRelation; /// /// Occurs before Deleting a RelationType @@ -557,7 +557,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a RelationType is Deleted /// - public static event TypedEventHandler> RelationTypeDeleted; + public static event TypedEventHandler> DeletedRelationType; /// /// Occurs before Saving a RelationType @@ -567,7 +567,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a RelationType is Saved /// - public static event TypedEventHandler> RelationTypeSaved; + public static event TypedEventHandler> SavedRelationType; #endregion } } \ No newline at end of file From d6b3d115b0ce39e65019d7eeb4f4447b2952dc88 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Tue, 14 Jan 2014 15:03:52 +0100 Subject: [PATCH 038/100] Correcting the naming of events in the PackagingService --- src/Umbraco.Core/Services/PackagingService.cs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index f1ba56b049..bb658c23f8 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -93,7 +93,7 @@ namespace Umbraco.Core.Services } if(raiseEvents) - ContentExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedContent.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -181,7 +181,7 @@ namespace Umbraco.Core.Services _contentService.Save(contents, userId); if(raiseEvents) - ContentImported.RaiseEvent(new SaveEventArgs(contents, false), this); + ImportedContent.RaiseEvent(new SaveEventArgs(contents, false), this); return contents; } @@ -195,7 +195,7 @@ namespace Umbraco.Core.Services _contentService.Save(contents, userId); if(raiseEvents) - ContentImported.RaiseEvent(new SaveEventArgs(contents, false), this); + ImportedContent.RaiseEvent(new SaveEventArgs(contents, false), this); return contents; } @@ -398,7 +398,7 @@ namespace Umbraco.Core.Services tabs); if (raiseEvents) - ContentTypeExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedContentType.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -478,7 +478,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - ContentTypeImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedContentType.RaiseEvent(new SaveEventArgs(list, false), this); return list; } @@ -804,7 +804,7 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString())); if (raiseEvents) - DataTypeExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedDataType.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -888,7 +888,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - DataTypeImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedDataType.RaiseEvent(new SaveEventArgs(list, false), this); return list; } @@ -974,7 +974,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - DictionaryItemExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedDictionaryItem.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -1004,7 +1004,7 @@ namespace Umbraco.Core.Services items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents)); if (raiseEvents) - DictionaryItemImported.RaiseEvent(new SaveEventArgs(items, false), this); + ImportedDictionaryItem.RaiseEvent(new SaveEventArgs(items, false), this); return items; } @@ -1107,7 +1107,7 @@ namespace Umbraco.Core.Services new XAttribute("FriendlyName", language.CultureName)); if (raiseEvents) - LanguageExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedLanguage.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -1143,7 +1143,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - LanguageImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedLanguage.RaiseEvent(new SaveEventArgs(list, false), this); return list; } @@ -1214,7 +1214,7 @@ namespace Umbraco.Core.Services } if(raiseEvents) - MediaExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedMedia.RaiseEvent(new SaveEventArgs(xml, false), this); return xml; } @@ -1395,7 +1395,7 @@ namespace Umbraco.Core.Services _fileService.SaveTemplate(templates, userId); if(raiseEvents) - TemplateImported.RaiseEvent(new SaveEventArgs(templates, false), this); + ImportedTemplate.RaiseEvent(new SaveEventArgs(templates, false), this); return templates; } @@ -1430,7 +1430,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Content is Imported and Saved /// - public static event TypedEventHandler> ContentImported; + public static event TypedEventHandler> ImportedContent; /// /// Occurs before Exporting Content @@ -1440,7 +1440,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Content is Exported to Xml /// - public static event TypedEventHandler> ContentExported; + public static event TypedEventHandler> ExportedContent; /// /// Occurs before Exporting Media @@ -1450,7 +1450,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Media is Exported to Xml /// - public static event TypedEventHandler> MediaExported; + public static event TypedEventHandler> ExportedMedia; /// /// Occurs before Importing ContentType @@ -1460,7 +1460,7 @@ namespace Umbraco.Core.Services /// /// Occurs after ContentType is Imported and Saved /// - public static event TypedEventHandler> ContentTypeImported; + public static event TypedEventHandler> ImportedContentType; /// /// Occurs before Exporting ContentType @@ -1470,7 +1470,7 @@ namespace Umbraco.Core.Services /// /// Occurs after ContentType is Exported to Xml /// - public static event TypedEventHandler> ContentTypeExported; + public static event TypedEventHandler> ExportedContentType; /// /// Occurs before Importing DataType @@ -1480,7 +1480,7 @@ namespace Umbraco.Core.Services /// /// Occurs after DataType is Imported and Saved /// - public static event TypedEventHandler> DataTypeImported; + public static event TypedEventHandler> ImportedDataType; /// /// Occurs before Exporting DataType @@ -1490,7 +1490,7 @@ namespace Umbraco.Core.Services /// /// Occurs after DataType is Exported to Xml /// - public static event TypedEventHandler> DataTypeExported; + public static event TypedEventHandler> ExportedDataType; /// /// Occurs before Importing DictionaryItem @@ -1500,7 +1500,7 @@ namespace Umbraco.Core.Services /// /// Occurs after DictionaryItem is Imported and Saved /// - public static event TypedEventHandler> DictionaryItemImported; + public static event TypedEventHandler> ImportedDictionaryItem; /// /// Occurs before Exporting DictionaryItem @@ -1510,7 +1510,7 @@ namespace Umbraco.Core.Services /// /// Occurs after DictionaryItem is Exported to Xml /// - public static event TypedEventHandler> DictionaryItemExported; + public static event TypedEventHandler> ExportedDictionaryItem; /// /// Occurs before Importing Language @@ -1520,7 +1520,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Language is Imported and Saved /// - public static event TypedEventHandler> LanguageImported; + public static event TypedEventHandler> ImportedLanguage; /// /// Occurs before Exporting Language @@ -1530,7 +1530,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Language is Exported to Xml /// - public static event TypedEventHandler> LanguageExported; + public static event TypedEventHandler> ExportedLanguage; /// /// Occurs before Importing Template @@ -1540,7 +1540,7 @@ namespace Umbraco.Core.Services /// /// Occurs after Template is Imported and Saved /// - public static event TypedEventHandler> TemplateImported; + public static event TypedEventHandler> ImportedTemplate; #endregion } } \ No newline at end of file From 14cfd2a8df3c49bcdada868c218ac0491ced08c9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 14 Jan 2014 15:21:20 +0100 Subject: [PATCH 039/100] D'oh .. now properly renamed, in the csproj as well - U4-4046 Rename "empty template" to "empty" --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index f97ff2ca1e..4bdd618fa2 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2135,13 +2135,13 @@ - + - + - + Code @@ -2161,7 +2161,7 @@ - + From 0da7ed8d93e4bb53f52f86213eb34f2dd3a57ec6 Mon Sep 17 00:00:00 2001 From: Bob Davidson Date: Tue, 14 Jan 2014 15:40:22 -0600 Subject: [PATCH 040/100] Fix for U4-3831. Removes form, adds ng-click with $event.preventDefault() to prevent premature saving. --- .../propertyeditors/relatedlinks/relatedlinks.controller.js | 3 ++- .../src/views/propertyeditors/relatedlinks/relatedlinks.html | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js index f275cdc25b..3e13e0014b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js @@ -43,7 +43,7 @@ $scope.model.value.splice(idx, 1); }; - $scope.add = function () { + $scope.add = function ($event) { if ($scope.newCaption == "") { $scope.hasError = true; } else { @@ -79,6 +79,7 @@ $scope.newInternalName = ''; } + $event.preventDefault(); }; $scope.switch = function ($event) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html index 5513d96a58..afd61a4e88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html @@ -51,7 +51,6 @@ -
    @@ -68,10 +67,9 @@
    - +
    - From 84da5f43a2df62fd63e9d469d4fcb13d2e6f6544 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 10:41:37 +1100 Subject: [PATCH 041/100] U4-4045 The "Empty" partial view snippet should have @inherits --- .../Umbraco/PartialViews/Templates/EmptyTemplate.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml index e69de29bb2..dda3a17f51 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml @@ -0,0 +1 @@ +@inherits Umbraco.Web.Macros.PartialViewMacroPage \ No newline at end of file From 75a42fd0d6a0fb57de4121169faa402085591e34 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 10:42:01 +1100 Subject: [PATCH 042/100] fixes the protect page logic --- .../umbraco/dialogs/protectPage.aspx.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 4bfb0ee7ad..2af1add9fa 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -183,7 +183,9 @@ namespace umbraco.presentation.umbraco.dialogs if (e.CommandName == "simple") { - var member = Membership.GetUser(simpleLogin.Text); + var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; + + var member = Membership.GetUser(memberLogin); if (member == null) { var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com"; @@ -191,7 +193,7 @@ namespace umbraco.presentation.umbraco.dialogs // this needs to work differently depending on umbraco members or external membership provider if (Membership.Provider.IsUmbracoMembershipProvider() == false) { - member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text, tempEmail); + member = Membership.CreateUser(memberLogin, simplePassword.Text, tempEmail); } else { @@ -203,7 +205,7 @@ namespace umbraco.presentation.umbraco.dialogs var provider = Membership.Provider.AsUmbracoMembershipProvider(); MembershipCreateStatus status; member = provider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, - simpleLogin.Text, simplePassword.Text, tempEmail, null, null, true, null, out status); + memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); if (status != MembershipCreateStatus.Success) { SimpleLoginNameValidator.IsValid = false; @@ -217,14 +219,14 @@ namespace umbraco.presentation.umbraco.dialogs { SimpleLoginNameValidator.IsValid = false; SimpleLoginLabel.Visible = true; - SimpleLoginLabel.Text = simpleLogin.Text; + SimpleLoginLabel.Text = memberLogin; simpleLogin.Visible = false; pp_pass.Visible = false; return; } // Create or find a memberGroup - string simpleRoleName = "__umbracoRole_" + simpleLogin.Text; + var simpleRoleName = "__umbracoRole_" + member.UserName; if (Roles.RoleExists(simpleRoleName) == false) { Roles.CreateRole(simpleRoleName); From 5a29441640e0a823fce279bacda65438da657dbd Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 10:53:14 +1100 Subject: [PATCH 043/100] Reverts GetByQuery and MNTP Xpath query WIP - is contained only in 7.1 --- .../src/common/resources/entity.resource.js | 10 ---- .../contentpicker/contentpicker.controller.js | 22 ++------ src/Umbraco.Web/Editors/EntityController.cs | 54 ------------------- 3 files changed, 4 insertions(+), 82 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 182b487f11..75d2f47a28 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -99,16 +99,6 @@ function entityResource($q, $http, umbRequestHelper) { 'Failed to retreive entity data for id ' + id); }, - getByQuery: function (query, rootNodeId, type) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "entityApiBaseUrl", - "GetByQuery", - [{query: query},{ rootNodeId: rootNodeId}, {type: type }])), - 'Failed to retreive entity data for query ' + query); - }, - /** * @ngdoc method * @name umbraco.resources.entityResource#getByIds diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 70de53bf24..ab1aeb06e2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -3,7 +3,7 @@ angular.module('umbraco') .controller("Umbraco.PropertyEditors.ContentPickerController", - function($scope, dialogService, entityResource, editorState, $log, iconHelper){ + function($scope, dialogService, entityResource, $log, iconHelper){ $scope.renderModel = []; $scope.ids = $scope.model.value ? $scope.model.value.split(',') : []; @@ -11,8 +11,6 @@ angular.module('umbraco') $scope.cfg = { multiPicker: "0", entityType: "Document", - filterCssClass: "not-allowed not-published", - startNode:{ type: "content", id: -1 @@ -33,23 +31,11 @@ angular.module('umbraco') $scope.cfg.entityType = "Media"; } - //if we have a query for the startnode, we will use that. - if($scope.cfg.startNode.query){ - var rootId = -1; - if($scope.cfg.startNode.scope === "current"){ - rootId = editorState.current.id; - } - - entityResource.getByQuery($scope.cfg.startNode.query, rootId, "Document").then(function(ent){ - $scope.cfg.startNodeId = ent.id; - }); - }else{ - $scope.cfg.startNodeId = $scope.cfg.startNode.id; - } - $scope.cfg.callback = populate; $scope.cfg.treeAlias = $scope.cfg.startNode.type; - $scope.cfg.section = $scope.cfg.startNode.type; + $scope.cfg.section = $scope.cfg.startNode.type; + $scope.cfg.startNodeId = $scope.cfg.startNode.id; + $scope.cfg.filterCssClass = "not-allowed not-published"; //load current data entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 4eef37e3ed..e30d67fe1d 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -25,7 +25,6 @@ using Examine; using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; using Umbraco.Web.Dynamics; -using umbraco; namespace Umbraco.Web.Editors { @@ -129,59 +128,6 @@ namespace Umbraco.Web.Editors return GetResultForKey(id, type); } - /// - /// Gets an entity by a xpath or css-like query - /// - /// - /// - /// - /// - public EntityBasic GetByQuery(string query, int rootNodeId, UmbracoEntityTypes type) - { - - //this is css (commented out for now, due to external dependency) - //if (!query.Contains("::") && !query.Contains('/')) - // query = css2xpath.Converter.CSSToXPath(query, ""); - - - if(rootNodeId < 0) - { - var node = Umbraco.TypedContentSingleAtXPath(query); - - if(node == null) - return null; - - return GetById(node.Id, UmbracoEntityTypes.Document); - } - else - { - //SD: This should be done using UmbracoHelper - - //var node = Umbraco.TypedContent(rootNodeId); - //if (node != null) - //{ - // //TODO: Build an Xpath query based on this node ID and the rest of the query - // // var subQuery = [@id=rootNodeId]/query - // // and then get that node with: - // // var result = Umbraco.TypedContentSingleAtXPath(subQuery); - //} - - var node = global::umbraco.library.GetXmlNodeById(rootNodeId.ToString()); - if (node.MoveNext()) - { - if (node.Current != null) - { - var result = node.Current.Select(query); - //set it to the first node found (if there is one), otherwise to -1 - if (result.Current != null) - return GetById(int.Parse(result.Current.GetAttribute("id", string.Empty)), UmbracoEntityTypes.Document); - } - } - } - - return null; - } - public EntityBasic GetById(int id, UmbracoEntityTypes type) { return GetResultForId(id, type); From 5caf09140677a263ade2e75340638ae77a91ee0e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 10:55:22 +1100 Subject: [PATCH 044/100] Revert "WIP: adds cropper directive" This reverts commit c5a7eab1d5496be7090a5bb686bc0a2f069992a6. new cropper is only contained in 7.1 branch --- .../imaging/umbImageCrop.directive.js | 265 ------------------ .../imaging/umbImageGravity.directive.js | 157 ----------- .../src/less/property-editors.less | 54 ---- .../directives/imaging/umb-image-crop.html | 32 --- .../directives/imaging/umb-image-gravity.html | 17 -- 5 files changed, 525 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageCrop.directive.js delete mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageGravity.directive.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-crop.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-gravity.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageCrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageCrop.directive.js deleted file mode 100644 index 2f8cf5ca60..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageCrop.directive.js +++ /dev/null @@ -1,265 +0,0 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbImageCrop -* @restrict E -* @function -**/ -angular.module("umbraco.directives") - .directive('umbImageCrop', function ($timeout, localizationService, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/directives/umb-image-crop.html', - scope: { - src: '@', - width: '@', - height: '@', - presets: '@', - crop: "=" - }, - link: function(scope, element, attrs) { - - scope.scale = 100; - - //if image is over this, we re-calculate the editors global ratio - //this will not have an effect on the result, since that is returned in percentage - scope.maxHeight = 500; - scope.maxWidth = 600; - - scope.width = 400; - scope.height = 320; - - scope.dimensions = { - image: {}, - cropper:{}, - viewport:{}, - margin: 20, - ratio: 1 - }; - - scope.style = function () { - return { - 'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px', - 'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px' - }; - }; - - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); - var $container = element.find(".crop-container"); - - //default constraints for drag n drop - var constraints = {left: {max: 20, min: 20}, top: {max: 20, min: 20}, }; - - var setDimensions = function(){ - scope.dimensions.image.width = $image.width(); - scope.dimensions.image.height = $image.height(); - - scope.dimensions.viewport.width = $viewport.width(); - scope.dimensions.viewport.height = $viewport.height(); - - scope.dimensions.cropper.width = scope.dimensions.viewport.width - 2 * scope.dimensions.margin; - scope.dimensions.cropper.height = scope.dimensions.viewport.height - 2 * scope.dimensions.margin; - }; - - var setImageSize = function(width, height){ - $image.width(width); - $image.height(height); - scope.dimensions.image.width = width; - scope.dimensions.image.height = height; - }; - - //when loading an image without any crop info, we center and fit it - var fitImage = function(){ - fitToViewPort($image); - centerImage($image); - syncOverLay(); - setConstraints($image); - }; - - //utill for centering scaled image - var centerImage = function(img) { - var image_width = img.width(), - image_height = img.height(), - mask_width = $viewport.width(), - mask_height = $viewport.height(); - - img.css({ - 'position': 'absolute', - 'left': scope.dimensions.viewport.width / 2 - scope.dimensions.image.width / 2, - 'top': scope.dimensions.viewport.height / 2 - scope.dimensions.image.height / 2 - }); - }; - - //utill for scaling image to fit viewport - var fitToViewPort = function(img) { - //returns size fitting the cropper - var size = calculateAspectRatioFit( - scope.dimensions.image.width, - scope.dimensions.image.height, - scope.dimensions.cropper.width, - scope.dimensions.cropper.height, - true); - - //sets the image size and updates the scope - setImageSize(size.width, size.height); - - scope.minScale = size.ratio; - scope.maxScale = size.ratio * 3; - scope.currentScale = scope.minScale; - scope.scale = scope.currentScale; - }; - - var resizeImageToScale = function(img, ratio){ - //do stuff - var size = calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); - - setImageSize(size.width, size.height); - centerImage(img); - scope.currentScale = scope.scale; - - syncOverLay(); - }; - - //set constaints for cropping drag and drop - var setConstraints = function(img){ - //do stuff - var w = img.width(), - h = img.height(), - crop_width = $viewport.width() - 2 * 20, - crop_height = $viewport.height() - 2 * 20; - - constraints.left.min = 20 + crop_width - w; - constraints.top.min = 20 + crop_height - h; - }; - - //utill for getting either min/max aspect ratio to scale image after - var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) { - var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ]; - - if(maximize){ - ratio = Math.max(ratio[0], ratio[1]); - }else{ - ratio = Math.min(ratio[0], ratio[1]); - } - - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - //utill for scaling width / height given a ratio - var calculateSizeToRatio= function(srcWidth, srcHeight, ratio) { - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - var calculateCropBox = function(){ - scope.crop.left = Math.abs($image[0].offsetLeft - scope.dimensions.margin) / scope.dimensions.image.width; - scope.crop.top = Math.abs($image[0].offsetTop - scope.dimensions.margin) / scope.dimensions.image.height; - - scope.crop.right = 1 - Math.abs(scope.dimensions.cropper.width - (scope.dimensions.image.width - scope.crop.left)) / scope.dimensions.image.width; - scope.crop.bottom = 1 - Math.abs(scope.dimensions.cropper.height - (scope.dimensions.image.height - scope.crop.top)) / scope.dimensions.image.height; - }; - - var calculatePosition = function(crop){ - - var left = (crop.left * scope.dimensions.image.originalWidth); - var top = (crop.top * scope.dimensions.image.originalHeight); - - var cropped_width = scope.dimensions.image.originalWidth - left; - var ratio = cropped_width / scope.dimensions.image.originalWidth; - - scope.scale = ratio; - resizeImageToScale($image, ratio); - - $image.css({ - "top": -top, - "left": -left - }); - - syncOverLay(); - }; - - - - var syncOverLay = function(){ - $overlay.height($image.height()); - $overlay.width($image.width()); - - $overlay.css({ - "top": $image[0].offsetTop, - "left": $image[0].offsetLeft - }); - - calculateCropBox(); - }; - - //Drag and drop positioning, using jquery ui draggable - var onStartDragPosition, top, left; - $overlay.draggable({ - start: function(event, ui) { - syncOverLay(); - }, - drag: function(event, ui) { - - - if(ui.position.left <= constraints.left.max && ui.position.left >= constraints.left.min){ - $image.css({ - 'left': ui.position.left - }); - } - - if(ui.position.top <= constraints.top.max && ui.position.top >= constraints.top.min){ - $image.css({ - 'top': ui.position.top - }); - } - }, - stop: function() { - syncOverLay(); - } - }); - - - - - - //// INIT ///// - $image.load(function(){ - $timeout(function(){ - $image.width("auto"); - $image.height("auto"); - - scope.dimensions.image.originalWidth = $image.width(); - scope.dimensions.image.originalHeight = $image.height(); - - setDimensions(); - - if(scope.crop && scope.crop.top){ - calculatePosition(scope.crop); - }else{ - fitImage(); - } - - scope.loaded = true; - }); - }); - - - /// WATCHERS //// - scope.$watch("scale", function(){ - if(scope.loaded && scope.scale !== scope.currentScale){ - resizeImageToScale($image, scope.scale); - setConstraints($image); - } - }); - - /// WATCHERS //// - scope.$watch("crop", function(newVal, oldVal){ - if(scope.loaded && newVal !== oldVal){ - calculatePosition(scope.crop); - } - }); - } - }; - }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageGravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageGravity.directive.js deleted file mode 100644 index 418e0f0709..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/imaging/umbImageGravity.directive.js +++ /dev/null @@ -1,157 +0,0 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbCropsy -* @restrict E -* @function -* @description -* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. -**/ -angular.module("umbraco.directives") - .directive('umbImageGravity', function ($timeout, localizationService, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/directives/umb-image-gravity.html', - scope: { - src: '@', - width: '@', - height: '@', - gravity: "=" - }, - link: function(scope, element, attrs) { - - scope.scale = 100; - - //if image is over this, we re-calculate the editors global ratio - //this will not have an effect on the result, since that is returned in percentage - scope.maxHeight = 500; - scope.maxWidth = 600; - - scope.width = 400; - scope.height = 320; - - scope.dimensions = { - image: {}, - viewport:{}, - ratio: 1 - }; - - scope.style = function () { - return { - 'height': parseInt(scope.height, 10) + 'px', - 'width': parseInt(scope.width, 10) + 'px' - }; - }; - - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); - var $container = element.find(".crop-container"); - - var setImageSize = function(width, height){ - $image.width(width); - $image.height(height); - - $viewport.width(width); - $viewport.height(height); - - scope.dimensions.image.width = width; - scope.dimensions.image.height = height; - }; - - //when loading an image without any crop info, we center and fit it - var fitImage = function(){ - fitToViewPort($image); - centerImage($image); - }; - - //utill for centering scaled image - var centerImage = function(img) { - img.css({ - 'position': 'absolute', - 'left': scope.dimensions.viewport.width / 2 - scope.dimensions.image.width / 2, - 'top': scope.dimensions.viewport.height / 2 - scope.dimensions.image.height / 2 - }); - }; - - //utill for scaling image to fit viewport - var fitToViewPort = function(img) { - //returns size fitting the cropper - var size = calculateAspectRatioFit( - scope.dimensions.image.width, - scope.dimensions.image.height, - scope.dimensions.viewport.width, - scope.dimensions.viewport.height, - true); - - //sets the image size and updates the scope - setImageSize(size.width, size.height); - - scope.minScale = size.ratio; - scope.maxScale = size.ratio * 3; - scope.currentScale = scope.minScale; - scope.scale = scope.currentScale; - }; - - var resizeImageToScale = function(img, ratio){ - //do stuff - var size = calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); - - setImageSize(size.width, size.height); - centerImage(img); - scope.currentScale = scope.scale; - }; - - //utill for getting either min/max aspect ratio to scale image after - var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) { - var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ]; - - if(maximize){ - ratio = Math.max(ratio[0], ratio[1]); - }else{ - ratio = Math.min(ratio[0], ratio[1]); - } - - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - //utill for scaling width / height given a ratio - var calculateSizeToRatio= function(srcWidth, srcHeight, ratio) { - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - var calculateGravity = function(){ - scope.gravity.left = $overlay[0].offsetLeft + 10; - scope.gravity.top = $overlay[0].offsetTop + 10; - }; - - - //Drag and drop positioning, using jquery ui draggable - var onStartDragPosition, top, left; - $overlay.draggable({ - stop: function() { - calculateGravity(); - } - }); - - //// INIT ///// - $image.load(function(){ - $timeout(function(){ - $image.width("auto"); - $image.height("auto"); - - scope.dimensions.image.originalWidth = $image.width(); - scope.dimensions.image.originalHeight = $image.height(); - - setDimensions(); - - fitImage(); - scope.loaded = true; - }); - }); - - - } - }; - }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 6291c1066c..6ffde599a6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -184,60 +184,6 @@ ul.color-picker li a { } -// -// Cropper -// ------------------------------------------------- - -.umb-cropper .crop-container .viewport img { - position: absolute; - top: 0; - left: 0; -} - - -.umb-cropper .overlay { - top: 0; - left: 0; - cursor: move; - z-index: 6001; -} - -.umb-cropper .viewport { - overflow: hidden; - position: relative; - width: 600px; - height: 400px; - border:1px solid #ccc; - background-image: url('images/viewport_background.gif'); -} - -.umb-cropper .viewport:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 5999; - -moz-opacity: .75; - opacity: .75; - filter: alpha(opacity=7); - -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); -} - -.umb-cropper-gravity .pointer{ - width: 20px; - height: 20px; - - top: 0; - left: 0; - cursor: move; - z-index: 6001; -} - - // // folder-browser // -------------------------------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-crop.html b/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-crop.html deleted file mode 100644 index 82f83d56d6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-crop.html +++ /dev/null @@ -1,32 +0,0 @@ -
    -
    -
    - -
    -
    -
    - - - -
    -		{{dimensions | json}}
    -
    - -
    -		{{box | json}}
    -
    - - -
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-gravity.html b/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-gravity.html deleted file mode 100644 index 97acd321d2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/imaging/umb-image-gravity.html +++ /dev/null @@ -1,17 +0,0 @@ -
    -
    -
    - - -
    - -
    - -
    -
    - -
    -		{{gravity | json}}
    -
    - -
    \ No newline at end of file From c527833081763e85221337380bbae8d6e548f519 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 10:59:05 +1100 Subject: [PATCH 045/100] Revert "adds editing function to media picker controller" This reverts commit 7d051dffbb1b891979cb4e1d698e5f536661d691. This will only be contained in 7.1 --- .../propertyeditors/mediapicker/mediapicker.controller.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index af5779b9a7..23a0a4aefc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -32,14 +32,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.sync(); }; - $scope.edit = function(image){ - $scope.currentImage = image; - $scope.currentImage.crop ={}; - if(!$scope.currentImage.crop){ - //$scope.currentImage.crop = { "left": 0.31731772342645215, "top": 0.17420325244997603, "right": 0.36246473116627076, "bottom": 0.30226197981593617 }; - } - } - $scope.add = function() { dialogService.mediaPicker({ multiPicker: multiPicker, From 3ef9635406266a9676f3fa01b82c5bd5a8edf64d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 13:17:38 +1100 Subject: [PATCH 046/100] Fixes: U4-4010 Member and user session gets mixed - fixes how we detect a back office request + unit tests and added new IsBackOffice attribute --- src/Umbraco.Core/UriExtensions.cs | 91 +++++++++++++++++-- src/Umbraco.Tests/UriExtensionsTests.cs | 41 ++++++--- src/Umbraco.Web/Mvc/PluginController.cs | 10 +- .../Mvc/PluginControllerMetadata.cs | 6 ++ .../ServerRegistrationEventHandler.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoModule.cs | 2 +- .../WebApi/IsBackOfficeAttribute.cs | 13 +++ .../WebApi/UmbracoAuthorizedApiController.cs | 1 + src/Umbraco.Web/WebBootManager.cs | 8 +- 10 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index da9498c96d..058c53892c 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -11,24 +11,97 @@ namespace Umbraco.Core ///
    public static class UriExtensions { - /// /// Checks if the current uri is a back office request /// /// + /// + /// The current application path or VirtualPath + /// /// - internal static bool IsBackOfficeRequest(this Uri url) + /// + /// There are some special routes we need to check to properly determine this: + /// + /// If any route has an extension in the path like .aspx = back office + /// + /// These are def back office: + /// /Umbraco/RestServices = back office + /// /Umbraco/BackOffice = back office + /// /Umbraco/UmbracpApi = back office + /// /Umbraco/UmbracoTrees = back office + /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end + /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice + /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. + /// + /// These are def front-end: + /// /Umbraco/Surface = front-end + /// /Umbraco/Api = front-end + /// But if we've got this far we'll just have to assume it's front-end anyways. + /// + /// + internal static bool IsBackOfficeRequest(this Uri url, string applicationPath) { - - var authority = url.GetLeftPart(UriPartial.Authority); - var afterAuthority = url.GetLeftPart(UriPartial.Query) - .TrimStart(authority) - .TrimStart("/"); + applicationPath = applicationPath ?? string.Empty; + var fullUrlPath = url.AbsolutePath.TrimStart(new[] {'/'}); + var appPath = applicationPath.TrimStart(new[] {'/'}); + var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/'); - //check if this is in the umbraco back office - return afterAuthority.InvariantStartsWith(GlobalSettings.Path.TrimStart("/")); + var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/')); + //if not, then def not back office + if (isUmbracoPath == false) return false; + + //if its the normal /umbraco path + if (urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea) + || urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea + "/")) + { + return true; + } + + //check for a file extension + var extension = Path.GetExtension(url.LocalPath); + //has an extension, def back office + if (extension.IsNullOrWhiteSpace() == false) return true; + //check for special case asp.net calls like: + // /umbraco/webservices/legacyAjaxCalls.asmx/js which will return a null file extension but are still considered extension'd requests + if (urlPath.InvariantContains(".asmx/") + || urlPath.InvariantContains(".aspx/") + || urlPath.InvariantContains(".ashx/") + || urlPath.InvariantContains(".axd/") + || urlPath.InvariantContains(".svc/")) + { + return true; + } + + //check for special back office paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoApi/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoTrees/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/")) + { + return true; + } + + //check for special front-end paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Surface/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Api/")) + { + return false; + } + + //if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by + // checking how many parts the route has, for example, all PluginController routes will be routed like + // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} + // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a + // plugin controller for the front-end. + if (urlPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Length >= 3) + { + return false; + } + + //if its anything else we can assume it's back office + return true; } /// diff --git a/src/Umbraco.Tests/UriExtensionsTests.cs b/src/Umbraco.Tests/UriExtensionsTests.cs index 61ed49d65d..9a6cfcae17 100644 --- a/src/Umbraco.Tests/UriExtensionsTests.cs +++ b/src/Umbraco.Tests/UriExtensionsTests.cs @@ -10,20 +10,35 @@ namespace Umbraco.Tests [TestFixture] public class UriExtensionsTests { - [TestCase("http://www.domain.com/umbraco", true)] - [TestCase("http://www.domain.com/Umbraco/", true)] - [TestCase("http://www.domain.com/umbraco/default.aspx", true)] - [TestCase("http://www.domain.com/umbraco/test/test", true)] - [TestCase("http://www.domain.com/Umbraco/test/test.aspx", true)] - [TestCase("http://www.domain.com/umbraco/test/test.js", true)] - [TestCase("http://www.domain.com/umbrac", false)] - [TestCase("http://www.domain.com/test", false)] - [TestCase("http://www.domain.com/test/umbraco", false)] - [TestCase("http://www.domain.com/test/umbraco.aspx", false)] - public void Is_Back_Office_Request(string input, bool expected) + [TestCase("http://www.domain.com/umbraco", "", true)] + [TestCase("http://www.domain.com/Umbraco/", "", true)] + [TestCase("http://www.domain.com/umbraco/default.aspx", "", true)] + [TestCase("http://www.domain.com/umbraco/test/test", "", false)] + [TestCase("http://www.domain.com/umbraco/test/test/test", "", false)] + [TestCase("http://www.domain.com/Umbraco/test/test.aspx", "", true)] + [TestCase("http://www.domain.com/umbraco/test/test.js", "", true)] + [TestCase("http://www.domain.com/umbrac", "", false)] + [TestCase("http://www.domain.com/test", "", false)] + [TestCase("http://www.domain.com/test/umbraco", "", false)] + [TestCase("http://www.domain.com/test/umbraco.aspx", "", false)] + [TestCase("http://www.domain.com/Umbraco/restServices/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/umbracoapi/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/umbracotrees/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/anything", "", true)] + [TestCase("http://www.domain.com/Umbraco/anything/", "", true)] + [TestCase("http://www.domain.com/Umbraco/surface/blah", "", false)] + [TestCase("http://www.domain.com/umbraco/api/blah", "", false)] + [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)] + [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)] + [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)] + [TestCase("http://www.domain.com/MyVdir/Umbraco/restServices/blah", "/myvdir", true)] + [TestCase("http://www.domain.com/umbraco/webservices/legacyAjaxCalls.asmx/js", "", true)] + [TestCase("http://www.domain.com/umbraco/test/legacyAjaxCalls.ashx?some=query&blah=js", "", true)] + public void Is_Back_Office_Request(string input, string virtualPath, bool expected) { - var source = new Uri(input); - Assert.AreEqual(expected, source.IsBackOfficeRequest()); + var source = new Uri(input); + Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath)); } [TestCase("http://www.domain.com/install", true)] diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs index 6c14edf3d9..1814da09b2 100644 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ b/src/Umbraco.Web/Mvc/PluginController.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Services; +using Umbraco.Web.WebApi; namespace Umbraco.Web.Mvc { @@ -86,14 +87,17 @@ namespace Umbraco.Web.Mvc return MetadataStorage.GetOrAdd(type, type1 => { - var attribute = type.GetCustomAttribute(false); + var pluginAttribute = type.GetCustomAttribute(false); + //check if any inherited class of this type contains the IsBackOffice attribute + var backOfficeAttribute = type.GetCustomAttribute(true); var meta = new PluginControllerMetadata() { - AreaName = attribute == null ? null : attribute.AreaName, + AreaName = pluginAttribute == null ? null : pluginAttribute.AreaName, ControllerName = ControllerExtensions.GetControllerName(type), ControllerNamespace = type.Namespace, - ControllerType = type + ControllerType = type, + IsBackOffice = backOfficeAttribute != null }; MetadataStorage.TryAdd(type, meta); diff --git a/src/Umbraco.Web/Mvc/PluginControllerMetadata.cs b/src/Umbraco.Web/Mvc/PluginControllerMetadata.cs index dfe01ef3a3..6de8525de9 100644 --- a/src/Umbraco.Web/Mvc/PluginControllerMetadata.cs +++ b/src/Umbraco.Web/Mvc/PluginControllerMetadata.cs @@ -11,5 +11,11 @@ namespace Umbraco.Web.Mvc internal string ControllerName { get; set; } internal string ControllerNamespace { get; set; } internal string AreaName { get; set; } + + /// + /// This is determined by another attribute [IsBackOffice] which slightly modifies the route path + /// allowing us to determine if it is indeed a back office request or not + /// + internal bool IsBackOffice { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs index 79edd8192e..ab478c6c2b 100644 --- a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs +++ b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web.Strategies } - static void UmbracoModuleRouteAttempt(object sender, Routing.RoutableAttemptEventArgs e) + static void UmbracoModuleRouteAttempt(object sender, RoutableAttemptEventArgs e) { if (e.HttpContext.Request == null || e.HttpContext.Request.Url == null) return; @@ -78,7 +78,7 @@ namespace Umbraco.Web.Strategies if (e.Outcome == EnsureRoutableOutcome.NotDocumentRequest) { //check if this is in the umbraco back office - if (e.HttpContext.Request.Url.IsBackOfficeRequest()) + if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)) { //yup it's a back office request! using (var lck = new UpgradeableReadLock(Locker)) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 076e7d6fbe..83d7ba6288 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -457,6 +457,7 @@ + diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 5ba568c56e..f058b85461 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -139,7 +139,7 @@ namespace Umbraco.Web if (http.Request.Url.IsClientSideRequest()) return; - if (app.Request.Url.IsBackOfficeRequest() || app.Request.Url.IsInstallerRequest()) + if (app.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath) || app.Request.Url.IsInstallerRequest()) { var ticket = http.GetUmbracoAuthTicket(); if (ticket != null) diff --git a/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs b/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs new file mode 100644 index 0000000000..219be51d48 --- /dev/null +++ b/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Umbraco.Web.WebApi +{ + /// + /// When applied to an api controller it will be routed to the /Umbraco/BackOffice prefix route so we can determine if it + /// is a back office route or not. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public sealed class IsBackOfficeAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs index d0ede6fcda..782b206bbd 100644 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs @@ -7,6 +7,7 @@ using umbraco.BusinessLogic; namespace Umbraco.Web.WebApi { + [IsBackOffice] [UmbracoAuthorize] public abstract class UmbracoAuthorizedApiController : UmbracoApiController { diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 1fd2371eb7..536eb23b84 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -231,9 +231,15 @@ namespace Umbraco.Web private void RouteLocalApiController(Type controller, string umbracoPath) { var meta = PluginController.GetMetadata(controller); + + //url to match + var routePath = meta.IsBackOffice == false + ? umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}" + : umbracoPath + "/BackOffice/Api/" + meta.ControllerName + "/{action}/{id}"; + var route = RouteTable.Routes.MapHttpRoute( string.Format("umbraco-{0}-{1}", "api", meta.ControllerName), - umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}", //url to match + routePath, new {controller = meta.ControllerName, id = UrlParameter.Optional}); //web api routes don't set the data tokens object if (route.DataTokens == null) From 5c614a88a2efc6101d66d2e7785f48c96c160ea5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 13:29:17 +1100 Subject: [PATCH 047/100] ensures webapi plugincontrollers are routed to /backoffice if [IsBackOffice] is specified --- src/Umbraco.Core/UriExtensions.cs | 6 +----- src/Umbraco.Tests/UriExtensionsTests.cs | 2 -- src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs | 15 ++++++++++++--- src/Umbraco.Web/Mvc/PluginControllerArea.cs | 4 +++- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 058c53892c..45cb906eef 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -27,8 +27,6 @@ namespace Umbraco.Core /// These are def back office: /// /Umbraco/RestServices = back office /// /Umbraco/BackOffice = back office - /// /Umbraco/UmbracpApi = back office - /// /Umbraco/UmbracoTrees = back office /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. @@ -75,9 +73,7 @@ namespace Umbraco.Core } //check for special back office paths - if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoApi/") - || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoTrees/") - || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/")) { return true; diff --git a/src/Umbraco.Tests/UriExtensionsTests.cs b/src/Umbraco.Tests/UriExtensionsTests.cs index 9a6cfcae17..8e826fc428 100644 --- a/src/Umbraco.Tests/UriExtensionsTests.cs +++ b/src/Umbraco.Tests/UriExtensionsTests.cs @@ -23,8 +23,6 @@ namespace Umbraco.Tests [TestCase("http://www.domain.com/test/umbraco.aspx", "", false)] [TestCase("http://www.domain.com/Umbraco/restServices/blah", "", true)] [TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)] - [TestCase("http://www.domain.com/Umbraco/umbracoapi/blah", "", true)] - [TestCase("http://www.domain.com/Umbraco/umbracotrees/blah", "", true)] [TestCase("http://www.domain.com/Umbraco/anything", "", true)] [TestCase("http://www.domain.com/Umbraco/anything/", "", true)] [TestCase("http://www.domain.com/Umbraco/surface/blah", "", false)] diff --git a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs index 833fe92906..2836c458fb 100644 --- a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs +++ b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs @@ -27,13 +27,20 @@ namespace Umbraco.Web.Mvc /// The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice' /// By default this value is just {action}/{id} but can be modified for things like web api routes /// Default is true for MVC, otherwise false for WebAPI + /// + /// If specified will add this string to the path between the umbraco path and the area path name, for example: + /// /umbraco/CUSTOMPATHPREFIX/areaname + /// if not specified, will just route like: + /// /umbraco/areaname + /// /// /// internal static Route RouteControllerPlugin(this AreaRegistration area, string controllerName, Type controllerType, RouteCollection routes, string controllerSuffixName, string defaultAction, object defaultId, string umbracoTokenValue = "backoffice", string routeTokens = "{action}/{id}", - bool isMvc = true) + bool isMvc = true, + string areaPathPrefix = "") { Mandate.ParameterNotNullOrEmpty(controllerName, "controllerName"); Mandate.ParameterNotNull(controllerSuffixName, "controllerSuffixName"); @@ -44,8 +51,10 @@ namespace Umbraco.Web.Mvc var umbracoArea = GlobalSettings.UmbracoMvcArea; - //routes are explicitly name with controller names and IDs - var url = umbracoArea + "/" + area.AreaName + "/" + controllerName + "/" + routeTokens; + //routes are explicitly named with controller names and IDs + var url = umbracoArea + "/" + + (areaPathPrefix.IsNullOrWhiteSpace() ? "" : areaPathPrefix + "/") + + area.AreaName + "/" + controllerName + "/" + routeTokens; Route controllerPluginRoute; //var meta = PluginController.GetMetadata(controllerType); diff --git a/src/Umbraco.Web/Mvc/PluginControllerArea.cs b/src/Umbraco.Web/Mvc/PluginControllerArea.cs index 2d4c1a56ce..e197804031 100644 --- a/src/Umbraco.Web/Mvc/PluginControllerArea.cs +++ b/src/Umbraco.Web/Mvc/PluginControllerArea.cs @@ -85,7 +85,9 @@ namespace Umbraco.Web.Mvc { foreach (var s in apiControllers) { - this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "", "", UrlParameter.Optional, "api", isMvc: false); + this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "", "", UrlParameter.Optional, "api", + isMvc: false, + areaPathPrefix: s.IsBackOffice ? "backoffice" : null); } } } From 6d57afc71a4dd4c9922f5652174a66c5c2dc8552 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 13:49:37 +1100 Subject: [PATCH 048/100] ensures auth controller has IsBackOffice specified --- src/Umbraco.Web/Editors/AuthenticationController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index f37b61eae8..5e1672fa90 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -30,6 +30,7 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] [ValidationFilter] [AngularJsonOnlyConfiguration] + [IsBackOffice] public class AuthenticationController : UmbracoApiController { From bb837232bad2eb5ce196538a2e59ba0e56fb37f1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 14:45:33 +1100 Subject: [PATCH 049/100] Allows DefaultPropertyValueConverters to shadow other default converters, this allows us to have a hierarchy of default converters. This also fixes part of this issue: U4-4030 RelatedLinksEditorValueConvertor can't be overruled and internal links are wrong --- .../PublishedContent/PublishedPropertyType.cs | 43 +++++++++++++++---- .../DefaultPropertyValueConverterAttribute.cs | 24 +++++++++++ .../PropertyValueConvertersResolver.cs | 38 ++++++++++++++++ .../RelatedLinksEditorValueConvertor.cs | 1 + 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 295cbdef66..c8bf96be8f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -80,11 +80,8 @@ namespace Umbraco.Core.Models.PublishedContent private void InitializeConverters() { - var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); - - var defaultConverters = converters - .Where(x => x.GetType().GetCustomAttribute(false) != null) - .ToArray(); + var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); + var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters; _converter = null; @@ -98,8 +95,14 @@ namespace Umbraco.Core.Models.PublishedContent else if (foundConverters.Length > 1) { //more than one was found, we need to first figure out if one of these is an Umbraco default value type converter - var nonDefault = foundConverters.Except(defaultConverters).ToArray(); - if (nonDefault.Length > 1) + //get the non-default and see if we have one + var nonDefault = foundConverters.Except(defaultConvertersWithAttributes.Select(x => x.Item1)).ToArray(); + if (nonDefault.Length == 1) + { + //there's only 1 custom converter registered that so use it + _converter = nonDefault[0]; + } + else if (nonDefault.Length > 1) { //this is not allowed, there cannot be more than 1 custom converter throw new InvalidOperationException( @@ -109,9 +112,31 @@ namespace Umbraco.Core.Models.PublishedContent ContentType.Alias, PropertyTypeAlias, nonDefault[1].GetType().FullName, nonDefault[0].GetType().FullName)); } + else + { + //we need to remove any converters that have been shadowed by another converter + var foundDefaultConvertersWithAttributes = defaultConvertersWithAttributes.Where(x => foundConverters.Contains(x.Item1)); + var shadowedTypes = foundDefaultConvertersWithAttributes.SelectMany(x => x.Item2.DefaultConvertersToShadow); + var shadowedDefaultConverters = foundConverters.Where(x => shadowedTypes.Contains(x.GetType())); + var nonShadowedDefaultConverters = foundConverters.Except(shadowedDefaultConverters).ToArray(); - //there's only 1 custom converter registered that so use it - _converter = nonDefault[0]; + if (nonShadowedDefaultConverters.Length == 1) + { + //assign to the single default converter + _converter = nonShadowedDefaultConverters[0]; + } + else if (nonShadowedDefaultConverters.Length > 1) + { + //this is not allowed, there cannot be more than 1 custom converter + throw new InvalidOperationException( + string.Format("Type '{2}' cannot be an IPropertyValueConverter" + + " for property '{1}' of content type '{0}' because type '{3}' has already been detected as a converter" + + " for that property, and only one converter can exist for a property.", + ContentType.Alias, PropertyTypeAlias, + nonShadowedDefaultConverters[1].GetType().FullName, nonShadowedDefaultConverters[0].GetType().FullName)); + } + } + } // get the cache levels, quietely fixing the inconsistencies (no need to throw, really) diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs index 4857e2bb07..10bdd053a8 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.PropertyEditors { @@ -8,5 +10,27 @@ namespace Umbraco.Core.PropertyEditors [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] internal class DefaultPropertyValueConverterAttribute : Attribute { + public DefaultPropertyValueConverterAttribute() + { + DefaultConvertersToShadow = Enumerable.Empty(); + } + + public DefaultPropertyValueConverterAttribute(params Type[] convertersToShadow) + { + DefaultConvertersToShadow = convertersToShadow; + } + + /// + /// A DefaultPropertyValueConverter can 'shadow' other default property value converters so that + /// a DefaultPropertyValueConverter can be more specific than another one. + /// + /// + /// An example where this is useful is that both the RelatedLiksEditorValueConverter and the JsonValueConverter + /// will be returned as value converters for the Related Links Property editor, however the JsonValueConverter + /// is a very generic converter and the RelatedLiksEditorValueConverter is more specific than it, so the RelatedLiksEditorValueConverter + /// can specify that it 'shadows' the JsonValueConverter. + /// + public IEnumerable DefaultConvertersToShadow { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs index 53fecdac9d..295fdb4b09 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Threading; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.PropertyEditors @@ -36,5 +38,41 @@ namespace Umbraco.Core.PropertyEditors { get { return Values; } } + + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private Tuple[] _defaults = null; + + /// + /// Caches and gets the default converters with their metadata + /// + internal Tuple[] DefaultConverters + { + get + { + using (var locker = new UpgradeableReadLock(_lock)) + { + if (_defaults == null) + { + locker.UpgradeToWriteLock(); + + var defaultConvertersWithAttributes = Converters + .Select(x => new + { + attribute = x.GetType().GetCustomAttribute(false), + converter = x + }) + .Where(x => x.attribute != null) + .ToArray(); + + _defaults = defaultConvertersWithAttributes + .Select( + x => new Tuple(x.converter, x.attribute)) + .ToArray(); + } + + return _defaults; + } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs index 469175f75f..e2431255d0 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs @@ -16,6 +16,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { [PropertyValueType(typeof(JToken))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + [DefaultPropertyValueConverter(typeof(JsonValueConverter))] //this shadows the JsonValueConverter public class RelatedLinksEditorValueConvertor : PropertyValueConverterBase { public override bool IsConverter(PublishedPropertyType propertyType) From 9f078357a791436e55e214dc96bb2f8aa65f5232 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 15:17:03 +1100 Subject: [PATCH 050/100] Fixes: U4-4030 RelatedLinksEditorValueConvertor can't be overruled and internal links are wrong --- .../RelatedLinksEditorValueConvertor.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs index e2431255d0..9d3b50f2d9 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs @@ -14,7 +14,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters; namespace Umbraco.Web.PropertyEditors.ValueConverters { - [PropertyValueType(typeof(JToken))] + [PropertyValueType(typeof(JArray))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] [DefaultPropertyValueConverter(typeof(JsonValueConverter))] //this shadows the JsonValueConverter public class RelatedLinksEditorValueConvertor : PropertyValueConverterBase @@ -33,7 +33,25 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { try { - var obj = JsonConvert.DeserializeObject(sourceString); + var obj = JsonConvert.DeserializeObject(sourceString); + //update the internal links if we have a context + if (UmbracoContext.Current != null) + { + var helper = new UmbracoHelper(UmbracoContext.Current); + foreach (var a in obj) + { + var type = a.Value("type"); + if (type.IsNullOrWhiteSpace() == false) + { + if (type == "internal") + { + var linkId = a.Value("link"); + var link = helper.NiceUrl(linkId); + a["link"] = link; + } + } + } + } return obj; } catch (Exception ex) From cf3de52249214babb3df991e5e977c8f3e73d22a Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 15:36:28 +1100 Subject: [PATCH 051/100] removes the old language.ascx webforms files --- .../umbraco/create/language.ascx | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 13 +-- .../umbraco/create/language.ascx | 17 ---- .../umbraco/create/language.ascx.cs | 81 ++++++++++--------- .../umbraco/create/language.ascx.designer.cs | 51 ------------ 5 files changed, 46 insertions(+), 118 deletions(-) delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs diff --git a/src/Umbraco.Web.UI/umbraco/create/language.ascx b/src/Umbraco.Web.UI/umbraco/create/language.ascx index feffae0a34..1814d741f6 100644 --- a/src/Umbraco.Web.UI/umbraco/create/language.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/language.ascx @@ -1,4 +1,4 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> +<%@ Control Language="c#" AutoEventWireup="True" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index d4d6931953..0a0573a4f5 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -424,6 +424,9 @@ ASPXCodeBehind + + ASPXCodeBehind + ASPXCodeBehind @@ -1205,13 +1208,6 @@ Code - - language.ascx - ASPXCodeBehind - - - language.ascx - media.ascx ASPXCodeBehind @@ -1926,9 +1922,6 @@ ASPXCodeBehind - - ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx deleted file mode 100644 index c0d359b612..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx +++ /dev/null @@ -1,17 +0,0 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - * - - - - - <%=umbraco.ui.Text("cancel")%> - - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs index 017b7fe9b4..5799f932e1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Globalization; using System.Web; @@ -16,9 +16,6 @@ namespace umbraco.cms.presentation.create.controls /// public partial class language : UserControl { - private Language[] m_langs = Language.getAll; - - protected void Page_Load(object sender, EventArgs e) { // get all existing languages @@ -31,7 +28,7 @@ namespace umbraco.cms.presentation.create.controls Cultures.Items.Add(new ListItem(ui.Text("choose") + "...", "")); foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) { - sortedCultures.Add(ci.DisplayName + "|||" + Guid.NewGuid().ToString(), ci.Name); + sortedCultures.Add(ci.DisplayName + "|||" + Guid.NewGuid().ToString(), ci.Name); } IDictionaryEnumerator ide = sortedCultures.GetEnumerator(); @@ -42,37 +39,6 @@ namespace umbraco.cms.presentation.create.controls } } - private bool languageExists(string culture) - { - foreach (Language l in m_langs) - { - if (l.CultureAlias == culture) - return true; - } - return false; - } - - #region Web Form Designer generated code - - protected override void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - protected void sbmt_Click(object sender, EventArgs e) { LegacyDialogHandler.Create( @@ -82,10 +48,47 @@ namespace umbraco.cms.presentation.create.controls -1, Cultures.SelectedValue); - BasePage.Current.ClientTools - .ChildNodeCreated() - .CloseModalWindow(); + BasePage.Current.ClientTools + .ChildNodeCreated() + .CloseModalWindow(); } + + /// + /// pp1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp1; + + /// + /// Cultures control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList Cultures; + + /// + /// RequiredFieldValidator1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; + + /// + /// sbmt control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button sbmt; + } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs deleted file mode 100644 index d3945627fd..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/language.ascx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation.create.controls { - - - public partial class language { - - /// - /// pp1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp1; - - /// - /// Cultures control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList Cultures; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// sbmt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button sbmt; - } -} From 0bc39afdabe858973132b0a829855881cf0e01b6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 15:39:35 +1100 Subject: [PATCH 052/100] fixes language drop down styles - was too big to fit --- src/Umbraco.Web.UI/umbraco/create/language.ascx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/create/language.ascx b/src/Umbraco.Web.UI/umbraco/create/language.ascx index 1814d741f6..686cbbdb96 100644 --- a/src/Umbraco.Web.UI/umbraco/create/language.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/language.ascx @@ -5,7 +5,7 @@ - + * From 0e6c199a7e7d4776ecb82d974c23d44f4dc67439 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 17:18:23 +1100 Subject: [PATCH 053/100] working on U4-4011 Package installation will require either a full app refresh or we will need to re-lazy load in all of the assets but unfortunately we cannot lazy load in things like directives as angular doesn't like that, we have to re-load the browser. --- .../lib/umbraco/LegacyUmbClientMgr.js | 6 + .../directives/umbsections.directive.js | 5 + .../src/common/services/assets.service.js | 19 +- .../src/common/services/events.service.js | 2 +- .../src/common/services/util.service.js | 16 + .../umbraco/developer/Packages/installer.aspx | 8 + .../Editors/BackOfficeController.cs | 22 + src/Umbraco.Web/Mvc/JsonNetResult.cs | 56 ++ .../UI/JavaScript/CssInitialization.cs | 9 +- .../UI/JavaScript/JsInitialization.cs | 13 +- src/Umbraco.Web/Umbraco.Web.csproj | 12 +- .../umbraco/developer/Packages/installer.aspx | 261 --------- .../developer/Packages/installer.aspx.cs | 538 ++++++++++++++++-- .../Packages/installer.aspx.designer.cs | 433 -------------- 14 files changed, 632 insertions(+), 768 deletions(-) create mode 100644 src/Umbraco.Web/Mvc/JsonNetResult.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js index 912c624ce4..5db8fc074e 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js @@ -364,6 +364,12 @@ Umbraco.Sys.registerNamespace("Umbraco.Application"); getRootScope().$emit("app.closeDialogs", undefined); } }, + /* This is used for the package installer to call in order to reload all app assets so we don't have to reload the window */ + _packageInstalled: function() { + var injector = getRootInjector(); + var packageHelper = injector.get("packageHelper"); + packageHelper.packageInstalled(); + }, _debug: function(strMsg) { if (this._isDebug) { Sys.Debug.trace("UmbClientMgr: " + strMsg); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js index 349bf0a496..b6b59991b5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js @@ -65,6 +65,11 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se scope.currentSection = args.value; } }); + + eventsService.on("app.reInitialize", function (e, args) { + //re-load the sections if we're re-initializing (i.e. package installed) + loadSections(); + }); //on page resize window.onresize = calculateHeight; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index ef534fd7b1..dbf1d3fc14 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -41,7 +41,7 @@ * */ angular.module('umbraco.services') -.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope) { +.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) { var initAssetsLoaded = false; @@ -71,6 +71,23 @@ angular.module('umbraco.services') return deferred.promise; }, + /** Internal method. This is used after installing a package to reload the application assets so we don't have to reload the whole window */ + _reloadApplicationAssets: function() { + + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("manifestAssetList", "", "")), + 'Failed to get manifest list').then(function(data) { + + //ok so we have the list of assets, now we'll use yepnope to go get them. Anything that is already loaded should remain loaded + // and this should just load anything that is newly installed. + + yepnope({ + load: data + }); + + }); + }, + /** * @ngdoc method * @name umbraco.services.assetsService#loadCss diff --git a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js index 560b743a29..d050b62961 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js @@ -65,7 +65,7 @@ function eventsService($q, $rootScope) { return $rootScope.$on(name, callback); }, - /** pass in the result of subscribe to this method, or just call the method returned from subscribe to unsubscribe */ + /** pass in the result of 'on' to this method, or just call the method returned from 'on' to unsubscribe */ unsubscribe: function(handle) { if (angular.isFunction(handle)) { handle(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 5abc1116d6..8f47a2270d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -1,5 +1,21 @@ /*Contains multiple services for various helper tasks */ +function packageHelper(assetsService, treeService, eventsService) { + + return { + + /** Called when a package is installed, this resets a bunch of data and ensures the new package assets are loaded in */ + packageInstalled: function () { + assetsService._reloadApplicationAssets(); + treeService.clearCache(); + //send event + eventsService.emit("app.reInitialize"); + } + + }; +} +angular.module('umbraco.services').factory('packageHelper', packageHelper); + function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, umbRequestHelper) { return { /** sets the image's url - will check if it is a folder or a real image */ diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx index 481a030bcc..6a94b46980 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx @@ -260,6 +260,14 @@ + + <%--This is a hack to fix this currently until we can replace the installer with a native angular editor + http://issues.umbraco.org/issue/U4-4011 + --%> + +

    All items in the package has been installed

    diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 899229b9cf..8c3d3a6039 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Web.Mvc; using System.Web.UI; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -68,6 +69,26 @@ namespace Umbraco.Web.Editors return JavaScript(result); } + ///

    + /// Returns a js array of all of the manifest assets + /// + /// + [UmbracoAuthorize(Order = 0)] + [HttpGet] + public JsonNetResult GetManifestAssetList() + { + var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins")); + var parser = new ManifestParser(plugins); + var initJs = new JsInitialization(parser); + var initCss = new CssInitialization(parser); + var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray()); + var cssResult = initCss.GetStylesheetInitializationArray(HttpContext); + + ManifestParser.MergeJArrays(jsResult, cssResult); + + return new JsonNetResult {Data = jsResult, Formatting = Formatting.Indented}; + } + /// /// Returns the JavaScript object representing the static server variables javascript object /// @@ -86,6 +107,7 @@ namespace Umbraco.Web.Editors "umbracoUrls", new Dictionary { {"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")}, + {"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")}, //API URLs { "contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web/Mvc/JsonNetResult.cs b/src/Umbraco.Web/Mvc/JsonNetResult.cs new file mode 100644 index 0000000000..da6780451e --- /dev/null +++ b/src/Umbraco.Web/Mvc/JsonNetResult.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using Newtonsoft.Json; + +namespace Umbraco.Web.Mvc +{ + + /// + /// Custom json result using newtonsoft json.net + /// + public class JsonNetResult : ActionResult + { + public Encoding ContentEncoding { get; set; } + public string ContentType { get; set; } + public object Data { get; set; } + + public JsonSerializerSettings SerializerSettings { get; set; } + public Formatting Formatting { get; set; } + + public JsonNetResult() + { + SerializerSettings = new JsonSerializerSettings(); + } + + public override void ExecuteResult(ControllerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + HttpResponseBase response = context.HttpContext.Response; + + response.ContentType = string.IsNullOrEmpty(ContentType) == false + ? ContentType + : "application/json"; + + if (ContentEncoding != null) + response.ContentEncoding = ContentEncoding; + + if (Data != null) + { + var writer = new JsonTextWriter(response.Output) { Formatting = Formatting }; + + var serializer = JsonSerializer.Create(SerializerSettings); + serializer.Serialize(writer, Data); + + writer.Flush(); + } + } + } + +} diff --git a/src/Umbraco.Web/UI/JavaScript/CssInitialization.cs b/src/Umbraco.Web/UI/JavaScript/CssInitialization.cs index a483d7f776..c2b200604e 100644 --- a/src/Umbraco.Web/UI/JavaScript/CssInitialization.cs +++ b/src/Umbraco.Web/UI/JavaScript/CssInitialization.cs @@ -24,6 +24,13 @@ namespace Umbraco.Web.UI.JavaScript /// Processes all found manifest files and outputs yepnope.injectcss calls for all css files found in all manifests ///
    public string GetStylesheetInitialization(HttpContextBase httpContext) + { + var result = GetStylesheetInitializationArray(httpContext); + + return ParseMain(result); + } + + public JArray GetStylesheetInitializationArray(HttpContextBase httpContext) { var merged = new JArray(); foreach (var m in _parser.GetManifests()) @@ -37,7 +44,7 @@ namespace Umbraco.Web.UI.JavaScript //now we need to merge in any found cdf declarations on property editors ManifestParser.MergeJArrays(merged, ScanPropertyEditors(ClientDependencyType.Css, httpContext)); - return ParseMain(merged); + return merged; } diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs b/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs index e78a329db0..51c780cfc2 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialization.cs @@ -39,6 +39,15 @@ namespace Umbraco.Web.UI.JavaScript /// Processes all found manifest files and outputs the main.js file containing all plugin manifests ///
    public string GetJavascriptInitialization(HttpContextBase httpContext, JArray umbracoInit, JArray additionalJsFiles = null) + { + var result = GetJavascriptInitializationArray(httpContext, umbracoInit, additionalJsFiles); + + return ParseMain( + result.ToString(), + IOHelper.ResolveUrl(SystemDirectories.Umbraco)); + } + + public JArray GetJavascriptInitializationArray(HttpContextBase httpContext, JArray umbracoInit, JArray additionalJsFiles = null) { foreach (var m in _parser.GetManifests()) { @@ -57,9 +66,7 @@ namespace Umbraco.Web.UI.JavaScript //now we need to merge in any found cdf declarations on property editors ManifestParser.MergeJArrays(umbracoInit, ScanPropertyEditors(ClientDependencyType.Javascript, httpContext)); - return ParseMain( - umbracoInit.ToString(), - IOHelper.ResolveUrl(SystemDirectories.Umbraco)); + return umbracoInit; } /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0a0573a4f5..ce74fb02a8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -305,6 +305,7 @@ + @@ -433,6 +434,9 @@ ASPXCodeBehind + + ASPXCodeBehind + ASPXCodeBehind @@ -1363,13 +1367,6 @@ notifications.aspx - - installer.aspx - ASPXCodeBehind - - - installer.aspx - RegexWs.aspx ASPXCodeBehind @@ -1867,7 +1864,6 @@ ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx deleted file mode 100644 index e271bf7413..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx +++ /dev/null @@ -1,261 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" CodeBehind="installer.aspx.cs" - AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" - Trace="false" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - -
    -

    - Only install packages from sources you know and trust!

    -

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

    -

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

    -

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

    -

    - - -

    -
    -
    - -

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

    -
    - - - - -
    - - -
    -

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

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

    - Please note: Installing a package containing several items and - files can take some time. Do not refresh the page or navigate away before, the installer - notifies you the install is completed. -

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

    - Binary files in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Macro Conflicts in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Template Conflicts in the package!

    -

    - Read more... -

    - -
    -
    - -
    -

    - Stylesheet Conflicts in the package!

    -

    - Read more... -

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

    - All items in the package has been installed

    -

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

    -

    - Uninstall is available at the same location.

    -

    - - -

    - -
    -
    - -
    -
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index e18d46c625..a192176fda 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.ComponentModel; using System.Data; @@ -53,9 +53,9 @@ namespace umbraco.presentation.developer.packages } //if we are actually in the middle of installing something... meaning we keep redirecting back to this page with - // custom query strings - // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController - // http://issues.umbraco.org/issue/U4-1047 + // custom query strings + // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController + // http://issues.umbraco.org/issue/U4-1047 if (!string.IsNullOrEmpty(Request.GetItemAsString("installing"))) { HideAllPanes(); @@ -63,22 +63,22 @@ namespace umbraco.presentation.developer.packages ProcessInstall(Request.GetItemAsString("installing")); //process the current step } - else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet - && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) + else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet + && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) { //we'll fetch the local information we have about our repo, to find out what webservice to query. - _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); - + _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); + if (_repo != null && _repo.HasConnection()) { //from the webservice we'll fetch some info about the package. - cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); + cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); //if the package is protected we will ask for the users credentials. (this happens every time they try to fetch anything) if (!pack.Protected) { //if it isn't then go straigt to the accept licens screen - tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); + tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); UpdateSettings(); } @@ -212,25 +212,25 @@ namespace umbraco.presentation.developer.packages private void ProcessInstall(string currentStep) { - var dir = Request.GetItemAsString("dir"); + var dir = Request.GetItemAsString("dir"); var packageId = 0; - int.TryParse(Request.GetItemAsString("pId"), out packageId); + int.TryParse(Request.GetItemAsString("pId"), out packageId); switch (currentStep) { case "businesslogic": - //first load in the config from the temporary directory - //this will ensure that the installer have access to all the new files and the package manifest - _installer.LoadConfig(dir); + //first load in the config from the temporary directory + //this will ensure that the installer have access to all the new files and the package manifest + _installer.LoadConfig(dir); _installer.InstallBusinessLogic(packageId, dir); //making sure that publishing actions performed from the cms layer gets pushed to the presentation library.RefreshContent(); - + if (!string.IsNullOrEmpty(_installer.Control)) { - Response.Redirect("installer.aspx?installing=customInstaller&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=customInstaller&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); } else { @@ -238,50 +238,50 @@ namespace umbraco.presentation.developer.packages } break; case "customInstaller": - var customControl = Request.GetItemAsString("customControl"); + var customControl = Request.GetItemAsString("customControl"); - if (!customControl.IsNullOrWhiteSpace()) + if (!customControl.IsNullOrWhiteSpace()) { HideAllPanes(); - _configControl = new System.Web.UI.UserControl().LoadControl(SystemDirectories.Root + customControl); + _configControl = new System.Web.UI.UserControl().LoadControl(SystemDirectories.Root + customControl); _configControl.ID = "packagerConfigControl"; pane_optional.Controls.Add(_configControl); pane_optional.Visible = true; - if (!IsPostBack) - { - //We still need to clean everything up which is normally done in the Finished Action - PerformPostInstallCleanup(packageId, dir); - } - + if (!IsPostBack) + { + //We still need to clean everything up which is normally done in the Finished Action + PerformPostInstallCleanup(packageId, dir); + } + } else { //if the custom installer control is empty here (though it should never be because we've already checked for it previously) - //then we should run the normal FinishedAction - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + //then we should run the normal FinishedAction + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); } break; case "finished": - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); break; default: break; } } - /// - /// Perform the 'Finished' action of the installer - /// - /// - /// - /// - private void PerformFinishedAction(int packageId, string dir, string url) - { - HideAllPanes(); - //string url = _installer.Url; + /// + /// Perform the 'Finished' action of the installer + /// + /// + /// + /// + private void PerformFinishedAction(int packageId, string dir, string url) + { + HideAllPanes(); + //string url = _installer.Url; string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(); bt_viewInstalledPackage.OnClientClick = "document.location = '" + packageViewUrl + "'; return false;"; @@ -292,23 +292,23 @@ namespace umbraco.presentation.developer.packages pane_success.Visible = true; - PerformPostInstallCleanup(packageId, dir); - } + PerformPostInstallCleanup(packageId, dir); + } - /// - /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... - /// - /// - /// - private void PerformPostInstallCleanup(int packageId, string dir) - { - BasePage.Current.ClientTools.ReloadActionNode(true, true); - _installer.InstallCleanUp(packageId, dir); - //clear the tree cache - ClientTools.ClearClientTreeCache().RefreshTree("packager"); - TreeDefinitionCollection.Instance.ReRegisterTrees(); - BizLogicAction.ReRegisterActionsAndHandlers(); - } + /// + /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... + /// + /// + /// + private void PerformPostInstallCleanup(int packageId, string dir) + { + BasePage.Current.ClientTools.ReloadActionNode(true, true); + _installer.InstallCleanUp(packageId, dir); + //clear the tree cache + ClientTools.ClearClientTreeCache().RefreshTree("packager"); + TreeDefinitionCollection.Instance.ReRegisterTrees(); + BizLogicAction.ReRegisterActionsAndHandlers(); + } //this accepts the package, creates the manifest and then installs the files. protected void startInstall(object sender, System.EventArgs e) @@ -323,11 +323,11 @@ namespace umbraco.presentation.developer.packages //and then copy over the files. This will take some time if it contains .dlls that will reboot the system.. _installer.InstallFiles(pId, tempFile.Value); - //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the - // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business - // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app - // pool will be restarted after redirect. - Thread.Sleep(2000); + //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the + // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business + // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app + // pool will be restarted after redirect. + Thread.Sleep(2000); Response.Redirect("installer.aspx?installing=businesslogic&dir=" + tempFile.Value + "&pId=" + pId.ToString()); } @@ -341,5 +341,423 @@ namespace umbraco.presentation.developer.packages pane_success.Visible = false; pane_upload.Visible = false; } + + /// + /// Panel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.UmbracoPanel Panel1; + + /// + /// fb control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback fb; + + /// + /// pane_upload control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_upload; + + /// + /// PropertyPanel9 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; + + /// + /// file1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; + + /// + /// ButtonLoadPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; + + /// + /// progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progbar1; + + /// + /// pane_authenticate control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_authenticate; + + /// + /// tb_email control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_email; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// tb_password control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_password; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// Button1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button Button1; + + /// + /// pane_acceptLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; + + /// + /// pane_acceptLicenseInner control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// LabelName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelName; + + /// + /// PropertyPanel5 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; + + /// + /// LabelAuthor control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelAuthor; + + /// + /// PropertyPanel4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; + + /// + /// LabelMore control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelMore; + + /// + /// PropertyPanel6 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; + + /// + /// LabelLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelLicense; + + /// + /// PropertyPanel7 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; + + /// + /// acceptCheckbox control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; + + /// + /// PropertyPanel8 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; + + /// + /// readme control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal readme; + + /// + /// pp_unsecureFiles control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; + + /// + /// lt_files control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lt_files; + + /// + /// pp_macroConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; + + /// + /// ltrMacroAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; + + /// + /// pp_templateConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; + + protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; + protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; + protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; + + /// + /// ltrTemplateAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; + + /// + /// pp_stylesheetConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; + + /// + /// ltrStylesheetNames control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; + + /// + /// _progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar _progbar1; + + /// + /// ButtonInstall control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonInstall; + + /// + /// pane_installing control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_installing; + + /// + /// progBar2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progBar2; + + /// + /// lit_installStatus control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_installStatus; + + /// + /// pane_optional control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_optional; + + /// + /// pane_success control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_success; + + /// + /// bt_viewInstalledPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; + + /// + /// lit_authorUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_authorUrl; + + /// + /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// processState control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs deleted file mode 100644 index abe1f67506..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs +++ /dev/null @@ -1,433 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.developer.packages { - - - public partial class Installer { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel Panel1; - - /// - /// fb control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback fb; - - /// - /// pane_upload control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_upload; - - /// - /// PropertyPanel9 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; - - /// - /// file1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; - - /// - /// ButtonLoadPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; - - /// - /// progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progbar1; - - /// - /// pane_authenticate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_authenticate; - - /// - /// tb_email control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_email; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// tb_password control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_password; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - - /// - /// pane_acceptLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; - - /// - /// pane_acceptLicenseInner control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// LabelName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelName; - - /// - /// PropertyPanel5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; - - /// - /// LabelAuthor control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelAuthor; - - /// - /// PropertyPanel4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; - - /// - /// LabelMore control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelMore; - - /// - /// PropertyPanel6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; - - /// - /// LabelLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelLicense; - - /// - /// PropertyPanel7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; - - /// - /// acceptCheckbox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; - - /// - /// PropertyPanel8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; - - /// - /// readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal readme; - - /// - /// pp_unsecureFiles control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; - - /// - /// lt_files control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_files; - - /// - /// pp_macroConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; - - /// - /// ltrMacroAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; - - /// - /// pp_templateConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; - - protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; - protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; - protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; - - /// - /// ltrTemplateAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; - - /// - /// pp_stylesheetConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; - - /// - /// ltrStylesheetNames control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; - - /// - /// _progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar _progbar1; - - /// - /// ButtonInstall control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonInstall; - - /// - /// pane_installing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_installing; - - /// - /// progBar2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progBar2; - - /// - /// lit_installStatus control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_installStatus; - - /// - /// pane_optional control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_optional; - - /// - /// pane_success control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_success; - - /// - /// bt_viewInstalledPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; - - /// - /// lit_authorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_authorUrl; - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// processState control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; - } -} From f334c3d74a5473ae5bdb7aa35b678a9f2d54e173 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Jan 2014 17:47:46 +1100 Subject: [PATCH 054/100] playing a bit more with re-initializing the app, but think we're def gonna have to do a full browser refresh. To do that we'll have to figure out how to gen a deep link url to the installer complete screen which could include a custom screen from a package. --- .../src/common/services/assets.service.js | 10 +++++++++- .../src/common/services/util.service.js | 13 +++++++++---- .../umbraco/developer/Packages/installer.aspx | 2 +- src/Umbraco.Web/Editors/BackOfficeController.cs | 1 + 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index dbf1d3fc14..715f795b71 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -74,6 +74,8 @@ angular.module('umbraco.services') /** Internal method. This is used after installing a package to reload the application assets so we don't have to reload the whole window */ _reloadApplicationAssets: function() { + var deferred = $q.defer(); + umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("manifestAssetList", "", "")), 'Failed to get manifest list').then(function(data) { @@ -82,10 +84,16 @@ angular.module('umbraco.services') // and this should just load anything that is newly installed. yepnope({ - load: data + load: data, + complete: function () { + //resolve the promise + deferred.resolve(data); + } }); }); + + return deferred.promise; }, /** diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 8f47a2270d..0b416a2753 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -6,10 +6,15 @@ function packageHelper(assetsService, treeService, eventsService) { /** Called when a package is installed, this resets a bunch of data and ensures the new package assets are loaded in */ packageInstalled: function () { - assetsService._reloadApplicationAssets(); - treeService.clearCache(); - //send event - eventsService.emit("app.reInitialize"); + //assetsService._reloadApplicationAssets().then(function() { + // treeService.clearCache(); + // //send event + // //eventsService.emit("app.reInitialize"); + + // //TODO: This doesn't work and will end in an infinite browser load loop, we can't really + // // re-bootstrap anyways since that would be the same as loading the whole browser window. + // //angular.bootstrap(document, ['umbraco']); + //}); } }; diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx index 6a94b46980..6c7cbe7038 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx @@ -265,7 +265,7 @@ http://issues.umbraco.org/issue/U4-4011 --%>

    diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 8c3d3a6039..3a0b648fc3 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -108,6 +108,7 @@ namespace Umbraco.Web.Editors { {"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")}, {"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")}, + {"serverVarsJs", Url.Action("Application", "BackOffice")}, //API URLs { "contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( From bad118ac6b7fd9687bc5f68ddb2583263879733c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 15 Jan 2014 10:17:46 +0100 Subject: [PATCH 055/100] U4-4046 Rename "empty template" to "empty" --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++++---- ...Views).cshtml => Empty (ForUseWithCustomViews).cshtml} | 0 .../Templates/{EmptyTemplate.cshtml => Empty.cshtml} | 0 ...Views).cshtml => Empty (ForUseWithCustomViews).cshtml} | 0 .../Templates/{EmptyTemplate.cshtml => Empty.cshtml} | 0 src/umbraco.sln | 4 +++- 6 files changed, 7 insertions(+), 5 deletions(-) rename src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/{EmptyTemplate (ForUseWithCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/{EmptyTemplate.cshtml => Empty.cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/{EmptyTemplate (ForUseWithCustomViews).cshtml => Empty (ForUseWithCustomViews).cshtml} (100%) rename src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/{EmptyTemplate.cshtml => Empty.cshtml} (100%) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2af0a7cf5d..577a6cc603 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2167,13 +2167,13 @@ Code - + - + - + Code @@ -2193,7 +2193,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml diff --git a/src/umbraco.sln b/src/umbraco.sln index ba7a350194..d1022a5a71 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,5 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2849E9D4-3B4E-40A3-A309-F3CB4F0E125F}" ProjectSection(SolutionItems) = preProject ..\build\Build.bat = ..\build\Build.bat From 99e91a1053d9bc249efd27d980a0ea35f4eecd77 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 15 Jan 2014 20:55:17 +0100 Subject: [PATCH 056/100] U4-2737 Snippets that are available when creating new MacroScripts should also be available for Partial Views --- src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml | 2 +- src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml index dda3a17f51..2363dcc14c 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml @@ -1 +1 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage \ No newline at end of file +@inherits Umbraco.Web.Mvc.UmbracoTemplatePage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx index b779f6854f..222c2b879a 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx @@ -12,7 +12,7 @@ Filename (without .cshtml):

    - Choose a template:
    + Choose a snippet:
    From 168abddacfd7cfcb243490650b54f32a6bf25129 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 15 Jan 2014 20:57:27 +0100 Subject: [PATCH 057/100] U4-2737 Snippets that are available when creating new MacroScripts should also be available for Partial Views --- src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml | 2 +- src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx | 2 +- src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml index dda3a17f51..2363dcc14c 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml @@ -1 +1 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage \ No newline at end of file +@inherits Umbraco.Web.Mvc.UmbracoTemplatePage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx index b0cc4e634d..b34560a19e 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx @@ -8,7 +8,7 @@ * - + Clean diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx index 7a4372d7c9..9dc16377eb 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx @@ -9,7 +9,7 @@ * - + From ae83b0fbafb1bf94e654f1db8eb672e5b20f8953 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 15 Jan 2014 21:07:24 +0100 Subject: [PATCH 058/100] Hardcoded empty snippets need to be updated as well.. --- .../Umbraco/create/PartialView.ascx.cs | 12 ++++++------ .../umbraco/create/PartialViewMacro.ascx.cs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs index 367daad3f4..777adfec01 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Web.UI; using System.Web.UI.WebControls; @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empty as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs index 8ed79da349..4e06c08cd4 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empt as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); From f56cb2c3ace04601aaa126cdf6f9c72c294d02d6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 15 Jan 2014 21:11:35 +0100 Subject: [PATCH 059/100] Hardcoded empty snippets need to be updated as well.. --- src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs | 10 +++++----- .../umbraco/create/PartialViewMacro.ascx.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs index 5fcd310656..05c9944177 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empty as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs index 8ed79da349..6e07894b9f 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empty as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); From 1e2e26d898b06d0dc9dd83ee67819d96a3b0cc0a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 12:31:29 +1100 Subject: [PATCH 060/100] publicizes server vars parser and event --- src/Umbraco.Web/UI/JavaScript/ServerVariablesParser.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/UI/JavaScript/ServerVariablesParser.cs b/src/Umbraco.Web/UI/JavaScript/ServerVariablesParser.cs index 6b32c68b2c..bab84de3ad 100644 --- a/src/Umbraco.Web/UI/JavaScript/ServerVariablesParser.cs +++ b/src/Umbraco.Web/UI/JavaScript/ServerVariablesParser.cs @@ -4,14 +4,13 @@ using Newtonsoft.Json.Linq; namespace Umbraco.Web.UI.JavaScript { - - internal class ServerVariablesParser + public sealed class ServerVariablesParser { /// - /// Could allow developers to add custom variables on startup - not sure if we want to allow that ? maybe. + /// Could allow developers to add custom variables on startup /// - internal static EventHandler> Parsing; + public static EventHandler> Parsing; internal const string Token = "##Variables##"; From 2b1a43b40a618562a8816a28435bfe4e9a3d70cc Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 18:33:31 +1100 Subject: [PATCH 061/100] Finally got somewhere with U4-4011 Package installation will require either a full app refresh or we will need to re-lazy load in all of the assets - took ages to get this going, now to get it working when packages have custom screens. --- .../src/common/services/assets.service.js | 25 ---------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 + .../umbraco/ClientRedirect.aspx | 23 +++++++++ .../umbraco/developer/Packages/installer.aspx | 37 ++++++++++++-- .../developer/Packages/installer.aspx.cs | 48 +++++++++++++++++-- 5 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index 715f795b71..e3f86a4b14 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -71,31 +71,6 @@ angular.module('umbraco.services') return deferred.promise; }, - /** Internal method. This is used after installing a package to reload the application assets so we don't have to reload the whole window */ - _reloadApplicationAssets: function() { - - var deferred = $q.defer(); - - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("manifestAssetList", "", "")), - 'Failed to get manifest list').then(function(data) { - - //ok so we have the list of assets, now we'll use yepnope to go get them. Anything that is already loaded should remain loaded - // and this should just load anything that is newly installed. - - yepnope({ - load: data, - complete: function () { - //resolve the promise - deferred.resolve(data); - } - }); - - }); - - return deferred.promise; - }, - /** * @ngdoc method * @name umbraco.services.assetsService#loadCss diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 4bdd618fa2..e68642c61f 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -616,6 +616,7 @@ treeInit.aspx + diff --git a/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx new file mode 100644 index 0000000000..fd9c5182f2 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx @@ -0,0 +1,23 @@ +<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> +<%-- + This page is required because we cannot reload the angular app with a changed Hash since it just detects the hash and doesn't reload. + So this is used purely for a full reload of an angular app with a changed hash. +--%> + + + + Redirecting... + + + + Redirecting... + + diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx index 6c7cbe7038..88750a011d 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx @@ -1,6 +1,7 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" CodeBehind="installer.aspx.cs" +<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" Trace="false" ValidateRequest="false" %> +<%@ Import Namespace="umbraco" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> @@ -261,11 +262,8 @@ - <%--This is a hack to fix this currently until we can replace the installer with a native angular editor - http://issues.umbraco.org/issue/U4-4011 - --%>

    @@ -282,6 +280,35 @@ + + + +

    + Please wait while the browser is reloaded... +
    + + +
    +
    diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index a192176fda..c91cb1c030 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -36,6 +36,8 @@ namespace umbraco.presentation.developer.packages private readonly cms.businesslogic.packager.Installer _installer = new cms.businesslogic.packager.Installer(); private string _tempFileName = ""; + protected string RefreshQueryString { get; set; } + protected void Page_Load(object sender, EventArgs e) { var ex = new Exception(); @@ -234,7 +236,7 @@ namespace umbraco.presentation.developer.packages } else { - Response.Redirect("installer.aspx?installing=finished&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); } break; case "customInstaller": @@ -261,9 +263,12 @@ namespace umbraco.presentation.developer.packages { //if the custom installer control is empty here (though it should never be because we've already checked for it previously) //then we should run the normal FinishedAction - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl")); } break; + case "refresh": + PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl")); + break; case "finished": PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); break; @@ -295,6 +300,39 @@ namespace umbraco.presentation.developer.packages PerformPostInstallCleanup(packageId, dir); } + /// + /// Perform the 'Refresh' action of the installer + /// + /// + /// + /// + private void PerformRefreshAction(int packageId, string dir, string url) + { + HideAllPanes(); + + //create the URL to refresh to + // /umbraco/developer/packages/installer.aspx?installing=finished + // &dir=X:\Projects\Umbraco\Umbraco_7.0\src\Umbraco.Web.UI\App_Data\aef8c41f-63a0-494b-a1e2-10d761647033 + // &pId=3 + // &customUrl=http:%2f%2four.umbraco.org%2fprojects%2fwebsite-utilities%2fmerchello + + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=finished&dir={0}&pId={1}&customUrl={2}", + dir, packageId, url)); + + pane_refresh.Visible = true; + + PerformPostInstallCleanup(packageId, dir); + } + + /// + /// Runs Post refresh actions such reloading the correct tree nodes, etc... + /// + private void PerformPostRefreshAction() + { + BasePage.Current.ClientTools.ReloadActionNode(true, true); + } + /// /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... /// @@ -302,9 +340,8 @@ namespace umbraco.presentation.developer.packages /// private void PerformPostInstallCleanup(int packageId, string dir) { - BasePage.Current.ClientTools.ReloadActionNode(true, true); _installer.InstallCleanUp(packageId, dir); - //clear the tree cache + //clear the tree cache - we'll do this here even though the browser will reload, but just in case it doesn't can't hurt. ClientTools.ClearClientTreeCache().RefreshTree("packager"); TreeDefinitionCollection.Instance.ReRegisterTrees(); BizLogicAction.ReRegisterActionsAndHandlers(); @@ -339,6 +376,7 @@ namespace umbraco.presentation.developer.packages pane_installing.Visible = false; pane_optional.Visible = false; pane_success.Visible = false; + pane_refresh.Visible = false; pane_upload.Visible = false; } @@ -724,6 +762,8 @@ namespace umbraco.presentation.developer.packages /// protected global::umbraco.uicontrols.Pane pane_success; + protected global::umbraco.uicontrols.Pane pane_refresh; + /// /// bt_viewInstalledPackage control. /// From 4b22ff3a759bcb9c87ac99fcd58e054f356b993b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 19:11:03 +1100 Subject: [PATCH 062/100] Completes: U4-4011 Package installation will require either a full app refresh or we will need to re-lazy load in all of the assets --- .../developer/Packages/installer.aspx.cs | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index c91cb1c030..a429f3018f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -3,6 +3,7 @@ using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Globalization; using System.Threading; using System.Web; using System.Web.SessionState; @@ -96,7 +97,7 @@ namespace umbraco.presentation.developer.packages else { fb.Style.Add("margin-top", "7px"); - fb.type = global::umbraco.uicontrols.Feedback.feedbacktype.error; + fb.type = uicontrols.Feedback.feedbacktype.error; fb.Text = "No connection to repository. Runway could not be installed as there was no connection to: '" + _repo.RepositoryUrl + "'"; pane_upload.Visible = false; } @@ -109,7 +110,7 @@ namespace umbraco.presentation.developer.packages acceptCheckbox.Attributes.Add("onmouseup", "document.getElementById('" + ButtonInstall.ClientID + "').disabled = false;"); } - protected void uploadFile(object sender, System.EventArgs e) + protected void uploadFile(object sender, EventArgs e) { try { @@ -133,7 +134,7 @@ namespace umbraco.presentation.developer.packages string memberGuid = _repo.Webservice.authenticate(tb_email.Text, library.md5(tb_password.Text)); //if we auth correctly and get a valid key back, we will fetch the file from the repo webservice. - if (!string.IsNullOrEmpty(memberGuid)) + if (string.IsNullOrEmpty(memberGuid) == false) { tempFile.Value = _installer.Import(_repo.fetch(helper.Request("guid"), memberGuid)); UpdateSettings(); @@ -230,9 +231,9 @@ namespace umbraco.presentation.developer.packages //making sure that publishing actions performed from the cms layer gets pushed to the presentation library.RefreshContent(); - if (!string.IsNullOrEmpty(_installer.Control)) + if (string.IsNullOrEmpty(_installer.Control) == false) { - Response.Redirect("installer.aspx?installing=customInstaller&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); } else { @@ -242,11 +243,11 @@ namespace umbraco.presentation.developer.packages case "customInstaller": var customControl = Request.GetItemAsString("customControl"); - if (!customControl.IsNullOrWhiteSpace()) + if (customControl.IsNullOrWhiteSpace() == false) { HideAllPanes(); - _configControl = new System.Web.UI.UserControl().LoadControl(SystemDirectories.Root + customControl); + _configControl = LoadControl(SystemDirectories.Root + customControl); _configControl.ID = "packagerConfigControl"; pane_optional.Controls.Add(_configControl); @@ -263,11 +264,11 @@ namespace umbraco.presentation.developer.packages { //if the custom installer control is empty here (though it should never be because we've already checked for it previously) //then we should run the normal FinishedAction - PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); } break; case "refresh": - PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl"), Request.GetItemAsString("customControl")); break; case "finished": PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); @@ -287,7 +288,7 @@ namespace umbraco.presentation.developer.packages { HideAllPanes(); //string url = _installer.Url; - string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(); + string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(CultureInfo.InvariantCulture); bt_viewInstalledPackage.OnClientClick = "document.location = '" + packageViewUrl + "'; return false;"; @@ -306,19 +307,29 @@ namespace umbraco.presentation.developer.packages /// /// /// - private void PerformRefreshAction(int packageId, string dir, string url) + /// + private void PerformRefreshAction(int packageId, string dir, string url, string customControl) { HideAllPanes(); - + //create the URL to refresh to // /umbraco/developer/packages/installer.aspx?installing=finished // &dir=X:\Projects\Umbraco\Umbraco_7.0\src\Umbraco.Web.UI\App_Data\aef8c41f-63a0-494b-a1e2-10d761647033 // &pId=3 // &customUrl=http:%2f%2four.umbraco.org%2fprojects%2fwebsite-utilities%2fmerchello - RefreshQueryString = Server.UrlEncode(string.Format( + if (customControl.IsNullOrWhiteSpace()) + { + RefreshQueryString = Server.UrlEncode(string.Format( "installing=finished&dir={0}&pId={1}&customUrl={2}", dir, packageId, url)); + } + else + { + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=customInstaller&dir={0}&pId={1}&customUrl={2}&customControl={3}", + dir, packageId, url, customControl)); + } pane_refresh.Visible = true; From c288734877d77c19450f320010540f0d07c5924e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 19:47:27 +1100 Subject: [PATCH 063/100] Fixes: U4-4054 Multinode Treepicker datatype - selecting a start node sends to content section --- .../views/prevalueeditors/treesource.controller.js | 9 +++++++++ .../src/views/prevalueeditors/treesource.html | 13 ------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js index 87fa2c8724..5850225e68 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js @@ -5,6 +5,15 @@ angular.module('umbraco') function($scope, dialogService, entityResource, $log, iconHelper){ + if (!$scope.model) { + $scope.model = {}; + } + if (!$scope.model.value) { + $scope.model.value = { + type: "content" + }; + } + if($scope.model.value.id && $scope.model.value.type !== "member"){ var ent = "Document"; if($scope.model.value.type === "media"){ diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html index 0e375cc106..c9fdd8674d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html @@ -24,20 +24,7 @@ Choose... - - \ No newline at end of file From 122069752ac65353a75073f0c5aa4b8bd8bf3d0d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 20:03:29 +1100 Subject: [PATCH 064/100] fixes legacy validate member logic --- .../members/UmbracoMembershipProvider.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/umbraco.providers/members/UmbracoMembershipProvider.cs b/src/umbraco.providers/members/UmbracoMembershipProvider.cs index fed4a5cd5d..c4c130b2c3 100644 --- a/src/umbraco.providers/members/UmbracoMembershipProvider.cs +++ b/src/umbraco.providers/members/UmbracoMembershipProvider.cs @@ -692,59 +692,62 @@ namespace umbraco.providers.members if (isLocked) { LogHelper.Info("Cannot validate member " + username + " because they are currently locked out"); - m = null; + return false; } } } } //check for approve status. If not approved, then set the member property to null - if (m != null && CheckApproveStatus(m) == false) + if (CheckApproveStatus(m) == false) { LogHelper.Info("Cannot validate member " + username + " because they are not approved"); - m = null; + return false; } // maybe update login date - if (m != null && string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) { UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); } // maybe reset password attempts - if (m != null && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) { UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, 0); } // persist data - if (m != null) - m.Save(); + m.Save(); + + return true; } - else if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false + + + // update fail rate if it's approved + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) - { - var updateMemberDataObject = Member.GetMemberFromLoginName(username); - // update fail rate if it's approved - if (updateMemberDataObject != null && CheckApproveStatus(updateMemberDataObject)) + { + if (CheckApproveStatus(m)) { - int failedAttempts = 0; - int.TryParse(GetMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); + var failedAttempts = 0; + int.TryParse(GetMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); failedAttempts = failedAttempts + 1; - UpdateMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); + UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); // lock user? if (failedAttempts >= MaxInvalidPasswordAttempts) { - UpdateMemberProperty(updateMemberDataObject, LockPropertyTypeAlias, 1); - UpdateMemberProperty(updateMemberDataObject, LastLockedOutPropertyTypeAlias, DateTime.Now); + UpdateMemberProperty(m, LockPropertyTypeAlias, 1); + UpdateMemberProperty(m, LastLockedOutPropertyTypeAlias, DateTime.Now); LogHelper.Info("Member " + username + " is now locked out, max invalid password attempts exceeded"); } - updateMemberDataObject.Save(); + m.Save(); } } - return (m != null); + + return false; } private static void UpdateMemberProperty(Member m, string propertyTypeAlias, object propertyValue) From c38030def28094889c46266c9d5d56579a18b552 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 20:47:13 +1100 Subject: [PATCH 065/100] Fixes: U4-3855 Preview cookie should be a session cookie not persisted --- .../Configuration/UmbracoSettings.cs | 2 +- src/Umbraco.Core/Constants-Web.cs | 22 ++++++++++++ .../Security/AuthenticationExtensions.cs | 34 +++++++++++-------- src/Umbraco.Core/Umbraco.Core.csproj | 3 ++ .../TestHelpers/FakeHttpContextFactory.cs | 3 +- src/umbraco.businesslogic/StateHelper.cs | 20 +++++++---- 6 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Core/Constants-Web.cs diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index 6b53540238..a6f81b593c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -309,7 +309,7 @@ namespace Umbraco.Core.Configuration { return value; } - return "UMB_UCONTEXT"; + return Constants.Web.AuthCookieName; } } diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs new file mode 100644 index 0000000000..83cb995eeb --- /dev/null +++ b/src/Umbraco.Core/Constants-Web.cs @@ -0,0 +1,22 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the identifiers for Umbraco system nodes. + /// + public static class Web + { + /// + /// The preview cookie name + /// + public const string PreviewCookieName = "UMB_PREVIEW"; + + /// + /// The auth cookie name + /// + public const string AuthCookieName = "UMB_UCONTEXT"; + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 87d09aecf5..306a385f61 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -99,21 +99,27 @@ namespace Umbraco.Core.Security /// private static void Logout(this HttpContextBase http, string cookieName) { - //remove from the request - http.Request.Cookies.Remove(cookieName); + //clear the preview cookie too + var cookies = new[] { cookieName, Constants.Web.PreviewCookieName }; + foreach (var c in cookies) + { + //remove from the request + http.Request.Cookies.Remove(c); + + //expire from the response + var formsCookie = http.Response.Cookies[c]; + if (formsCookie != null) + { + //this will expire immediately and be removed from the browser + formsCookie.Expires = DateTime.Now.AddYears(-1); + } + else + { + //ensure there's def an expired cookie + http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) }); + } + } - //expire from the response - var formsCookie = http.Response.Cookies[cookieName]; - if (formsCookie != null) - { - //this will expire immediately and be removed from the browser - formsCookie.Expires = DateTime.Now.AddYears(-1); - } - else - { - //ensure there's def an expired cookie - http.Response.Cookies.Add(new HttpCookie(cookieName) { Expires = DateTime.Now.AddYears(-1) }); - } } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 90bf089d0f..09ab59e054 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -837,6 +837,9 @@ Constants.cs + + Constants.cs + diff --git a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs index abb477d3c6..c7e7764512 100644 --- a/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs @@ -7,6 +7,7 @@ using System.Text; using System.Web; using System.Web.Routing; using Moq; +using Umbraco.Core; namespace Umbraco.Tests.TestHelpers { @@ -59,7 +60,7 @@ namespace Umbraco.Tests.TestHelpers //Cookie collection var cookieCollection = new HttpCookieCollection(); - cookieCollection.Add(new HttpCookie("UMB_UCONTEXT", "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); + cookieCollection.Add(new HttpCookie(Constants.Web.AuthCookieName, "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); //Request var requestMock = new Mock(); diff --git a/src/umbraco.businesslogic/StateHelper.cs b/src/umbraco.businesslogic/StateHelper.cs index 387efd2ca3..d14031d7c1 100644 --- a/src/umbraco.businesslogic/StateHelper.cs +++ b/src/umbraco.businesslogic/StateHelper.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; using System.Web; using System.Web.UI; +using Umbraco.Core; namespace umbraco.BusinessLogic { @@ -347,8 +348,8 @@ namespace umbraco.BusinessLogic * we currently reproduce this by configuring each cookie with a 30d expires, but does * that actually make sense? shouldn't some cookie have _no_ expires? */ - static readonly Cookie _preview = new Cookie("UMB_PREVIEW", 30d); // was "PreviewSet" - static readonly Cookie _userContext = new Cookie("UMB_UCONTEXT", 30d); // was "UserContext" + static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, TimeSpan.Zero); // was "PreviewSet" + static readonly Cookie _userContext = new Cookie(Constants.Web.AuthCookieName, 30d); // was "UserContext" static readonly Cookie _member = new Cookie("UMB_MEMBER", 30d); // was "umbracoMember" public static Cookie Preview { get { return _preview; } } @@ -429,7 +430,7 @@ namespace umbraco.BusinessLogic } public void SetValue(string value) { - SetValueWithDate(value, DateTime.Now + _expires); + SetValueWithDate(value, _expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + _expires); } public void SetValue(string value, double days) @@ -439,7 +440,7 @@ namespace umbraco.BusinessLogic public void SetValue(string value, TimeSpan expires) { - SetValue(value, DateTime.Now + expires); + SetValue(value, expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + expires); } public void SetValue(string value, DateTime expires) @@ -449,7 +450,7 @@ namespace umbraco.BusinessLogic private void SetValueWithDate(string value, DateTime expires) { - HttpCookie cookie = new HttpCookie(_key, value); + var cookie = new HttpCookie(_key, value); if (GlobalSettings.UseSSL) cookie.Secure = true; @@ -457,7 +458,12 @@ namespace umbraco.BusinessLogic //ensure http only, this should only be able to be accessed via the server cookie.HttpOnly = true; - cookie.Expires = expires; + //set an expiry date if not min value, otherwise leave it as a session cookie. + if (expires != DateTime.MinValue) + { + cookie.Expires = expires; + } + ResponseCookie = cookie; // original Umbraco code also does this @@ -471,7 +477,7 @@ namespace umbraco.BusinessLogic { if (RequestCookie != null || ResponseCookie != null) { - HttpCookie cookie = new HttpCookie(_key); + var cookie = new HttpCookie(_key); cookie.Expires = DateTime.Now.AddDays(-1); ResponseCookie = cookie; } From ee7ee81098e4aa2c392ce14af131b0898f2b7590 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 20:51:23 +1100 Subject: [PATCH 066/100] fixes merge issue --- src/Umbraco.Core/Constants-System.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index f22c41d922..c4ce5b58f6 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -23,22 +23,5 @@ public const int RecycleBinMedia = -21; } - - /// - /// Defines the identifiers for Umbraco system nodes. - /// - public static class Web - { - /// - /// The preview cookie name - /// - public const string PreviewCookieName = "UMB_PREVIEW"; - - /// - /// The auth cookie name - /// - public const string AuthCookieName = "UMB_UCONTEXT"; - - } } } \ No newline at end of file From 8926e8c7d950a7640b6e4c26bd8c4c5a486d6ef0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Jan 2014 20:56:34 +1100 Subject: [PATCH 067/100] ensures preview cookie is gone on logout. --- src/Umbraco.Core/Security/AuthenticationExtensions.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 6ba1df8b6c..45c290daba 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -137,12 +137,19 @@ namespace Umbraco.Core.Security { if (response == null) throw new ArgumentNullException("response"); //remove the cookie - var cookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") + var authCookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") { Expires = DateTime.Now.AddYears(-1), Path = "/" }; - response.Headers.AddCookies(new[] { cookie }); + //remove the preview cookie too + var prevCookie = new CookieHeaderValue(Constants.Web.PreviewCookieName, "") + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }; + + response.Headers.AddCookies(new[] { authCookie, prevCookie }); } /// From 326309e61ee022755b428afa0409b8938c9d607b Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 16 Jan 2014 17:03:51 +0100 Subject: [PATCH 068/100] U4-4056 - better unicode/ascii support for urls --- .../Configuration/UmbracoSettings.cs | 2 + .../Strings/DefaultShortStringHelper.cs | 13 +- .../Strings/Utf8ToAsciiConverter.cs | 237 ++++++++++++++++++ .../DefaultShortStringHelperTests.cs | 20 ++ 4 files changed, 271 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index a6f81b593c..0bdb0cd5c9 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -855,6 +855,8 @@ namespace Umbraco.Core.Configuration /// /// Whether to replace double dashes from url (ie my--story----from--dash.aspx caused by multiple url replacement chars /// + // was used by the legacy short string helper, is not used anymore by the new default short string helper + // should update documentation internal static bool RemoveDoubleDashesFromUrlReplacing { get diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 38e9edc4d8..fbbc25ea20 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -71,6 +71,17 @@ namespace Umbraco.Core.Strings } } + private static bool UrlReplacingToAscii + { + get + { + var replaceChars = UmbracoSettings.UrlReplaceCharacters; + if (replaceChars == null || replaceChars.Attributes == null) return false; + var attr = replaceChars.Attributes.GetNamedItem("toAscii"); + return attr != null && attr.Value == "true"; + } + } + /// /// Returns a new string in which characters have been replaced according to the Umbraco settings UrlReplaceCharacters. /// @@ -147,7 +158,7 @@ namespace Umbraco.Core.Strings { PreFilter = ApplyUrlReplaceCharacters, IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore - StringType = CleanStringType.Utf8 | CleanStringType.LowerCase, + StringType = (UrlReplacingToAscii ? CleanStringType.Ascii : CleanStringType.Utf8) | CleanStringType.LowerCase, BreakTermsOnUpper = false, Separator = '-' }).WithConfig(CleanStringType.FileName, new Config diff --git a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs index 23ac4e3931..e08defdedd 100644 --- a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs +++ b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs @@ -3316,6 +3316,243 @@ namespace Umbraco.Core.Strings output[opos++] = '~'; break; + // BEGIN CUSTOM TRANSLITERATION OF CYRILIC CHARS + + #region Cyrilic chars + + // russian uppercase "А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я" + // russian lowercase "а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я" + + // notes + // read http://www.vesic.org/english/blog/c-sharp/transliteration-easy-way-microsoft-transliteration-utility/ + // should we look into MS Transliteration Utility (http://msdn.microsoft.com/en-US/goglobal/bb688104.aspx) + // also UnicodeSharpFork https://bitbucket.org/DimaStefantsov/unidecodesharpfork + // also Transliterator http://transliterator.codeplex.com/ + // + // in any case it would be good to generate all those "case" statements instead of writing them by hand + // time for a T4 template? + // also we should support extensibility so ppl can register more cases in external code + + // fixme + // transliterates Анастасия as Anastasiya, and not Anastasia + // Ольга --> Ol'ga, Татьяна --> Tat'yana -- that's bad (?) + // Note: should ä (german umlaut) become a or ae ? + + case '\u0410': // А + output[opos++] = 'A'; + break; + case '\u0430': // а + output[opos++] = 'a'; + break; + case '\u0411': // Б + output[opos++] = 'B'; + break; + case '\u0431': // б + output[opos++] = 'b'; + break; + case '\u0412': // В + output[opos++] = 'V'; + break; + case '\u0432': // в + output[opos++] = 'v'; + break; + case '\u0413': // Г + output[opos++] = 'G'; + break; + case '\u0433': // г + output[opos++] = 'g'; + break; + case '\u0414': // Д + output[opos++] = 'D'; + break; + case '\u0434': // д + output[opos++] = 'd'; + break; + case '\u0415': // Е + output[opos++] = 'E'; + break; + case '\u0435': // е + output[opos++] = 'e'; + break; + case '\u0401': // Ё + output[opos++] = 'E'; // alt. Yo + break; + case '\u0451': // ё + output[opos++] = 'e'; // alt. yo + break; + case '\u0416': // Ж + output[opos++] = 'Z'; + output[opos++] = 'h'; + break; + case '\u0436': // ж + output[opos++] = 'z'; + output[opos++] = 'h'; + break; + case '\u0417': // З + output[opos++] = 'Z'; + break; + case '\u0437': // з + output[opos++] = 'z'; + break; + case '\u0418': // И + output[opos++] = 'I'; + break; + case '\u0438': // и + output[opos++] = 'i'; + break; + case '\u0419': // Й + output[opos++] = 'I'; // alt. Y, J + break; + case '\u0439': // й + output[opos++] = 'i'; // alt. y, j + break; + case '\u041A': // К + output[opos++] = 'K'; + break; + case '\u043A': // к + output[opos++] = 'k'; + break; + case '\u041B': // Л + output[opos++] = 'L'; + break; + case '\u043B': // л + output[opos++] = 'l'; + break; + case '\u041C': // М + output[opos++] = 'M'; + break; + case '\u043C': // м + output[opos++] = 'm'; + break; + case '\u041D': // Н + output[opos++] = 'N'; + break; + case '\u043D': // н + output[opos++] = 'n'; + break; + case '\u041E': // О + output[opos++] = 'O'; + break; + case '\u043E': // о + output[opos++] = 'o'; + break; + case '\u041F': // П + output[opos++] = 'P'; + break; + case '\u043F': // п + output[opos++] = 'p'; + break; + case '\u0420': // Р + output[opos++] = 'R'; + break; + case '\u0440': // р + output[opos++] = 'r'; + break; + case '\u0421': // С + output[opos++] = 'S'; + break; + case '\u0441': // с + output[opos++] = 's'; + break; + case '\u0422': // Т + output[opos++] = 'T'; + break; + case '\u0442': // т + output[opos++] = 't'; + break; + case '\u0423': // У + output[opos++] = 'U'; + break; + case '\u0443': // у + output[opos++] = 'u'; + break; + case '\u0424': // Ф + output[opos++] = 'F'; + break; + case '\u0444': // ф + output[opos++] = 'f'; + break; + case '\u0425': // Х + output[opos++] = 'K'; // alt. X + output[opos++] = 'h'; + break; + case '\u0445': // х + output[opos++] = 'k'; // alt. x + output[opos++] = 'h'; + break; + case '\u0426': // Ц + output[opos++] = 'F'; + break; + case '\u0446': // ц + output[opos++] = 'f'; + break; + case '\u0427': // Ч + output[opos++] = 'C'; // alt. Ts, C + output[opos++] = 'h'; + break; + case '\u0447': // ч + output[opos++] = 'c'; // alt. ts, c + output[opos++] = 'h'; + break; + case '\u0428': // Ш + output[opos++] = 'S'; // alt. Ch, S + output[opos++] = 'h'; + break; + case '\u0448': // ш + output[opos++] = 's'; // alt. ch, s + output[opos++] = 'h'; + break; + case '\u0429': // Щ + output[opos++] = 'S'; // alt. Shch, Sc + output[opos++] = 'h'; + break; + case '\u0449': // щ + output[opos++] = 's'; // alt. shch, sc + output[opos++] = 'h'; + break; + case '\u042A': // Ъ + output[opos++] = '"'; // " + break; + case '\u044A': // ъ + output[opos++] = '"'; // " + break; + case '\u042B': // Ы + output[opos++] = 'Y'; + break; + case '\u044B': // ы + output[opos++] = 'y'; + break; + case '\u042C': // Ь + output[opos++] = '\''; // ' + break; + case '\u044C': // ь + output[opos++] = '\''; // ' + break; + case '\u042D': // Э + output[opos++] = 'E'; + break; + case '\u044D': // э + output[opos++] = 'e'; + break; + case '\u042E': // Ю + output[opos++] = 'Y'; // alt. Ju + output[opos++] = 'u'; + break; + case '\u044E': // ю + output[opos++] = 'y'; // alt. ju + output[opos++] = 'u'; + break; + case '\u042F': // Я + output[opos++] = 'Y'; // alt. Ja + output[opos++] = 'a'; + break; + case '\u044F': // я + output[opos++] = 'y'; // alt. ja + output[opos++] = 'a'; + break; + + #endregion + // BEGIN EXTRA /* case '£': diff --git a/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs index 6828e4ea88..83328c5135 100644 --- a/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs @@ -94,6 +94,26 @@ namespace Umbraco.Tests.CoreStrings return s; } + [Test] + public void U4_4056() + { + const string input = "ÆØÅ and æøå and 中文测试 and אודות האתר and größer БбДдЖж page"; + + var helper = new DefaultShortStringHelper().WithDefaultConfig(); // unicode + var output = helper.CleanStringForUrlSegment(input); + Assert.AreEqual("æøå-and-æøå-and-中文测试-and-אודות-האתר-and-größer-ббдджж-page", output); + + helper = new DefaultShortStringHelper() + .WithConfig(CleanStringType.UrlSegment, new DefaultShortStringHelper.Config + { + IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', + StringType = CleanStringType.LowerCase | CleanStringType.Ascii, // ascii + Separator = '-' + }); + output = helper.CleanStringForUrlSegment(input); + Assert.AreEqual("aeoa-and-aeoa-and-and-and-grosser-bbddzhzh-page", output); + } + [Test] public void CleanStringUnderscoreInTerm() { From c2ee3303a552a579d7acc74fe8bc1cb0e5c1eca3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 16 Jan 2014 19:49:57 +0100 Subject: [PATCH 069/100] U4-4059 Update MNTP snippets for string of id's rather than old xml fragment --- .../PartialViewMacros/Templates/MultinodeTree-picker.cshtml | 4 ++-- .../PartialViews/Templates/MultinodeTree-picker.cshtml | 4 ++-- .../scripting/templates/cshtml/MultinodeTree-picker.cshtml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml index f9e913d9c4..062e5c7171 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml @@ -10,10 +10,10 @@ @* Lists each selected value from the picker as a link *@