diff --git a/.github/ISSUE_TEMPLATE/1_Bug.md b/.github/ISSUE_TEMPLATE/1_Bug.md index a1e33e3854..619452f700 100644 --- a/.github/ISSUE_TEMPLATE/1_Bug.md +++ b/.github/ISSUE_TEMPLATE/1_Bug.md @@ -12,7 +12,7 @@ thoroughly. Then, proceed by filling out the rest of the details in the issue template below. The more details you can give us, the easier it will be for us to determine the cause of a problem. -See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md +See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md --> diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 9ed398d52f..841e054aee 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.1.0")] -[assembly: AssemblyInformationalVersion("8.1.0")] +[assembly: AssemblyFileVersion("8.1.1")] +[assembly: AssemblyInformationalVersion("8.1.1")] diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 834eade986..e8fd3414ec 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -74,6 +74,11 @@ namespace Umbraco.Core.Migrations.Upgrade throw new InvalidOperationException($"Version {currentVersion} cannot be migrated to {UmbracoVersion.SemanticVersion}." + $" Please upgrade first to at least {minVersion}."); + // Force versions between 7.14.*-7.15.* into into 7.14 initial state. Because there is no db-changes, + // and we don't want users to workaround my putting in version 7.14.0 them self. + if (minVersion <= currentVersion && currentVersion < new SemVersion(7, 16)) + return GetInitState(minVersion); + // initial state is eg "{init-7.14.0}" return GetInitState(currentVersion); } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs index 064ffc7228..dac62abb75 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs @@ -30,7 +30,20 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 .Where(x => x.EditorAlias == toAlias)); if (oldCount > 0) - throw new InvalidOperationException($"Cannot rename datatype alias \"{fromAlias}\" to \"{toAlias}\" because the target alias is already used."); + { + // If we throw it means that the upgrade will exit and cannot continue. + // This will occur if a v7 site has the old "Obsolete" property editors that are already named with the `toAlias` name. + // TODO: We should have an additional upgrade step when going from 7 -> 8 like we did with 6 -> 7 that shows a compatibility report, + // this would include this check and then we can provide users with information on what they should do (i.e. before upgrading to v8 they will + // need to migrate these old obsolete editors to non-obsolete editors) + + throw new InvalidOperationException( + $"Cannot rename datatype alias \"{fromAlias}\" to \"{toAlias}\" because the target alias is already used." + + $"This is generally because when upgrading from a v7 to v8 site, the v7 site contains Data Types that reference old and already Obsolete " + + $"Property Editors. Before upgrading to v8, any Data Types using property editors that are named with the prefix '(Obsolete)' must be migrated " + + $"to the non-obsolete v7 property editors of the same type."); + } + } Database.Execute(Sql() diff --git a/src/Umbraco.Examine/ExamineExtensions.cs b/src/Umbraco.Examine/ExamineExtensions.cs index 43a3ccc196..1b8033c458 100644 --- a/src/Umbraco.Examine/ExamineExtensions.cs +++ b/src/Umbraco.Examine/ExamineExtensions.cs @@ -77,7 +77,7 @@ namespace Umbraco.Examine /// /// This is not thread safe, use with care /// - internal static void UnlockLuceneIndexes(this IExamineManager examineManager, ILogger logger) + internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing) { foreach (var luceneIndexer in examineManager.Indexes.OfType()) { @@ -86,6 +86,8 @@ namespace Umbraco.Examine //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems. luceneIndexer.WaitForIndexQueueOnShutdown = false; + if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom + //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that //the indexes are not operational unless MainDom is true var dir = luceneIndexer.GetLuceneDirectory(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js index 47e6818466..15e74bbd90 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js @@ -101,14 +101,14 @@ angular.module('umbraco.directives') var eventBindings = []; function oneTimeClick(event) { - // ignore clicks on button groups toggles (i.e. the save and publish button) - var parents = $(event.target).closest("[data-element='button-group-toggle']"); - if (parents.length > 0) { - return; - } + var el = event.target.nodeName; + + //ignore link and button clicks + var els = ["INPUT", "A", "BUTTON"]; + if (els.indexOf(el) >= 0) { return; } // ignore clicks on new overlay - parents = $(event.target).parents(".umb-overlay,.umb-tour"); + var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour"); if (parents.length > 0) { return; } @@ -131,12 +131,6 @@ angular.module('umbraco.directives') return; } - // ignore clicks on dialog actions - var actions = $(event.target).parents(".umb-action"); - if (actions.length === 1) { - return; - } - //ignore clicks inside this element if ($(element).has($(event.target)).length > 0) { return; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js index 2f2df7c12b..07e45ff0f7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js @@ -4,7 +4,7 @@ @restrict A @description -Use this directive make an element sticky and follow the page when scrolling. +Use this directive make an element sticky and follow the page when scrolling. `umb-sticky-bar--active` class is applied when the element is stuck

Markup example

@@ -12,140 +12,102 @@ Use this directive make an element sticky and follow the page when scrolling.
 
         
+ umb-sticky-bar>
-

CSS example

-
-    .my-sticky-bar {
-        padding: 15px 0;
-        background: #000000;
-        position: relative;
-        top: 0;
-    }
-
-    .my-sticky-bar.-umb-sticky-bar {
-        top: 100px;
-    }
-
- -@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element. **/ (function () { 'use strict'; - function StickyBarDirective($rootScope) { + function StickyBarDirective() { + + /** + On initial load, the intersector fires if the grid editor is in the viewport + This flag is used to suppress the setClass behaviour on the initial load + **/ + var initial = true; + + /** + Toggle `umb-sticky-bar--active` class on the sticky-bar element + **/ + function setClass(addClass, current) { + if (!initial) { + current.classList.toggle('umb-sticky-bar--active', addClass); + } else { + initial = false; + } + } + + /** + Inserts two elements in the umbStickyBar parent element + These are used by the IntersectionObserve to calculate scroll position + **/ + function addSentinels(current) { + ['-top', '-bottom'].forEach(s => { + const sentinel = document.createElement('div'); + sentinel.classList.add('umb-sticky-sentinel', s); + current.parentElement.appendChild(sentinel); + }); + } + + /** + Calls into setClass when the footer sentinel enters/exits the bottom of the container + Container is the parent element of the umbStickyBar element + **/ + function observeFooter(current, container) { + const observer = new IntersectionObserver((records, observer) => { + let [target, rootBounds, intersected] = [records[0].boundingClientRect, records[0].rootBounds, records[0].intersectionRatio === 1]; + + if (target.bottom > rootBounds.top && intersected) { + setClass(true, current); + } + if (target.top < rootBounds.top && target.bottom < rootBounds.bottom) { + setClass(false, current); + } + }, { + threshold: [1], + root: container + }); + + observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-bottom')); + } + + /** + Calls into setClass when the header sentinel enters/exits the top of the container + Container is the parent element of the umbStickyBar element + **/ + function observeHeader(current, container) { + const observer = new IntersectionObserver((records, observer) => { + let [target, rootBounds] = [records[0].boundingClientRect, records[0].rootBounds]; + + if (target.bottom < rootBounds.top) { + setClass(true, current); + } + + if (target.bottom >= rootBounds.top && target.bottom < rootBounds.bottom) { + setClass(false, current); + } + }, { + threshold: [0], + root: container + }); + + observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-top')); + } function link(scope, el, attr, ctrl) { - var bar = $(el); - var scrollableContainer = null; - var clonedBar = null; - var cloneIsMade = false; + let current = el[0]; + let container = current.closest('[data-element="editor-container"]'); - function activate() { - - if (bar.parents(".umb-property").length > 1) { - bar.addClass("nested"); - return; - } - - if (attr.scrollableContainer) { - scrollableContainer = bar.closest(attr.scrollableContainer); - } else { - scrollableContainer = $(window); - } - - scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll"); - $(window).on('resize.umbStickyBar', determineVisibility); - - scope.$on('$destroy', function () { - scrollableContainer.off('.umbStickyBar'); - $(window).off('.umbStickyBar'); - }); - - } - - function determineVisibility() { - - var barTop = bar[0].offsetTop; - var scrollTop = scrollableContainer.scrollTop(); - - if (scrollTop > barTop) { - - if (!cloneIsMade) { - - createClone(); - - clonedBar.css({ - 'visibility': 'visible' - }); - - } else { - - calculateSize(); - - } - - } else { - - if (cloneIsMade) { - - //remove cloned element (switched places with original on creation) - bar.remove(); - bar = clonedBar; - clonedBar = null; - - bar.removeClass('-umb-sticky-bar'); - bar.css({ - position: 'relative', - 'width': 'auto', - 'height': 'auto', - 'z-index': 'auto', - 'visibility': 'visible' - }); - - cloneIsMade = false; - - } - - } - - } - - function calculateSize() { - var width = bar.innerWidth(); - clonedBar.css({ - width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS. - }); - } - - function createClone() { - //switch place with cloned element, to keep binding intact - clonedBar = bar; - bar = clonedBar.clone(); - clonedBar.after(bar); - clonedBar.addClass('-umb-sticky-bar'); - clonedBar.css({ - 'position': 'fixed', - // if you change this z-index value, make sure the sticky editor sub headers do not - // clash with umb-dropdown (e.g. the content actions dropdown in content list view) - 'z-index': 99, - 'visibility': 'hidden' - }); - - cloneIsMade = true; - calculateSize(); - - } - - activate(); + addSentinels(current); + observeHeader(current, container); + observeFooter(current, container); } var directive = { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 78cccac57a..6cf3598638 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -32,15 +32,32 @@ border-radius: 3px; } -.umb-editor-sub-header.-umb-sticky-bar { - box-shadow: 0 6px 3px -3px rgba(0,0,0,.16); +[umb-sticky-bar] { transition: box-shadow 240ms; - margin-top: 0; + margin-top: 0; margin-bottom: 0; - top: calc(@appHeaderHeight + @editorHeaderHeight); + position:sticky; + z-index: 99; - .umb-editor--infinityMode & { - top: calc(@editorHeaderHeight); + &.umb-sticky-bar--active { + box-shadow: 0 6px 3px -3px rgba(0,0,0,.16); + } +} + +.umb-sticky-sentinel { + position: absolute; + left: 0; + width: 100%; + pointer-events: none; + + &.-top { + top:0px; + height:1px; + } + + &.-bottom { + bottom:50px; + height:10px; } } 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 9e8dd37ab9..92351d09ca 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -5,6 +5,7 @@ // -------------------------------------------------- .umb-property-editor { width: 100%; + position:relative; } .umb-property-editor-tiny { @@ -165,8 +166,6 @@ .sp-replacer { display: inline-flex; margin-right: 18px; - border: solid 1px @gray-8; - border-radius: 3px; } label { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html index c385223baf..140931ec4b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html @@ -1,6 +1,5 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.controller.js index b622d7a517..00fcbc0cea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.controller.js @@ -49,8 +49,9 @@ //functions vm.searchLogQuery = searchLogQuery; vm.findMessageTemplate = findMessageTemplate; + vm.searchErrors = searchErrors; - function preFlightCheck() { + function preFlightCheck(){ vm.loading = true; //Do our pre-flight check (to see if we can view logs) //IE the log file is NOT too big such as 1GB & crash the site @@ -151,6 +152,11 @@ function getDateRangeLabel(suffix) { return "Log Overview for " + suffix; } + + function searchErrors(){ + var logQuery = "@Level='Fatal' or @Level='Error' or Has(@Exception)"; + searchLogQuery(logQuery); + } preFlightCheck(); diff --git a/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html b/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html index 7648bbf162..9e6936ad47 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html @@ -83,7 +83,7 @@
- + {{ vm.numberOfErrors }} diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js index b2d91e2f66..7276bd9a86 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -139,7 +139,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR //anytime a user is changing a member's password without the oldPassword, we are in effect resetting it so we need to set that flag here var passwordProp = _.find(contentEditingHelper.getAllProps($scope.content), function (e) { return e.alias === '_umb_password' }); - if (!passwordProp.value.reset) { + if (passwordProp && passwordProp.value && !passwordProp.value.reset) { //so if the admin is not explicitly resetting the password, flag it for resetting if a new password is being entered passwordProp.value.reset = !passwordProp.value.oldPassword && passwordProp.config.allowManuallyChangingPassword; } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index b27f6aa335..e58f44e1ae 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -345,9 +345,9 @@ False True - 8100 + 8110 / - http://localhost:8100 + http://localhost:8110 False False diff --git a/src/Umbraco.Web.UI/config/serilog.user.Release.config b/src/Umbraco.Web.UI/config/serilog.user.Release.config index 24e5e4e4be..8f207406e3 100644 --- a/src/Umbraco.Web.UI/config/serilog.user.Release.config +++ b/src/Umbraco.Web.UI/config/serilog.user.Release.config @@ -26,12 +26,13 @@ --> + diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 5f969cdd80..7c040ae508 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -85,7 +85,7 @@ namespace Umbraco.Web.Editors }; } - + /// /// Check if the index has been rebuilt @@ -250,7 +250,7 @@ namespace Umbraco.Web.Editors private void Indexer_IndexOperationComplete(object sender, EventArgs e) { - var indexer = (LuceneIndex)sender; + var indexer = (IIndex)sender; _logger.Debug("Logging operation completed for index {IndexName}", indexer.Name); @@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors _logger .Info($"Rebuilding index '{indexer.Name}' done, {indexer.CommitCount} items committed (can differ from the number of items in the index)"); + >($"Rebuilding index '{indexer.Name}' done."); var cacheKey = "temp_indexing_op_" + indexer.Name; _runtimeCache.Clear(cacheKey); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 3583e5b7f9..ed248a9e24 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -30,8 +30,9 @@ namespace Umbraco.Web.Search private readonly IValueSetBuilder _mediaValueSetBuilder; private readonly IValueSetBuilder _memberValueSetBuilder; private static bool _disableExamineIndexing = false; - private static volatile bool _isConfigured = false; - private static readonly object IsConfiguredLocker = new object(); + private static bool _isConfigured = false; + private static object _configuredInit = null; + private static object _isConfiguredLocker = new object(); private readonly IScopeProvider _scopeProvider; private readonly ServiceContext _services; private static BackgroundTaskRunner _rebuildOnStartupRunner; @@ -91,7 +92,7 @@ namespace Umbraco.Web.Search if (!examineShutdownRegistered) { - _logger.Debug("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); + _logger.Info("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! Suspendable.ExamineEvents.SuspendIndexers(_logger); @@ -120,7 +121,7 @@ namespace Umbraco.Web.Search MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; - EnsureUnlocked(_logger, _examineManager); + ConfigureIndexes(_logger, _examineManager); // TODO: Instead of waiting 5000 ms, we could add an event handler on to fulfilling the first request, then start? RebuildIndexes(_indexRebuilder, _logger, true, 5000); @@ -161,25 +162,24 @@ namespace Umbraco.Web.Search } /// - /// Must be called to each index is unlocked before any indexing occurs + /// Called on startup to configure each index. /// /// /// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before /// either of these happens, we need to configure the indexes. + /// Configuring also ensure the indexes are not locked. /// - private static void EnsureUnlocked(ILogger logger, IExamineManager examineManager) + private static void ConfigureIndexes(ILogger logger, IExamineManager examineManager) { - if (_disableExamineIndexing) return; - if (_isConfigured) return; - - lock (IsConfiguredLocker) - { - //double check - if (_isConfigured) return; - - _isConfigured = true; - examineManager.UnlockLuceneIndexes(logger); - } + LazyInitializer.EnsureInitialized( + ref _configuredInit, + ref _isConfigured, + ref _isConfiguredLocker, + () => + { + examineManager.ConfigureLuceneIndexes(logger, _disableExamineIndexing); + return null; + }); } #region Cache refresher updated event handlers @@ -737,7 +737,7 @@ namespace Umbraco.Web.Search { var strId = id.ToString(CultureInfo.InvariantCulture); foreach (var index in examineComponent._examineManager.Indexes.OfType() - .Where(x => (keepIfUnpublished && !x.PublishedValuesOnly) || !keepIfUnpublished) + .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) .Where(x => x.EnableDefaultEventHandler)) { index.DeleteFromIndex(strId); @@ -800,7 +800,7 @@ namespace Umbraco.Web.Search if (_waitMilliseconds > 0) Thread.Sleep(_waitMilliseconds); - EnsureUnlocked(_logger, _indexRebuilder.ExamineManager); + ConfigureIndexes(_logger, _indexRebuilder.ExamineManager); _indexRebuilder.RebuildIndexes(_onlyEmptyIndexes); } }