diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index c9f8c4fba2..d3ab992a23 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -17,7 +17,7 @@ - + diff --git a/src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs b/src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs index 07c9a8ded2..1a4e7ae1a9 100644 --- a/src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs +++ b/src/Umbraco.Core/Collections/CompositeTypeTypeKey.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Collections /// /// Initializes a new instance of the struct. /// - public CompositeTypeTypeKey(Type type1, Type type2) + public CompositeTypeTypeKey(Type type1, Type type2) : this() { Type1 = type1; Type2 = type2; @@ -19,26 +19,35 @@ namespace Umbraco.Core.Collections /// /// Gets the first type. /// - public Type Type1 { get; } + public Type Type1 { get; private set; } /// /// Gets the second type. /// - public Type Type2 { get; } + public Type Type2 { get; private set; } /// public bool Equals(CompositeTypeTypeKey other) - => Type1 == other.Type1 && Type2 == other.Type2; + { + return Type1 == other.Type1 && Type2 == other.Type2; + } /// public override bool Equals(object obj) - => obj is CompositeTypeTypeKey other && Type1 == other.Type1 && Type2 == other.Type2; + { + var other = obj is CompositeTypeTypeKey ? (CompositeTypeTypeKey)obj : default(CompositeTypeTypeKey); + return Type1 == other.Type1 && Type2 == other.Type2; + } public static bool operator ==(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2) - => key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2; + { + return key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2; + } public static bool operator !=(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2) - => key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2; + { + return key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2; + } /// public override int GetHashCode() diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 1e3c75ee6d..ccd379f7d2 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -140,7 +140,8 @@ namespace Umbraco.Core if (underlying != null) { // Special case for empty strings for bools/dates which should return null if an empty string. - if (input is string inputString) + var inputString = input as string; + if (inputString != null) { if (string.IsNullOrEmpty(inputString) && (underlying == typeof(DateTime) || underlying == typeof(bool))) { @@ -166,7 +167,8 @@ namespace Umbraco.Core { // target is not a generic type - if (input is string inputString) + var inputString = input as string; + if (inputString != null) { // Try convert from string, returns an Attempt if the string could be // processed (either succeeded or failed), else null if we need to try @@ -207,7 +209,8 @@ namespace Umbraco.Core } // Re-check convertables since we altered the input through recursion - if (input is IConvertible convertible2) + var convertible2 = input as IConvertible; + if (convertible2 != null) { return Attempt.Succeed(Convert.ChangeType(convertible2, target)); } @@ -265,7 +268,8 @@ namespace Umbraco.Core { if (target == typeof(int)) { - if (int.TryParse(input, out var value)) + int value; + if (int.TryParse(input, out value)) { return Attempt.Succeed(value); } @@ -273,26 +277,30 @@ namespace Umbraco.Core // Because decimal 100.01m will happily convert to integer 100, it // makes sense that string "100.01" *also* converts to integer 100. var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2)); + decimal value2; + return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2)); } if (target == typeof(long)) { - if (long.TryParse(input, out var value)) + long value; + if (long.TryParse(input, out value)) { return Attempt.Succeed(value); } // Same as int var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2)); + decimal value2; + return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2)); } // TODO: Should we do the decimal trick for short, byte, unsigned? if (target == typeof(bool)) { - if (bool.TryParse(input, out var value)) + bool value; + if (bool.TryParse(input, out value)) { return Attempt.Succeed(value); } @@ -305,42 +313,53 @@ namespace Umbraco.Core switch (Type.GetTypeCode(target)) { case TypeCode.Int16: - return Attempt.SucceedIf(short.TryParse(input, out var value), value); + short value; + return Attempt.SucceedIf(short.TryParse(input, out value), value); case TypeCode.Double: var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(double.TryParse(input2, out var valueD), valueD); + double valueD; + return Attempt.SucceedIf(double.TryParse(input2, out valueD), valueD); case TypeCode.Single: var input3 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(float.TryParse(input3, out var valueF), valueF); + float valueF; + return Attempt.SucceedIf(float.TryParse(input3, out valueF), valueF); case TypeCode.Char: - return Attempt.SucceedIf(char.TryParse(input, out var valueC), valueC); + char valueC; + return Attempt.SucceedIf(char.TryParse(input, out valueC), valueC); case TypeCode.Byte: - return Attempt.SucceedIf(byte.TryParse(input, out var valueB), valueB); + byte valueB; + return Attempt.SucceedIf(byte.TryParse(input, out valueB), valueB); case TypeCode.SByte: - return Attempt.SucceedIf(sbyte.TryParse(input, out var valueSb), valueSb); + sbyte valueSb; + return Attempt.SucceedIf(sbyte.TryParse(input, out valueSb), valueSb); case TypeCode.UInt32: - return Attempt.SucceedIf(uint.TryParse(input, out var valueU), valueU); + uint valueU; + return Attempt.SucceedIf(uint.TryParse(input, out valueU), valueU); case TypeCode.UInt16: - return Attempt.SucceedIf(ushort.TryParse(input, out var valueUs), valueUs); + ushort valueUs; + return Attempt.SucceedIf(ushort.TryParse(input, out valueUs), valueUs); case TypeCode.UInt64: - return Attempt.SucceedIf(ulong.TryParse(input, out var valueUl), valueUl); + ulong valueUl; + return Attempt.SucceedIf(ulong.TryParse(input, out valueUl), valueUl); } } else if (target == typeof(Guid)) { - return Attempt.SucceedIf(Guid.TryParse(input, out var value), value); + Guid value; + return Attempt.SucceedIf(Guid.TryParse(input, out value), value); } else if (target == typeof(DateTime)) { - if (DateTime.TryParse(input, out var value)) + DateTime value; + if (DateTime.TryParse(input, out value)) { switch (value.Kind) { @@ -360,20 +379,24 @@ namespace Umbraco.Core } else if (target == typeof(DateTimeOffset)) { - return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out var value), value); + DateTimeOffset value; + return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out value), value); } else if (target == typeof(TimeSpan)) { - return Attempt.SucceedIf(TimeSpan.TryParse(input, out var value), value); + TimeSpan value; + return Attempt.SucceedIf(TimeSpan.TryParse(input, out value), value); } else if (target == typeof(decimal)) { var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.SucceedIf(decimal.TryParse(input2, out var value), value); + decimal value; + return Attempt.SucceedIf(decimal.TryParse(input2, out value), value); } else if (input != null && target == typeof(Version)) { - return Attempt.SucceedIf(Version.TryParse(input, out var value), value); + Version value; + return Attempt.SucceedIf(Version.TryParse(input, out value), value); } // E_NOTIMPL IPAddress, BigInteger @@ -658,7 +681,8 @@ namespace Umbraco.Core { var key = new CompositeTypeTypeKey(source, target); - if (InputTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter)) + TypeConverter typeConverter; + if (InputTypeConverterCache.TryGetValue(key, out typeConverter)) { return typeConverter; } @@ -678,7 +702,8 @@ namespace Umbraco.Core { var key = new CompositeTypeTypeKey(source, target); - if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter)) + TypeConverter typeConverter; + if (DestinationTypeConverterCache.TryGetValue(key, out typeConverter)) { return typeConverter; } @@ -696,7 +721,8 @@ namespace Umbraco.Core [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Type GetCachedGenericNullableType(Type type) { - if (NullableGenericCache.TryGetValue(type, out Type underlyingType)) + Type underlyingType; + if (NullableGenericCache.TryGetValue(type, out underlyingType)) { return underlyingType; } @@ -715,7 +741,8 @@ namespace Umbraco.Core private static bool GetCachedCanAssign(object input, Type source, Type target) { var key = new CompositeTypeTypeKey(source, target); - if (AssignableTypeCache.TryGetValue(key, out bool canConvert)) + bool canConvert; + if (AssignableTypeCache.TryGetValue(key, out canConvert)) { return canConvert; } @@ -734,7 +761,8 @@ namespace Umbraco.Core [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool GetCachedCanConvertToBoolean(Type type) { - if (BoolConvertCache.TryGetValue(type, out bool result)) + bool result; + if (BoolConvertCache.TryGetValue(type, out result)) { return result; } @@ -747,4 +775,4 @@ namespace Umbraco.Core return BoolConvertCache[type] = false; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Publishing/PublishingStrategy.cs b/src/Umbraco.Core/Publishing/PublishingStrategy.cs index 2c603a40ed..c4e57c5122 100644 --- a/src/Umbraco.Core/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/PublishingStrategy.cs @@ -401,9 +401,10 @@ namespace Umbraco.Core.Publishing content.Name, content.Id)); } - // if newest is published, unpublish - if (content.Published) - content.ChangePublishedState(PublishedState.Unpublished); + // make sure we dirty .Published and always unpublish + // the version we have here could be the newest, !Published + content.ChangePublishedState(PublishedState.Published); + content.ChangePublishedState(PublishedState.Unpublished); _logger.Info( string.Format("Content '{0}' with Id '{1}' has been unpublished.", diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 57bd816576..b46781f76d 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -884,8 +884,8 @@ namespace Umbraco.Core.Services using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var repository = RepositoryFactory.CreateContentRepository(uow); - var query = Query.Builder.Where(x => x.Published && x.ExpireDate <= DateTime.Now); - return repository.GetByQuery(query); + var query = Query.Builder.Where(x => x.ExpireDate <= DateTime.Now); + return repository.GetByQuery(query).Where(x => x.HasPublishedVersion); } } diff --git a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs index 5229ce7dec..57520a3754 100644 --- a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs +++ b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs @@ -55,7 +55,7 @@ namespace Umbraco.Core.Sync if (newApplicationUrl) { appContext._umbracoApplicationDomains.Add(applicationUrl); - LogHelper.Info(typeof(ApplicationUrlHelper), $"New ApplicationUrl detected: {applicationUrl}"); + LogHelper.Info(typeof(ApplicationUrlHelper), string.Format("New ApplicationUrl detected: {0}", applicationUrl)); } } @@ -169,4 +169,4 @@ namespace Umbraco.Core.Sync return url.TrimEnd('/'); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs index 6cb39b7235..4e8476bb6d 100644 --- a/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs @@ -37,7 +37,8 @@ namespace Umbraco.Tests.Benchmarks { // This method is 10% faster var key = new CompositeTypeTypeKey(source, target); - if (AssignableTypeCache.TryGetValue(key, out bool canConvert)) + bool canConvert; + if (AssignableTypeCache.TryGetValue(key, out canConvert)) { return canConvert; } diff --git a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg index 7ee2c48330..d7800af062 100644 Binary files a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg and b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg differ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js index edc3a0a560..6dc066b282 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js @@ -25,6 +25,7 @@ The tour object consist of two parts - The overall tour configuration and a list "group": "My Custom Group" // Used to group tours in the help drawer "groupOrder": 200 // Control the order of tour groups "allowDisable": // Adds a "Don't" show this tour again"-button to the intro step + "requiredSections":["content", "media", "mySection"] // Sections that the tour will access while running, if the user does not have access to the required tour sections, the tour will not load. "steps": [] // tour steps - see next example } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js index a1cb579433..28ac7f6485 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js @@ -212,6 +212,9 @@ throw "Tour " + tour.alias + " is missing tour steps"; } + if (tour.requiredSections.length === 0) { + throw "Tour " + tour.alias + " is missing the required sections"; + } } /** @@ -275,4 +278,4 @@ angular.module("umbraco.services").factory("tourService", tourService); -})(); \ No newline at end of file +})(); diff --git a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js index 57ddb6babd..fe1148d6a8 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js @@ -14,7 +14,6 @@ function SearchController($scope, searchService, $log, $location, navigationServ $scope.isSearching = false; $scope.selectedResult = -1; - $scope.navigateResults = function (ev) { //38: up 40: down, 13: enter @@ -34,24 +33,37 @@ function SearchController($scope, searchService, $log, $location, navigationServ } }; - var group = undefined; + var groupNames = []; var groupIndex = -1; var itemIndex = -1; $scope.selectedItem = undefined; - + $scope.clearSearch = function () { + $scope.searchTerm = null; + }; function iterateResults(up) { //default group if (!group) { - group = $scope.groups[0]; + + for (var g in $scope.groups) { + if ($scope.groups.hasOwnProperty(g)) { + groupNames.push(g); + + } + } + + //Sorting to match the groups order + groupNames.sort(); + + group = $scope.groups[groupNames[0]]; groupIndex = 0; } if (up) { if (itemIndex === 0) { if (groupIndex === 0) { - gotoGroup($scope.groups.length - 1, true); + gotoGroup(Object.keys($scope.groups).length - 1, true); } else { gotoGroup(groupIndex - 1, true); } @@ -62,7 +74,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ if (itemIndex < group.results.length - 1) { gotoItem(itemIndex + 1); } else { - if (groupIndex === $scope.groups.length - 1) { + if (groupIndex === Object.keys($scope.groups).length - 1) { gotoGroup(0); } else { gotoGroup(groupIndex + 1); @@ -73,7 +85,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ function gotoGroup(index, up) { groupIndex = index; - group = $scope.groups[groupIndex]; + group = $scope.groups[groupNames[groupIndex]]; if (up) { gotoItem(group.results.length - 1); @@ -95,6 +107,13 @@ function SearchController($scope, searchService, $log, $location, navigationServ $scope.hasResults = false; if ($scope.searchTerm) { if (newVal !== null && newVal !== undefined && newVal !== oldVal) { + + //Resetting for brand new search + group = undefined; + groupNames = []; + groupIndex = -1; + itemIndex = -1; + $scope.isSearching = true; navigationService.showSearch(); $scope.selectedItem = undefined; @@ -114,7 +133,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ var filtered = {}; _.each(result, function (value, key) { if (value.results.length > 0) { - filtered[key] = value; + filtered[key] = value; } }); $scope.groups = filtered; diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c933a6b115..8cbf553d12 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -332,8 +332,8 @@ umbraco.providers - - ..\packages\Umbraco.ModelsBuilder.3.0.8\lib\Umbraco.ModelsBuilder.dll + + ..\packages\Umbraco.ModelsBuilder.3.0.10\lib\Umbraco.ModelsBuilder.dll @@ -1027,9 +1027,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" 7800 / http://localhost:7800 - 7790 + 7800 / - http://localhost:7790 + http://localhost:7800 False False diff --git a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json index 36350b5d18..f2bf531ab4 100644 --- a/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json +++ b/src/Umbraco.Web.UI/config/BackOfficeTours/getting-started.json @@ -5,6 +5,15 @@ "group": "Getting Started", "groupOrder": 100, "allowDisable": true, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "Welcome to Umbraco - The Friendly CMS", @@ -25,7 +34,6 @@ "content": "Each area in Umbraco is called a Section. Right now you are in the Content section, when you want to go to another section simply click on the appropriate icon in the main menu and you'll be there in no time.", "backdropOpacity": 0.6 }, - { "element": "#tree", "elementPreventClick": true, @@ -88,6 +96,15 @@ "alias": "umbIntroCreateDocType", "group": "Getting Started", "groupOrder": 100, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "Create your first Document Type", @@ -203,6 +220,15 @@ "alias": "umbIntroCreateContent", "group": "Getting Started", "groupOrder": 100, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "Creating your first content node", @@ -253,6 +279,15 @@ "alias": "umbIntroRenderInTemplate", "group": "Getting Started", "groupOrder": 100, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "Render your content in a template", @@ -299,6 +334,15 @@ "alias": "umbIntroViewHomePage", "group": "Getting Started", "groupOrder": 100, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "View your Umbraco site", @@ -339,6 +383,15 @@ "alias": "umbIntroMediaSection", "group": "Getting Started", "groupOrder": 100, + "requiredSections": [ + "content", + "media", + "settings", + "developer", + "users", + "member", + "forms" + ], "steps": [ { "title": "How to use the media library", diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index c72ff9aa8c..8a4b77ca3a 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -36,5 +36,5 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml index 388ffcf6ba..d0b0118870 100644 --- a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml @@ -43,6 +43,7 @@ + Umbraco diff --git a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs index 2d61b254a0..6d3ce6bb57 100644 --- a/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MemberGroupCacheRefresher.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using System.Web.Script.Serialization; +using System.Web; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; @@ -83,36 +83,28 @@ namespace Umbraco.Web.Cache public override void Refresh(string jsonPayload) { - ClearCache(DeserializeFromJsonPayload(jsonPayload)); + ClearCache(); base.Refresh(jsonPayload); } public override void Refresh(int id) { - ClearCache(FromMemberGroup(ApplicationContext.Current.Services.MemberGroupService.GetById(id))); + ClearCache(); base.Refresh(id); } public override void Remove(int id) { - ClearCache(FromMemberGroup(ApplicationContext.Current.Services.MemberGroupService.GetById(id))); + ClearCache(); base.Remove(id); } - private void ClearCache(params JsonPayload[] payloads) + private void ClearCache() { - if (payloads == null) return; - - var memberGroupCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); - payloads.ForEach(payload => - { - if (payload != null && memberGroupCache) - { - memberGroupCache.Result.ClearCacheByKeySearch(string.Format("{0}.{1}", typeof(IMemberGroup).FullName, payload.Name)); - memberGroupCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(payload.Id)); - } - }); - + // Since we cache by group name, it could be problematic when renaming to + // previously existing names - see http://issues.umbraco.org/issue/U4-10846. + // To work around this, just clear all the cache items + ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache(); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index 152879bf5a..da16659cfe 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -7,8 +7,6 @@ using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Web.Models; using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi.Filters; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { @@ -23,7 +21,7 @@ namespace Umbraco.Web.Editors return result; var filters = TourFilterResolver.Current.Filters.ToList(); - + //get all filters that will be applied to all tour aliases var aliasOnlyFilters = filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); @@ -64,8 +62,28 @@ namespace Umbraco.Web.Editors } } } + //Get all allowed sections for the current user + var allowedSections = UmbracoContext.Current.Security.CurrentUser.AllowedSections.ToList(); - return result.OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); + var toursToBeRemoved = new List(); + + //Checking to see if the user has access to the required tour sections, else we remove the tour + foreach (var backOfficeTourFile in result) + { + foreach (var tour in backOfficeTourFile.Tours) + { + foreach (var toursRequiredSection in tour.RequiredSections) + { + if (allowedSections.Contains(toursRequiredSection) == false) + { + toursToBeRemoved.Add(backOfficeTourFile); + break; + } + } + } + } + + return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } private void TryParseTourFile(string tourFile, @@ -79,7 +97,7 @@ namespace Umbraco.Web.Editors //get the filters specific to this file var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList(); - + //If there is any filter applied to match the file only (no tour alias) then ignore the file entirely var isFileFiltered = fileFilters.Any(x => x.TourAlias == null); if (isFileFiltered) return; @@ -117,4 +135,4 @@ namespace Umbraco.Web.Editors } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Models/BackOfficeTour.cs b/src/Umbraco.Web/Models/BackOfficeTour.cs index a973a92429..78a4cd1897 100644 --- a/src/Umbraco.Web/Models/BackOfficeTour.cs +++ b/src/Umbraco.Web/Models/BackOfficeTour.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Runtime.Serialization; namespace Umbraco.Web.Models { @@ -18,6 +19,8 @@ namespace Umbraco.Web.Models public int GroupOrder { get; set; } [DataMember(Name = "allowDisable")] public bool AllowDisable { get; set; } + [DataMember(Name = "requiredSections")] + public List RequiredSections { get; set; } [DataMember(Name = "steps")] public BackOfficeTourStep[] Steps { get; set; } } diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs index 6c018f105c..9f235e00d5 100644 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs @@ -47,7 +47,6 @@ using System.Security; [assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")] [assembly: InternalsVisibleTo("Umbraco.Forms.Web")] -[assembly: InternalsVisibleTo("Umbraco.Headless")] //allow custom unit-testing code to access internals through custom adapters [assembly: InternalsVisibleTo("Umbraco.VisualStudio")] // backwards compat. diff --git a/src/Umbraco.Web/Security/OwinExtensions.cs b/src/Umbraco.Web/Security/OwinExtensions.cs new file mode 100644 index 0000000000..8217dc7f6c --- /dev/null +++ b/src/Umbraco.Web/Security/OwinExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Web; +using Microsoft.AspNet.Identity.Owin; +using Microsoft.Owin; +using Umbraco.Core; +using Umbraco.Core.Models.Identity; +using Umbraco.Core.Security; +using Umbraco.Web.Security.Identity; + +namespace Umbraco.Web.Security +{ + internal static class OwinExtensions + { + + /// + /// Nasty little hack to get httpcontextbase from an owin context + /// + /// + /// + internal static Attempt TryGetHttpContext(this IOwinContext owinContext) + { + var ctx = owinContext.Get(typeof(HttpContextBase).FullName); + return ctx == null ? Attempt.Fail() : Attempt.Succeed(ctx); + } + + } +} \ No newline at end of file diff --git a/src/umbraco.sln b/src/umbraco.sln index a0cf9908ec..6deb764252 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}" EndProject