From 95b4f9141ef5ee4f335ed7b8cdfb9093a8982235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 1 Feb 2019 14:06:44 +0100 Subject: [PATCH 001/130] test for gathering url for list items so you can Ctrl/CMD+click. --- .../components/umbcontentgrid.directive.js | 7 ++++ .../components/umbtable.directive.js | 7 ++++ .../src/views/components/umb-table.html | 3 +- .../grid/grid.listviewlayout.controller.js | 37 +++++++++++++++++-- .../listview/layouts/list/list.html | 1 + .../list/list.listviewlayout.controller.js | 32 ++++++++++++++-- 6 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js index 3994770c8e..5bacda4ecd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js @@ -115,6 +115,12 @@ Use this directive to generate a list of content items presented as a flexbox gr scope.onClick(item, $event, $index); } }; + + scope.getItemUrl = function(item) { + if(scope.getItemUrl) { + scope.getItemUrl(item); + } + }; scope.clickItemName = function(item, $event, $index) { if(scope.onClickName) { @@ -131,6 +137,7 @@ Use this directive to generate a list of content items presented as a flexbox gr scope: { content: '=', contentProperties: "=", + getItemUrl: "&", onClick: "=", onClickName: "=" }, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js index ae41073c0d..1a297f7cc3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js @@ -123,6 +123,12 @@ } }; + vm.getItemUrl = function (item) { + if (vm.getItemUrl) { + vm.getItemUrl({ item: item}); + } + }; + vm.selectItem = function (item, $index, $event) { if (vm.onSelect) { vm.onSelect({ item: item, $index: $index, $event: $event }); @@ -171,6 +177,7 @@ allowSelectAll: '<', onSelect: '&', onClick: '&', + getItemUrl: '&', onSelectAll: '&', onSelectedAll: '&', onSortingDirection: '&', diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index efd212b517..36c5814986 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -40,7 +40,8 @@
- diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js index 9a28627aa1..3060928d7e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js @@ -9,7 +9,7 @@ (function () { "use strict"; - function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper, mediaTypeHelper) { + function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper, mediaTypeHelper, editorService) { var vm = this; var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; @@ -115,9 +115,38 @@ function selectFolder(folder, $event, $index) { listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event); } - - function goToItem(item, $event, $index) { - $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); + + function getItemUrl(item) { + // if item.id is 2147483647 (int.MaxValue) use item.key + + console.log("request URL for", item) + console.log($scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id)) + + return $scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id); + } + + function goToItem(node, $event, $index) { + + var contentEditor = { + id: node.id, + submit: function (model) { + // update the node + node.name = model.contentNode.name; + // TODO: node.description = model.contentNode.description; + node.published = model.contentNode.hasPublishedVersion; + if (entityType !== "Member") { + entityResource.getUrl(model.contentNode.id, entityType).then(function (data) { + node.url = data; + }); + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.contentEditor(contentEditor); + } activate(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html index 4cfa8c7984..d095adc7e3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html @@ -44,6 +44,7 @@ item-properties="options.includeProperties" on-select="vm.selectItem(item, $index, $event)" on-click="vm.clickItem(item)" + get-item-url="vm.getItemUrl(item)" on-select-all="vm.selectAll($event)" on-selected-all="vm.isSelectedAll()" on-sorting-direction="vm.isSortDirection(col, direction)" diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js index 00e6c6edb4..881ce7bdcd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js @@ -22,6 +22,7 @@ vm.selectItem = selectItem; vm.clickItem = clickItem; + vm.getItemUrl = getItemUrl; vm.selectAll = selectAll; vm.isSelectedAll = isSelectedAll; vm.isSortDirection = isSortDirection; @@ -51,10 +52,35 @@ function selectItem(selectedItem, $index, $event) { listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event); } - - function clickItem(item) { + + function getItemUrl(item) { // if item.id is 2147483647 (int.MaxValue) use item.key - $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id)); + return $scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id); + } + + function clickItem(item) { + + var contentEditor = { + id: node.id, + submit: function (model) { + // update the node + node.name = model.contentNode.name; + // TODO: node.description = model.contentNode.description; + node.published = model.contentNode.hasPublishedVersion; + if (entityType !== "Member") { + entityResource.getUrl(model.contentNode.id, entityType).then(function (data) { + node.url = data; + }); + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.contentEditor(contentEditor); + + } function isSortDirection(col, direction) { From 988be21990ef56c6b452fb4a83b28cfa67a08b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Feb 2019 13:20:01 +0100 Subject: [PATCH 002/130] making the grid work in splitview --- .../src/less/components/umb-grid.less | 37 +++++----- .../propertyeditors/grid/grid.controller.js | 68 +++++++++++-------- .../src/views/propertyeditors/grid/grid.html | 40 +++++------ 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index e7f24d08fa..6bf8cd612a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -128,9 +128,10 @@ position: relative; margin-bottom: 40px; padding-top: 10px; + border: 1px solid @grayLighter; &:hover { - background-color: @grayLighter; + border-color: @grayLight; } &[ng-click], @@ -161,15 +162,16 @@ } .umb-grid .umb-row .umb-cell-placeholder { - min-height: 130px; - background-color: @gray-10; - border-width: 2px; + min-height: 88px; + border-width: 1px; border-style: dashed; - border-color: @gray-8; + border-color: @ui-action-disgrete-border; + color: @ui-action-disgrete-type; transition: border-color 100ms linear; &:hover { - border-color: @blueMid; + border-color: @ui-action-disgrete-border-hover; + color: @ui-action-disgrete-type-hover; cursor: pointer; } } @@ -207,9 +209,9 @@ } .umb-grid .cell-tools-add { - color: @ui-action-type; + color: @ui-action-disgrete-type; &:focus, &:hover { - color: @ui-action-type-hover; + color: @ui-action-disgrete-type-hover; text-decoration: none; } } @@ -219,16 +221,18 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - color: @ui-action-type; + color: @ui-action-disgrete-type; } .umb-grid .cell-tools-add.-bar { display: block; - background: @gray-10; text-align: center; padding: 5px; - border: 1px dashed @gray-7; + border: 1px dashed @ui-action-disgrete-border; margin: 10px; + &:focus, &:hover { + border-color: @ui-action-disgrete-border-hover; + } } @@ -249,7 +253,6 @@ .umb-grid .cell-tools-edit { display: inline-block; - color: @white; } .umb-grid .drop-overlay { @@ -412,12 +415,12 @@ // Row states .umb-grid .umb-row.-active { - background-color: @ui-action-type; + border-color: @ui-action-type; .umb-row-title-bar { cursor: move; } - + /* .row-tool, .umb-row-title { color: @white; @@ -436,12 +439,13 @@ .umb-cell .umb-cell-content { border-color: transparent; } + */ } .umb-grid .umb-row.-active-child { background-color: @gray-10; - + .umb-row-title-bar { cursor: inherit; } @@ -449,7 +453,7 @@ .umb-row-title { color: @gray-3; } - + /* .row-tool { color: fade(@black, 23); } @@ -465,6 +469,7 @@ border-color: fade(@gray, 44); } } + */ } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index b40305f5f5..0ea5842612 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -14,11 +14,16 @@ angular.module("umbraco") // Grid status variables var placeHolder = ""; var currentForm = angularHelper.getCurrentForm($scope); - + + $scope.currentRowWithActiveChild = null; + $scope.currentCellWithActiveChild = null; + $scope.active = null; + $scope.currentRow = null; $scope.currentCell = null; $scope.currentToolsControl = null; $scope.currentControl = null; + $scope.openRTEToolbarId = null; $scope.hasSettings = false; $scope.showRowConfigurations = true; @@ -235,10 +240,16 @@ angular.module("umbraco") }, 500, false); $scope.$apply(function () { - + + console.log("$apply function called...") + var cell = $(e.target).scope().area; - cell.hasActiveChild = hasActiveChild(cell, cell.controls); - cell.active = false; + + if(hasActiveChild(cell, cell.controls)) { + $scope.currentCellWithActiveChild = cell; + } + $scope.active = cell; + }); } @@ -307,12 +318,13 @@ angular.module("umbraco") // Row management function // ********************************************* - $scope.clickRow = function(index, rows) { - rows[index].active = true; - }; - - $scope.clickOutsideRow = function(index, rows) { - rows[index].active = false; + $scope.clickRow = function(index, rows, $event) { + console.log("clickRow") + //rows[index].active = true; + $scope.currentRowWithActiveChild = null; + $scope.active = rows[index]; + + $event.stopPropagation(); }; function getAllowedLayouts(section) { @@ -359,6 +371,7 @@ angular.module("umbraco") if (section.rows.length > 0) { section.rows.splice($index, 1); $scope.currentRow = null; + $scope.currentRowWithActiveChild = null; $scope.openRTEToolbarId = null; currentForm.$setDirty(); } @@ -513,14 +526,13 @@ angular.module("umbraco") // Area management functions // ********************************************* - $scope.clickCell = function(index, cells, row) { - cells[index].active = true; - row.hasActiveChild = true; - }; - - $scope.clickOutsideCell = function(index, cells, row) { - cells[index].active = false; - row.hasActiveChild = hasActiveChild(row, cells); + $scope.clickCell = function(index, cells, row, $event) { + + $scope.currentCellWithActiveChild = null; + + $scope.active = cells[index]; + $scope.currentRowWithActiveChild = row; + $event.stopPropagation(); }; $scope.cellPreview = function (cell) { @@ -536,14 +548,14 @@ angular.module("umbraco") // ********************************************* // Control management functions // ********************************************* - $scope.clickControl = function (index, controls, cell) { - controls[index].active = true; - cell.hasActiveChild = true; - }; - - $scope.clickOutsideControl = function (index, controls, cell) { - controls[index].active = false; - cell.hasActiveChild = hasActiveChild(cell, controls); + $scope.clickControl = function (index, controls, cell, $event) { + + console.log("clickControl"); + + $scope.active = controls[index]; + $scope.currentCellWithActiveChild = cell; + + $event.stopPropagation(); }; function hasActiveChild(item, children) { @@ -594,8 +606,8 @@ angular.module("umbraco") if (index === undefined) { index = cell.controls.length; } - - newControl.active = true; + + $scope.active = newControl; //populate control $scope.initControl(newControl, index + 1); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index 22e72daff9..eeb70718f9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -69,13 +69,13 @@
@@ -89,7 +89,7 @@
-
+
@@ -123,17 +123,17 @@ ng-style="{width: area.$percentage + '%'}" ng-class="{ '-has-config': area.hasConfig, - '-active': area.active, - '-active-child': area.hasActiveChild}" + '-active': area === active, + '-active-child': area === currentCellWithActiveChild}" ng-model="area.controls" - ng-click="clickCell($index, row.areas, row)" - on-outside-click="clickOutsideCell($index, row.areas, row)" - bind-click-on="{{area.active}}"> + ng-click="clickCell($index, row.areas, row, $event)" + + bind-click-on="{{area === active}}">
@@ -153,7 +153,7 @@
-
+
@@ -172,14 +172,14 @@
-
+
{{control.editor.name}} @@ -189,11 +189,11 @@
-
+
{{control.editor.name}}
-
+
From 60fa931f3f6c64a97f5350421461f14b99bca507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Feb 2019 14:38:25 +0100 Subject: [PATCH 003/130] Grid-RTE got a unique ID, which fixes TineMCE code, which is dependent on a unique ID. #4011 --- .../components/grid/grid.rte.directive.js | 14 ++++++-------- .../src/views/components/grid/grid-rte.html | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index 6472dd3d38..dbbb0e98a2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -13,6 +13,10 @@ angular.module("umbraco.directives") // TODO: A lot of the code below should be shared between the grid rte and the normal rte var promises = []; + + var d = new Date(); + var n = d.getTime(); + scope.textAreaHtmlId = scope.uniqueId + "_" + n + "_rte"; //queue file loading if (typeof (tinymce) === "undefined") { @@ -28,7 +32,7 @@ angular.module("umbraco.directives") var tinyMceEditor = null; promises.push(tinyMceService.getTinyMceEditorConfig({ - htmlId: scope.uniqueId, + htmlId: scope.textAreaHtmlId, stylesheets: scope.configuration ? scope.configuration.stylesheets : null, toolbar: toolbar, mode: scope.configuration.mode @@ -132,17 +136,11 @@ angular.module("umbraco.directives") } }); - - //listen for formSubmitting event (the result is callback used to remove the event subscription) - var formSubmittingListener = scope.$on("formSubmitting", function () { - scope.value = tinyMceEditor ? tinyMceEditor.getContent() : null; - }); - + //when the element is disposed we need to unsubscribe! // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom // element might still be there even after the modal has been hidden. scope.$on('$destroy', function () { - formSubmittingListener(); eventsService.unsubscribe(tabShownListener); //ensure we unbind this in case the blur doesn't fire above $('.umb-panel-body').off('scroll', pinToolbar); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html b/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html index f3b854a062..afa9869676 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/grid/grid-rte.html @@ -1,3 +1,3 @@ 
-
+
From 13a326b4673a89fa5694ed81de0ff06470110ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 18 Feb 2019 10:58:59 +0100 Subject: [PATCH 004/130] remove logs --- .../src/views/propertyeditors/grid/grid.controller.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 0ea5842612..650717b7c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -240,9 +240,6 @@ angular.module("umbraco") }, 500, false); $scope.$apply(function () { - - console.log("$apply function called...") - var cell = $(e.target).scope().area; if(hasActiveChild(cell, cell.controls)) { @@ -319,7 +316,6 @@ angular.module("umbraco") // ********************************************* $scope.clickRow = function(index, rows, $event) { - console.log("clickRow") //rows[index].active = true; $scope.currentRowWithActiveChild = null; $scope.active = rows[index]; @@ -550,8 +546,6 @@ angular.module("umbraco") // ********************************************* $scope.clickControl = function (index, controls, cell, $event) { - console.log("clickControl"); - $scope.active = controls[index]; $scope.currentCellWithActiveChild = cell; From 2e4091145066435bf8eba2d9e7bce5d1399fbe36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 18 Feb 2019 10:59:30 +0100 Subject: [PATCH 005/130] get uniq ID by using String.CreateGuid() --- .../directives/components/grid/grid.rte.directive.js | 9 +++++---- .../src/views/propertyeditors/rte/rte.controller.js | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index dbbb0e98a2..0f933a4ccc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -14,10 +14,11 @@ angular.module("umbraco.directives") var promises = []; - var d = new Date(); - var n = d.getTime(); - scope.textAreaHtmlId = scope.uniqueId + "_" + n + "_rte"; - + //To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias + // because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because + // we have this mini content editor panel that can be launched with MNTP. + scope.textAreaHtmlId = scope.uniqueId + "_" + String.CreateGuid(); + //queue file loading if (typeof (tinymce) === "undefined") { promises.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope)); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 6099c5dad4..43da0cb66f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -9,9 +9,7 @@ angular.module("umbraco") //To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias // because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because // we have this mini content editor panel that can be launched with MNTP. - var d = new Date(); - var n = d.getTime(); - $scope.textAreaHtmlId = $scope.model.alias + "_" + n + "_rte"; + $scope.textAreaHtmlId = $scope.model.alias + "_" + String.CreateGuid(); var editorConfig = $scope.model.config ? $scope.model.config.editor : null; if (!editorConfig || angular.isString(editorConfig)) { From 44b1a738bf5a4d9ca04b105399d61990ac469bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 18 Feb 2019 10:59:40 +0100 Subject: [PATCH 006/130] clean up css for grid --- .../src/less/components/umb-grid.less | 38 +------------------ 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 6bf8cd612a..5283e259c7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -420,26 +420,6 @@ .umb-row-title-bar { cursor: move; } - /* - .row-tool, - .umb-row-title { - color: @white; - } - - .umb-grid-has-config { - color: fade(@white, 66); - } - - .umb-cell { - .umb-grid-has-config { - color: fade(@black, 44); - } - } - - .umb-cell .umb-cell-content { - border-color: transparent; - } - */ } @@ -453,23 +433,7 @@ .umb-row-title { color: @gray-3; } - /* - .row-tool { - color: fade(@black, 23); - } - - .umb-grid-has-config { - color: fade(@black, 44); - } - - .umb-cell-content.-placeholder { - border-color: @gray-8; - - &:hover { - border-color: fade(@gray, 44); - } - } - */ + } From 3f13baa8e773d9435ae9eab239c7d883881bd193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 18 Feb 2019 11:00:47 +0100 Subject: [PATCH 007/130] make Embed and Media work for Grid in SplitView when grid is not allowed to vary. --- .../grid/editors/embed.controller.js | 21 +++++------------- .../propertyeditors/grid/editors/embed.html | 4 ++-- .../grid/editors/media.controller.js | 22 ++++++------------- .../propertyeditors/grid/editors/media.html | 4 ++-- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js index beb8edab20..17916299bc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js @@ -2,24 +2,16 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.EmbedController", function ($scope, $timeout, $sce, editorService) { - function onInit() { - $scope.trustedValue = null; - $scope.trustedValue = $sce.trustAsHtml($scope.control.value); - - if(!$scope.control.value) { - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setEmbed(); - } - }, 200); - } + $scope.hasEmbed = function(){ + return $scope.control.value !== null; + } + $scope.getEmbed = function(){ + return $sce.trustAsHtml($scope.control.value); } - $scope.setEmbed = function(){ var embed = { submit: function(model) { $scope.control.value = model.embed.preview; - $scope.trustedValue = $sce.trustAsHtml(model.embed.preview); editorService.close(); }, close: function() { @@ -28,6 +20,5 @@ angular.module("umbraco") }; editorService.embed(embed); }; - - onInit(); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html index 87109e1eb9..32ebfd3257 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html @@ -1,10 +1,10 @@
-
+
Click to embed
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index e267133cf4..c290b8ae7d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -8,13 +8,7 @@ angular.module("umbraco") $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1; }); } - - function onInit() { - if($scope.control.value){ - $scope.setUrl(); - } - } - + $scope.setImage = function(){ var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined; @@ -34,10 +28,8 @@ angular.module("umbraco") id: selectedImage.id, udi: selectedImage.udi, image: selectedImage.image, - altText: selectedImage.altText - }; - - $scope.setUrl(); + caption: selectedImage.altText + }; editorService.close(); }, @@ -49,7 +41,7 @@ angular.module("umbraco") editorService.mediaPicker(mediaPicker); }; - $scope.setUrl = function(){ + $scope.getThumbnailUrl = function(){ if($scope.control.value.image){ var url = $scope.control.value.image; @@ -70,10 +62,10 @@ angular.module("umbraco") { url += "?width=800&upscale=false&animationprocessmode=false" } - $scope.url = url; + return url; } + + return ""; }; - onInit(); - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html index 7ffb26d831..1a91fe52b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html @@ -7,9 +7,9 @@
From 6f673184324a90f10109a18e87e60e2b65f76ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 19 Feb 2019 10:18:01 +0100 Subject: [PATCH 008/130] corrected color variables --- .../src/less/components/umb-grid.less | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 3aab0340a8..5a14294ebf 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -542,14 +542,13 @@ display: inline-block; cursor: pointer; border-radius: 200px; - background: @gray-10; - border: 1px solid @gray-7; + border: 1px solid @ui-action-disgrete-border; margin: 2px; &:hover, &:hover * { - background: @ui-action-type-hover !important; + background: @ui-action-disgrete-type-hover !important; color: @white !important; - border-color: @ui-action-type-hover !important; + border-color: @ui-action-disgrete-border-hover !important; text-decoration: none; } } From 592d8db3e72194eeafa7a52229287ce538e7974c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 19 Feb 2019 10:18:13 +0100 Subject: [PATCH 009/130] removed comment --- .../src/views/propertyeditors/grid/grid.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 650717b7c0..ff01c8cc71 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -316,7 +316,7 @@ angular.module("umbraco") // ********************************************* $scope.clickRow = function(index, rows, $event) { - //rows[index].active = true; + $scope.currentRowWithActiveChild = null; $scope.active = rows[index]; From 564117e62edd23f5e786c9619f02a9f72ad60455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 19 Feb 2019 10:19:02 +0100 Subject: [PATCH 010/130] Grid editor to use $watch for values that needs to transform for presentation --- .../grid/editors/embed.controller.js | 24 ++++++++++++++----- .../propertyeditors/grid/editors/embed.html | 2 +- .../grid/editors/media.controller.js | 22 +++++++++++++---- .../propertyeditors/grid/editors/media.html | 5 ++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js index 17916299bc..8e9ea06a84 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.controller.js @@ -1,14 +1,26 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.EmbedController", function ($scope, $timeout, $sce, editorService) { - - $scope.hasEmbed = function(){ - return $scope.control.value !== null; - } - $scope.getEmbed = function(){ + + + + function getEmbed() { return $sce.trustAsHtml($scope.control.value); } - $scope.setEmbed = function(){ + + + $scope.embedHtml = getEmbed(); + $scope.$watch('control.value', function(newValue, oldValue) { + if(angular.equals(newValue, oldValue)){ + return; // simply skip that + } + + $scope.embedHtml = getEmbed(); + }, false); + $scope.hasEmbed = function() { + return $scope.control.value !== null; + } + $scope.setEmbed = function() { var embed = { submit: function(model) { $scope.control.value = model.embed.preview; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html index 32ebfd3257..0178c3b3ec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/embed.html @@ -5,6 +5,6 @@
Click to embed
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index c290b8ae7d..eb1032a9c7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -1,7 +1,11 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MediaController", function ($scope, $timeout, userService, editorService) { - + + + $scope.thumbnailUrl = getThumbnailUrl(); + + if (!$scope.model.config.startNodeId) { userService.getCurrentUser().then(function (userData) { $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; @@ -40,10 +44,18 @@ angular.module("umbraco") editorService.mediaPicker(mediaPicker); }; + + $scope.$watch('control.value', function(newValue, oldValue) { + if(angular.equals(newValue, oldValue)){ + return; // simply skip that + } + + $scope.thumbnailUrl = getThumbnailUrl(); + }, true); + + function getThumbnailUrl() { - $scope.getThumbnailUrl = function(){ - - if($scope.control.value.image){ + if($scope.control.value && $scope.control.value.image) { var url = $scope.control.value.image; if($scope.control.editor.config && $scope.control.editor.config.size){ @@ -65,7 +77,7 @@ angular.module("umbraco") return url; } - return ""; + return null; }; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html index 1a91fe52b8..184f707ebf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.html @@ -5,11 +5,10 @@
Click to insert image
-
+
From e6951450199707ed40c2036ed6345b660a4ca043 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sat, 2 Mar 2019 12:06:04 +0100 Subject: [PATCH 011/130] Grid: Hide empty sticky bar --- .../src/views/propertyeditors/grid/grid.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index 22e72daff9..792b22ca9d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -1,10 +1,9 @@
- + Date: Tue, 5 Mar 2019 13:02:59 +0100 Subject: [PATCH 012/130] Implement IPublishedContent Siblings --- src/Umbraco.Web/PublishedContentExtensions.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index f880076c50..dbd5008b74 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Web.Composing; +using Umbraco.Web.PublishedCache; namespace Umbraco.Web { @@ -21,6 +22,7 @@ namespace Umbraco.Web // see notes in PublishedElementExtensions // private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + private static IPublishedSnapshot PublishedSnapshot => Current.PublishedSnapshot; #region Urls @@ -235,7 +237,8 @@ namespace Umbraco.Web /// /// The content items. /// The specific culture to filter for. If null is used the current culture is used. (Default is null). - internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, string culture = null) + internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, string culture = null) + where T : class, IPublishedContent { if (contents == null) throw new ArgumentNullException(nameof(contents)); @@ -1117,6 +1120,52 @@ namespace Umbraco.Web #endregion + #region Axes: siblings + + /// + /// Gets the siblings of the content. + /// + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content. + public static IEnumerable Siblings(this IPublishedContent content, string culture = null) + { + return content.Parent != null + ? content.Parent.Children(culture) + : PublishedSnapshot.Content.GetAtRoot().WhereIsInvariantOrHasCulture(culture); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The content type alias. + /// The siblings of the content, of the given content type. + public static IEnumerable SiblingsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) + { + return content.Parent != null + ? content.Parent.ChildrenOfType(contentTypeAlias, culture) + : PublishedSnapshot.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(culture); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content type. + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content, of the given content type. + public static IEnumerable Siblings(this IPublishedContent content, string culture = null) + where T : class, IPublishedContent + { + return content.Parent != null + ? content.Parent.Children(culture) + : PublishedSnapshot.Content.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(culture); + } + + #endregion + #region Axes: custom /// From 7b815cfcb3ca7e454eb066ed6661d47d1916fd98 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 5 Mar 2019 21:34:49 +0100 Subject: [PATCH 013/130] Add methods for fetching multiple members to UmbracoHelper --- src/Umbraco.Web/UmbracoHelper.cs | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 12d4ce1d76..5d39f1c8dd 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -288,6 +288,56 @@ namespace Umbraco.Web var asInt = id.TryConvertTo(); return asInt ? MembershipHelper.GetById(asInt.Result) : MembershipHelper.GetByProviderKey(id); } + + public IEnumerable Member(IEnumerable ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(IEnumerable ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(IEnumerable ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(IEnumerable ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(IEnumerable ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(params int[] ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(params string[] ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(params Guid[] ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(params Udi[] ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } + + public IEnumerable Member(params object[] ids) + { + return ids.Select(id => Member(id)).WhereNotNull(); + } #endregion From 36827cfa9f608659c2c7178b2d8fa8efb3437221 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 8 Mar 2019 07:31:39 +0100 Subject: [PATCH 014/130] Move some of the multiget member methods to MembershipHelper --- src/Umbraco.Web/Security/MembershipHelper.cs | 35 ++++++++++++++++++++ src/Umbraco.Web/UmbracoHelper.cs | 24 +++++++------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 3bb6951d9a..c2d80257e1 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -289,11 +289,46 @@ namespace Umbraco.Web.Security return MemberCache.GetByProviderKey(key); } + public virtual IEnumerable GetByProviderKey(IEnumerable keys) + { + return keys?.Select(GetByProviderKey).WhereNotNull() ?? new IPublishedContent[0]; + } + + public virtual IEnumerable GetByProviderKey(params object[] keys) + { + return keys?.Select(GetByProviderKey).WhereNotNull() ?? new IPublishedContent[0]; + } + public virtual IPublishedContent GetById(int memberId) { return MemberCache.GetById(memberId); } + public virtual IEnumerable GetById(IEnumerable memberIds) + { + return memberIds?.Select(GetById).WhereNotNull() ?? new IPublishedContent[0]; + } + + public virtual IEnumerable GetById(params int[] memberIds) + { + return memberIds?.Select(GetById).WhereNotNull() ?? new IPublishedContent[0]; + } + + public virtual IPublishedContent GetById(Guid memberId) + { + return GetByProviderKey(memberId); + } + + public virtual IEnumerable GetById(IEnumerable memberIds) + { + return GetByProviderKey(memberIds.OfType()); + } + + public virtual IEnumerable GetById(params Guid[] memberIds) + { + return GetByProviderKey(memberIds.OfType()); + } + public virtual IPublishedContent GetByUsername(string username) { return MemberCache.GetByUsername(username); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 5d39f1c8dd..e9fdfd73e5 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -264,7 +264,7 @@ namespace Umbraco.Web public IPublishedContent Member(Guid id) { - return MembershipHelper.GetByProviderKey(id); + return MembershipHelper.GetById(id); } public IPublishedContent Member(object id) @@ -288,55 +288,55 @@ namespace Umbraco.Web var asInt = id.TryConvertTo(); return asInt ? MembershipHelper.GetById(asInt.Result) : MembershipHelper.GetByProviderKey(id); } - + public IEnumerable Member(IEnumerable ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return MembershipHelper.GetById(ids); } public IEnumerable Member(IEnumerable ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(IEnumerable ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return MembershipHelper.GetById(ids); } public IEnumerable Member(IEnumerable ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(IEnumerable ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(params int[] ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(params string[] ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(params Guid[] ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return MembershipHelper.GetById(ids); } public IEnumerable Member(params Udi[] ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } public IEnumerable Member(params object[] ids) { - return ids.Select(id => Member(id)).WhereNotNull(); + return ids.Select(Member).WhereNotNull(); } #endregion From 1098db1632e6dfc2d1fa79ad1e62478680630ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2019 14:52:16 +0100 Subject: [PATCH 015/130] use item.editPath for new tab URL --- .../components/umbcontentgrid.directive.js | 11 +++-------- .../directives/components/umbtable.directive.js | 12 +++--------- .../src/less/components/umb-content-grid.less | 4 ++-- .../src/views/components/umb-content-grid.html | 7 +++++-- .../src/views/components/umb-table.html | 2 +- .../layouts/grid/grid.listviewlayout.controller.js | 9 --------- .../propertyeditors/listview/layouts/list/list.html | 1 - .../layouts/list/list.listviewlayout.controller.js | 4 ++-- 8 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js index 5bacda4ecd..f226d924af 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcontentgrid.directive.js @@ -115,17 +115,13 @@ Use this directive to generate a list of content items presented as a flexbox gr scope.onClick(item, $event, $index); } }; - - scope.getItemUrl = function(item) { - if(scope.getItemUrl) { - scope.getItemUrl(item); - } - }; scope.clickItemName = function(item, $event, $index) { - if(scope.onClickName) { + if(scope.onClickName && !($event.metaKey || $event.ctrlKey)) { scope.onClickName(item, $event, $index); + $event.preventDefault(); } + $event.stopPropagation(); }; } @@ -137,7 +133,6 @@ Use this directive to generate a list of content items presented as a flexbox gr scope: { content: '=', contentProperties: "=", - getItemUrl: "&", onClick: "=", onClickName: "=" }, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js index 1a297f7cc3..3f1929e97d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbtable.directive.js @@ -117,16 +117,11 @@ var vm = this; vm.clickItem = function (item, $event) { - if (vm.onClick) { + if (vm.onClick && !($event.metaKey || $event.ctrlKey)) { vm.onClick({ item: item}); - $event.stopPropagation(); - } - }; - - vm.getItemUrl = function (item) { - if (vm.getItemUrl) { - vm.getItemUrl({ item: item}); + $event.preventDefault(); } + $event.stopPropagation(); }; vm.selectItem = function (item, $index, $event) { @@ -177,7 +172,6 @@ allowSelectAll: '<', onSelect: '&', onClick: '&', - getItemUrl: '&', onSelectAll: '&', onSelectedAll: '&', onSortingDirection: '&', diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index d2fa2be0c7..2a4a3be2e4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -13,7 +13,6 @@ user-select: none; box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); border-radius: 3px; - } .umb-content-grid__item.-selected { @@ -59,7 +58,8 @@ display: inline-flex; color: @ui-option-type; - &:hover { + &:hover, &:focus { + text-decoration: none; color:@ui-option-type-hover; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html index 487be4af87..ce85537d7c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html @@ -10,10 +10,13 @@
- +
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index 36c5814986..65955f31eb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -41,7 +41,7 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js index 3060928d7e..70dd2926b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js @@ -116,15 +116,6 @@ listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event); } - function getItemUrl(item) { - // if item.id is 2147483647 (int.MaxValue) use item.key - - console.log("request URL for", item) - console.log($scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id)) - - return $scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id); - } - function goToItem(node, $event, $index) { var contentEditor = { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html index d095adc7e3..4cfa8c7984 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.html @@ -44,7 +44,6 @@ item-properties="options.includeProperties" on-select="vm.selectItem(item, $index, $event)" on-click="vm.clickItem(item)" - get-item-url="vm.getItemUrl(item)" on-select-all="vm.selectAll($event)" on-selected-all="vm.isSelectedAll()" on-sorting-direction="vm.isSortDirection(col, direction)" diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js index b796da8d0f..f26f077e66 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js @@ -19,7 +19,7 @@ vm.activeDrag = false; vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; vm.acceptedMediatypes = []; - + vm.selectItem = selectItem; vm.clickItem = clickItem; vm.selectAll = selectAll; @@ -55,7 +55,7 @@ function clickItem(item) { // if item.id is 2147483647 (int.MaxValue) use item.key $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id)); - } + } function isSortDirection(col, direction) { return listViewHelper.setSortingDirection(col, direction, $scope.options); From 48ec795e7b9450f036006a2ba3285565554aeb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2019 14:54:11 +0100 Subject: [PATCH 016/130] do not open as infinite editing yet. --- .../grid/grid.listviewlayout.controller.js | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js index 70dd2926b6..9a2ff4975d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.listviewlayout.controller.js @@ -116,28 +116,8 @@ listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event); } - function goToItem(node, $event, $index) { - - var contentEditor = { - id: node.id, - submit: function (model) { - // update the node - node.name = model.contentNode.name; - // TODO: node.description = model.contentNode.description; - node.published = model.contentNode.hasPublishedVersion; - if (entityType !== "Member") { - entityResource.getUrl(model.contentNode.id, entityType).then(function (data) { - node.url = data; - }); - } - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.contentEditor(contentEditor); - + function goToItem(item, $event, $index) { + $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); } activate(); From 03a5d5b866d8341bb7d618808d0d583476a52173 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 12 Mar 2019 08:20:43 +0100 Subject: [PATCH 017/130] Allow mixing languages with and without region --- src/Umbraco.Web/Editors/LanguageController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 64132d3d8e..e1c0c61303 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -103,6 +103,13 @@ namespace Umbraco.Web.Editors // this is prone to race conditions but the service will not let us proceed anyways var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode); + // the localization service might return the generic language even when queried for specific ones (e.g. "da" when queried for "da-DK") + // - we need to handle that explicitly + if (existing.IsoCode != language.IsoCode) + { + existing = null; + } + if (existing != null && language.Id != existing.Id) { //someone is trying to create a language that already exist From e550e5cd8159ac7f08d8fb3c7d35b0d82c2c56a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2019 10:35:04 +0100 Subject: [PATCH 018/130] rename LESS variables with disgrete to discreet --- .../src/less/components/editor.less | 8 ++++---- .../src/less/components/umb-file-dropzone.less | 4 ++-- .../src/less/components/umb-media-grid.less | 2 +- .../src/less/components/umb-node-preview.less | 8 ++++---- .../src/less/components/umb-packages.less | 4 ++-- src/Umbraco.Web.UI.Client/src/less/listview.less | 8 ++++---- src/Umbraco.Web.UI.Client/src/less/panel.less | 4 ++-- .../src/less/property-editors.less | 4 ++-- src/Umbraco.Web.UI.Client/src/less/variables.less | 12 ++++++------ 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index 0770a895c0..b8ff6a6554 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -167,7 +167,7 @@ a.umb-editor-header__close-split-view:hover { text-decoration: none !important; font-size: 13px; //color: @gray-4; - color: @ui-action-disgrete-type; + color: @ui-action-discreet-type; //background-color: @white; } @@ -175,15 +175,15 @@ a.umb-variant-switcher__toggle { transition: color 0.2s ease-in-out; &:hover { //background-color: @gray-10; - color: @ui-action-disgrete-type-hover; + color: @ui-action-discreet-type-hover; .umb-variant-switcher__expand { - color: @ui-action-disgrete-type-hover; + color: @ui-action-discreet-type-hover; } } } .umb-variant-switcher__expand { - color: @ui-action-disgrete-type; + color: @ui-action-discreet-type; margin-top: 3px; margin-left: 5px; margin-right: -5px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less index b5021d7c38..5071091fcc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less @@ -41,13 +41,13 @@ // file select link .file-select { font-size: 15px; - color: @ui-action-disgrete-type; + color: @ui-action-discreet-type; cursor: pointer; margin-top: 10px; &:hover { - color: @ui-action-disgrete-type-hover; + color: @ui-action-discreet-type-hover; text-decoration: none; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 7a6cd043a4..41e8a2892c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -211,7 +211,7 @@ transition: opacity 150ms; &:hover { - color: @ui-action-disgrete-type-hover; + color: @ui-action-discreet-type-hover; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index b054ddf907..d52cb918f6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -89,16 +89,16 @@ display: flex; align-items: center; justify-content: center; - border: 1px dashed @ui-action-disgrete-border; - color: @ui-action-disgrete-type; + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; font-weight: bold; padding: 5px 15px; box-sizing: border-box; } .umb-node-preview-add:hover { - color: @ui-action-disgrete-type-hover; - border-color: @ui-action-disgrete-border-hover; + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; text-decoration: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less index daee29351c..f704dd48e2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less @@ -408,7 +408,7 @@ a.umb-package-details__back-link { .umb-gallery__thumbnail { flex: 0 1 100px; - border: 1px solid @ui-action-disgrete-border; + border: 1px solid @ui-action-discreet-border; border-radius: 3px; margin: 5px; padding: 10px; @@ -418,7 +418,7 @@ a.umb-package-details__back-link { .umb-gallery__thumbnail:hover { cursor: pointer; - border-color: @ui-action-disgrete-border-hover; + border-color: @ui-action-discreet-border-hover; } /* PACKAGE LIST */ diff --git a/src/Umbraco.Web.UI.Client/src/less/listview.less b/src/Umbraco.Web.UI.Client/src/less/listview.less index 8b5a295752..b3de3e575d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/listview.less +++ b/src/Umbraco.Web.UI.Client/src/less/listview.less @@ -239,8 +239,8 @@ .list-view-add-layout { margin-top: 10px; - color: @ui-action-disgrete-type; - border: 1px dashed @ui-action-disgrete-border; + color: @ui-action-discreet-type; + border: 1px dashed @ui-action-discreet-border; display: flex; align-items: center; justify-content: center; @@ -250,6 +250,6 @@ .list-view-add-layout:hover { text-decoration: none; - color: @ui-action-disgrete-type-hover; - border-color: @ui-action-disgrete-border-hover; + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; } diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index a8c1efe1d3..bad0ab9715 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -346,7 +346,7 @@ justify-content: center; align-items: center; background: @white; - border: 1px solid @ui-action-disgrete-border; + border: 1px solid @ui-action-discreet-border; animation: fadeIn 0.5s; border-radius: 3px; width: 50px; @@ -354,7 +354,7 @@ .icon { opacity: 0.8; } - border-color: @ui-action-disgrete-border-hover; + border-color: @ui-action-discreet-border-hover; } } 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 e021a577a5..279163d64b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -269,8 +269,8 @@ transition: all 150ms ease-in-out; &:hover { - color: @ui-action-disgrete-type-hover; - border-color: @ui-action-disgrete-type-hover; + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-type-hover; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 19098b8a98..6a8fe00dd7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -162,12 +162,12 @@ @ui-action-border: @blueExtraDark; @ui-action-border-hover: @blueMid; -@ui-action-disgrete: white; -@ui-action-disgrete-hover: @sand-7; -@ui-action-disgrete-type: @blueExtraDark; -@ui-action-disgrete-type-hover: @blueMid; -@ui-action-disgrete-border: @gray-7; -@ui-action-disgrete-border-hover: @blueMid; +@ui-action-discreet: white; +@ui-action-discreet-hover: @sand-7; +@ui-action-discreet-type: @blueExtraDark; +@ui-action-discreet-type-hover: @blueMid; +@ui-action-discreet-border: @gray-7; +@ui-action-discreet-border-hover: @blueMid; @type-white: @white; @type-black: @blueNight; From 1f7172e98dc4306c28dc2f2a5f4b9349d646d3ea Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Tue, 12 Mar 2019 21:54:13 +0000 Subject: [PATCH 019/130] Cleaned up class names in logviewer overview to use new logviewer classes and made a logviewer specific less stylesheet. Also left aligned the saved searches to make them look better --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-logviewer.less | 42 +++++++++++++++++++ .../src/views/logviewer/overview.html | 10 ++--- 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 88988485fe..82fe416319 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -137,6 +137,7 @@ @import "components/umb-iconpicker.less"; @import "components/umb-insert-code-box.less"; @import "components/umb-packages.less"; +@import "components/umb-logviewer.less"; @import "components/umb-package-local-install.less"; @import "components/umb-panel-group.less"; @import "components/umb-lightbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less new file mode 100644 index 0000000000..f7aa0e4558 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less @@ -0,0 +1,42 @@ +/* PACKAGE DETAILS */ + +.umb-logviewer { + display: flex; + flex-flow: row wrap; +} + +@sidebarwidth: 350px; // Width of sidebar. Ugly hack because of old version of Less + +.umb-logviewer__main-content { + flex: 1 1 auto; + margin-right: 20px; + width: calc(~'100%' - ~'@{sidebarwidth}' - ~'20px'); // Make sure that the main content area doesn't gets affected by inline styling + min-width: 500px; + + .btn-link { + text-align: left; + } +} + +.umb-logviewer__sidebar { + flex: 0 0 @sidebarwidth; +} + +@media (max-width: 768px) { + + .umb-logviewer { + flex-direction: column; + } + + .umb-logviewer__main-content { + flex: 1 1 auto; + width: 100%; + margin-bottom: 30px; + margin-right: 0; + } + + .umb-logviewer__sidebar { + flex: 1 1 auto; + width: 100%; + } +} 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 67d89e5ee7..a46853f97e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/logviewer/overview.html @@ -1,4 +1,4 @@ -
+
@@ -24,8 +24,8 @@
-
-
+
+
@@ -68,7 +68,7 @@
-
+
@@ -95,4 +95,4 @@
-
\ No newline at end of file +
From 6d88726479b17338dcc2f3860a4b423ade7fff7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2019 11:46:33 +0100 Subject: [PATCH 020/130] Fixing code in sync functionality of checkbox-list --- .../checkboxlist/checkboxlist.controller.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js index 5ba9746c6e..43e50dc392 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.controller.js @@ -33,9 +33,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListContro } function updateViewModel(configItems) { - - //check if it's already in sync - + //get the checked vals from the view model var selectedVals = _.map( _.filter($scope.selectedItems, @@ -47,22 +45,23 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListContro return m.value; } ); - //get all of the same values between the arrays - var same = _.intersection($scope.model.value, selectedVals); - //if the lengths are the same as the value, then we are in sync, just exit - - if (same.length === $scope.model.value.length === selectedVals.length) { + + //if the length is zero, then we are in sync, just exit. + if (_.difference($scope.model.value, selectedVals).length === 0) { + debugger; return; } $scope.selectedItems = []; - + + var iConfigItem; for (var i = 0; i < configItems.length; i++) { - var isChecked = _.contains($scope.model.value, configItems[i].value); + iConfigItem = configItems[i]; + var isChecked = _.contains($scope.model.value, iConfigItem.value); $scope.selectedItems.push({ checked: isChecked, - key: configItems[i].id, - val: configItems[i].value + key: iConfigItem.id, + val: iConfigItem.value }); } } From 53e6c620eb13a9ee8e862953b1108d72ddb6295c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2019 14:45:30 +0100 Subject: [PATCH 021/130] fixes 4627 + makes front-end validations appear as soon as they are invalid again. --- .../directives/validation/valpropertymsg.directive.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index 9ee83dc2ba..c027e0778e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -62,8 +62,8 @@ function valPropertyMsg(serverValidationManager) { if (!watcher) { watcher = scope.$watch("currentProperty.value", function (newValue, oldValue) { - - if (!newValue || angular.equals(newValue, oldValue)) { + + if (angular.equals(newValue, oldValue)) { return; } @@ -78,10 +78,12 @@ function valPropertyMsg(serverValidationManager) { // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg // is the only one, then we'll clear. - if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { + if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { scope.errorMsg = ""; formCtrl.$setValidity('valPropertyMsg', true); - stopWatch(); + } else if (showValidation && scope.errorMsg === "") { + formCtrl.$setValidity('valPropertyMsg', false); + scope.errorMsg = getErrorMsg(); } }, true); } @@ -152,6 +154,7 @@ function valPropertyMsg(serverValidationManager) { showValidation = true; if (hasError && scope.errorMsg === "") { scope.errorMsg = getErrorMsg(); + startWatch(); } else if (!hasError) { scope.errorMsg = ""; From b0b987f9cd70b03a97fe57ef9bfbb7e8ae155d66 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 12 Mar 2019 08:31:28 +0100 Subject: [PATCH 022/130] Merge pull request #4710 from umbraco/temp8-4708-fix-for-exit-preview-with-null-culture Fix for exit preview with null culture (cherry picked from commit 9b25cdaa142c1545067b9155b91c51fe558cc700) --- .../src/preview/preview.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 7d6584d2f1..4733c58556 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -113,7 +113,12 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.exitPreview = function () { var culture = $location.search().culture || getParameterByName("culture"); - var relativeUrl = "/" + $scope.pageId +'?culture='+ culture; + var relativeUrl = "/" + $scope.pageId; + + if(culture){ + relativeUrl +='?culture='+ culture; + } + window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl); }; From 541c7e69552db1a22af6f904eba32e44a03afd8f Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 22 Feb 2019 15:27:22 +0100 Subject: [PATCH 023/130] NuCache: cleanup scoped objects --- .../Scoping/ScopeContextualBase.cs | 33 +++++++++++++++-- .../NuCache/PublishedSnapshotService.cs | 36 +++++++++---------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Core/Scoping/ScopeContextualBase.cs b/src/Umbraco.Core/Scoping/ScopeContextualBase.cs index 7461142234..1f2b6155e6 100644 --- a/src/Umbraco.Core/Scoping/ScopeContextualBase.cs +++ b/src/Umbraco.Core/Scoping/ScopeContextualBase.cs @@ -2,24 +2,43 @@ namespace Umbraco.Core.Scoping { - // base class for an object that will be enlisted in scope context, if any. it - // must be used in a 'using' block, and if not scoped, released when disposed, - // else when scope context runs enlisted actions + /// + /// Provides a base class for scope contextual objects. + /// + /// + /// A scope contextual object is enlisted in the current scope context, + /// if any, and released when the context exists. It must be used in a 'using' + /// block, and will be released when disposed, if not part of a scope. + /// public abstract class ScopeContextualBase : IDisposable { private bool _using, _scoped; + /// + /// Gets a contextual object. + /// + /// The type of the object. + /// A scope provider. + /// A context key for the object. + /// A function producing the contextual object. + /// The contextual object. + /// + /// + /// public static T Get(IScopeProvider scopeProvider, string key, Func ctor) where T : ScopeContextualBase { + // no scope context = create a non-scoped object var scopeContext = scopeProvider.Context; if (scopeContext == null) return ctor(false); + // create & enlist the scoped object var w = scopeContext.Enlist("ScopeContextualBase_" + key, () => ctor(true), (completed, item) => { item.Release(completed); }); + // the object can be 'used' only once at a time if (w._using) throw new InvalidOperationException("panic: used."); w._using = true; w._scoped = true; @@ -27,6 +46,10 @@ namespace Umbraco.Core.Scoping return w; } + /// + /// + /// If not scoped, then this releases the contextual object. + /// public void Dispose() { _using = false; @@ -35,6 +58,10 @@ namespace Umbraco.Core.Scoping Release(true); } + /// + /// Releases the contextual object. + /// + /// A value indicating whether the scoped operation completed. public abstract void Release(bool completed); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index e19531a25b..541ff2ea23 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -330,14 +330,15 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadContent(Action action) { + // first get a writer, then a scope + // if there already is a scope, the writer will attach to it + // otherwise, it will only exist here - cheap using (_contentStore.GetWriter(_scopeProvider)) + using (var scope = _scopeProvider.CreateScope()) { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - action(scope); - scope.Complete(); - } + scope.ReadLock(Constants.Locks.ContentTree); + action(scope); + scope.Complete(); } } @@ -399,14 +400,13 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadMedia(Action action) { + // see note in LockAndLoadContent using (_mediaStore.GetWriter(_scopeProvider)) + using (var scope = _scopeProvider.CreateScope()) { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.MediaTree); - action(scope); - scope.Complete(); - } + scope.ReadLock(Constants.Locks.MediaTree); + action(scope); + scope.Complete(); } } @@ -528,14 +528,13 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadDomains() { + // see note in LockAndLoadContent using (_domainStore.GetWriter(_scopeProvider)) + using (var scope = _scopeProvider.CreateScope()) { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.Domains); - LoadDomainsLocked(); - scope.Complete(); - } + scope.ReadLock(Constants.Locks.Domains); + LoadDomainsLocked(); + scope.Complete(); } } @@ -858,6 +857,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_isReady == false) return; + // see note in LockAndLoadContent using (_domainStore.GetWriter(_scopeProvider)) { foreach (var payload in payloads) From 66f9ecd8e2eac19a2ca6566f8cc982f1fe561f9d Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 22 Feb 2019 15:30:55 +0100 Subject: [PATCH 024/130] NuCache: rename things --- .../Cache/SnapDictionaryTests.cs | 10 ++-- .../PublishedCache/NuCache/ContentStore.cs | 56 ++++++++--------- .../PublishedCache/NuCache/SnapDictionary.cs | 60 +++++++++---------- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index 013dbadbb8..0ee0d180c3 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -388,8 +388,8 @@ namespace Umbraco.Tests.Cache // collect liveGen GC.Collect(); - SnapDictionary.GenerationObject genObj; - Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj)); + SnapDictionary.GenObj genObj; + Assert.IsTrue(d.Test.GenObjs.TryPeek(out genObj)); genObj = null; // in Release mode, it works, but in Debug mode, the weak reference is still alive @@ -399,14 +399,14 @@ namespace Umbraco.Tests.Cache GC.Collect(); #endif - Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj)); - Assert.IsFalse(genObj.WeakReference.IsAlive); // snapshot is gone, along with its reference + Assert.IsTrue(d.Test.GenObjs.TryPeek(out genObj)); + Assert.IsFalse(genObj.WeakGenRef.IsAlive); // snapshot is gone, along with its reference await d.CollectAsync(); Assert.AreEqual(0, d.Test.GetValues(1).Length); // null value is gone Assert.AreEqual(0, d.Count); // item is gone - Assert.AreEqual(0, d.Test.GenerationObjects.Count); + Assert.AreEqual(0, d.Test.GenObjs.Count); Assert.AreEqual(0, d.SnapCount); // snapshot is gone Assert.AreEqual(0, d.GenCount); // and generation has been dequeued } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index b3996050a6..e78c4d419e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -29,8 +29,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ILogger _logger; private BPlusTree _localDb; - private readonly ConcurrentQueue _genRefRefs; - private GenRefRef _genRefRef; + private readonly ConcurrentQueue _genObjs; + private GenObj _genObj; private readonly object _wlocko = new object(); private readonly object _rlocko = new object(); private long _liveGen, _floorGen; @@ -64,8 +64,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); _xmap = new ConcurrentDictionary(); - _genRefRefs = new ConcurrentQueue(); - _genRefRef = null; // no initial gen exists + _genObjs = new ConcurrentQueue(); + _genObj = null; // no initial gen exists _liveGen = _floorGen = 0; _nextGen = false; // first time, must create a snapshot _collectAuto = true; // collect automatically by default @@ -836,8 +836,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // if no next generation is required, and we already have one, // use it and create a new snapshot - if (_nextGen == false && _genRefRef != null) - return new Snapshot(this, _genRefRef.GetGenRef() + if (_nextGen == false && _genObj != null) + return new Snapshot(this, _genObj.GetGenRef() #if DEBUG , _logger #endif @@ -852,15 +852,15 @@ namespace Umbraco.Web.PublishedCache.NuCache var snapGen = _nextGen ? _liveGen - 1 : _liveGen; // create a new gen ref unless we already have it - if (_genRefRef == null) - _genRefRefs.Enqueue(_genRefRef = new GenRefRef(snapGen)); - else if (_genRefRef.Gen != snapGen) + if (_genObj == null) + _genObjs.Enqueue(_genObj = new GenObj(snapGen)); + else if (_genObj.Gen != snapGen) throw new Exception("panic"); } else { // not write-locked, can use latest gen, create a new gen ref - _genRefRefs.Enqueue(_genRefRef = new GenRefRef(_liveGen)); + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _nextGen = false; // this is the ONLY thing that triggers a _liveGen++ } @@ -873,7 +873,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // - the genRefRef weak ref is dead because all snapshots have been collected // in both cases, we will dequeue and collect - var snapshot = new Snapshot(this, _genRefRef.GetGenRef() + var snapshot = new Snapshot(this, _genObj.GetGenRef() #if DEBUG , _logger #endif @@ -930,10 +930,10 @@ namespace Umbraco.Web.PublishedCache.NuCache #if DEBUG _logger.Debug("Collect."); #endif - while (_genRefRefs.TryPeek(out GenRefRef genRefRef) && (genRefRef.Count == 0 || genRefRef.WGenRef.IsAlive == false)) + while (_genObjs.TryPeek(out var genObj) && (genObj.Count == 0 || genObj.WeakGenRef.IsAlive == false)) { - _genRefRefs.TryDequeue(out genRefRef); // cannot fail since TryPeek has succeeded - _floorGen = genRefRef.Gen; + _genObjs.TryDequeue(out genObj); // cannot fail since TryPeek has succeeded + _floorGen = genObj.Gen; #if DEBUG //_logger.Debug("_floorGen=" + _floorGen + ", _liveGen=" + _liveGen); #endif @@ -1009,9 +1009,9 @@ namespace Umbraco.Web.PublishedCache.NuCache await task; } - public long GenCount => _genRefRefs.Count; + public long GenCount => _genObjs.Count; - public long SnapCount => _genRefRefs.Sum(x => x.Count); + public long SnapCount => _genObjs.Sum(x => x.Count); #endregion @@ -1100,7 +1100,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _store = store; _genRef = genRef; _gen = genRef.Gen; - Interlocked.Increment(ref genRef.GenRefRef.Count); + Interlocked.Increment(ref genRef.GenObj.Count); //_thisCount = _count++; #if DEBUG @@ -1201,46 +1201,46 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (_gen < 0) return; #if DEBUG - _logger.Debug("Dispose snapshot ({Snapshot})", _genRef?.GenRefRef.Count.ToString() ?? "live"); + _logger.Debug("Dispose snapshot ({Snapshot})", _genRef?.GenObj.Count.ToString() ?? "live"); #endif _gen = -1; if (_genRef != null) - Interlocked.Decrement(ref _genRef.GenRefRef.Count); + Interlocked.Decrement(ref _genRef.GenObj.Count); GC.SuppressFinalize(this); } } - internal class GenRefRef + internal class GenObj { - public GenRefRef(long gen) + public GenObj(long gen) { Gen = gen; - WGenRef = new WeakReference(null); + WeakGenRef = new WeakReference(null); } public GenRef GetGenRef() { // not thread-safe but always invoked from within a lock - var genRef = (GenRef) WGenRef.Target; + var genRef = (GenRef) WeakGenRef.Target; if (genRef == null) - WGenRef.Target = genRef = new GenRef(this, Gen); + WeakGenRef.Target = genRef = new GenRef(this, Gen); return genRef; } public readonly long Gen; - public readonly WeakReference WGenRef; + public readonly WeakReference WeakGenRef; public int Count; } internal class GenRef { - public GenRef(GenRefRef genRefRef, long gen) + public GenRef(GenObj genObj, long gen) { - GenRefRef = genRefRef; + GenObj = genObj; Gen = gen; } - public readonly GenRefRef GenRefRef; + public readonly GenObj GenObj; public readonly long Gen; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs index 30f6e7e638..eebbe3122f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs @@ -21,8 +21,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // Readers are lock-free private readonly ConcurrentDictionary _items; - private readonly ConcurrentQueue _generationObjects; - private GenerationObject _generationObject; + private readonly ConcurrentQueue _genObjs; + private GenObj _genObj; private readonly object _wlocko = new object(); private readonly object _rlocko = new object(); private long _liveGen, _floorGen; @@ -41,8 +41,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public SnapDictionary() { _items = new ConcurrentDictionary(); - _generationObjects = new ConcurrentQueue(); - _generationObject = null; // no initial gen exists + _genObjs = new ConcurrentQueue(); + _genObj = null; // no initial gen exists _liveGen = _floorGen = 0; _nextGen = false; // first time, must create a snapshot _collectAuto = true; // collect automatically by default @@ -339,8 +339,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // if no next generation is required, and we already have one, // use it and create a new snapshot - if (_nextGen == false && _generationObject != null) - return new Snapshot(this, _generationObject.GetReference()); + if (_nextGen == false && _genObj != null) + return new Snapshot(this, _genObj.GetGenRef()); // else we need to try to create a new gen ref // whether we are wlocked or not, noone can rlock while we do, @@ -351,15 +351,15 @@ namespace Umbraco.Web.PublishedCache.NuCache var snapGen = _nextGen ? _liveGen - 1 : _liveGen; // create a new gen ref unless we already have it - if (_generationObject == null) - _generationObjects.Enqueue(_generationObject = new GenerationObject(snapGen)); - else if (_generationObject.Gen != snapGen) + if (_genObj == null) + _genObjs.Enqueue(_genObj = new GenObj(snapGen)); + else if (_genObj.Gen != snapGen) throw new Exception("panic"); } else { // not write-locked, can use latest gen, create a new gen ref - _generationObjects.Enqueue(_generationObject = new GenerationObject(_liveGen)); + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _nextGen = false; // this is the ONLY thing that triggers a _liveGen++ } @@ -372,7 +372,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // - the genRefRef weak ref is dead because all snapshots have been collected // in both cases, we will dequeue and collect - var snapshot = new Snapshot(this, _generationObject.GetReference()); + var snapshot = new Snapshot(this, _genObj.GetGenRef()); // reading _floorGen is safe if _collectTask is null if (_collectTask == null && _collectAuto && _liveGen - _floorGen > CollectMinGenDelta) @@ -416,10 +416,10 @@ namespace Umbraco.Web.PublishedCache.NuCache private void Collect() { // see notes in CreateSnapshot - while (_generationObjects.TryPeek(out GenerationObject generationObject) && (generationObject.Count == 0 || generationObject.WeakReference.IsAlive == false)) + while (_genObjs.TryPeek(out var genObj) && (genObj.Count == 0 || genObj.WeakGenRef.IsAlive == false)) { - _generationObjects.TryDequeue(out generationObject); // cannot fail since TryPeek has succeeded - _floorGen = generationObject.Gen; + _genObjs.TryDequeue(out genObj); // cannot fail since TryPeek has succeeded + _floorGen = genObj.Gen; } Collect(_items); @@ -490,9 +490,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // await task; } - public long GenCount => _generationObjects.Count; + public long GenCount => _genObjs.Count; - public long SnapCount => _generationObjects.Sum(x => x.Count); + public long SnapCount => _genObjs.Sum(x => x.Count); #endregion @@ -520,7 +520,7 @@ namespace Umbraco.Web.PublishedCache.NuCache set => _dict._collectAuto = value; } - public ConcurrentQueue GenerationObjects => _dict._generationObjects; + public ConcurrentQueue GenObjs => _dict._genObjs; public Snapshot LiveSnapshot => new Snapshot(_dict, _dict._liveGen); @@ -586,8 +586,8 @@ namespace Umbraco.Web.PublishedCache.NuCache { _store = store; _generationReference = generationReference; - _gen = generationReference.GenerationObject.Gen; - _generationReference.GenerationObject.Reference(); + _gen = generationReference.GenObj.Gen; + _generationReference.GenObj.Reference(); } internal Snapshot(SnapDictionary store, long gen) @@ -634,30 +634,30 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (_gen < 0) return; _gen = -1; - _generationReference?.GenerationObject.Release(); + _generationReference?.GenObj.Release(); GC.SuppressFinalize(this); } } - internal class GenerationObject + internal class GenObj { - public GenerationObject(long gen) + public GenObj(long gen) { Gen = gen; - WeakReference = new WeakReference(null); + WeakGenRef = new WeakReference(null); } - public GenerationReference GetReference() + public GenerationReference GetGenRef() { // not thread-safe but always invoked from within a lock - var generationReference = (GenerationReference) WeakReference.Target; + var generationReference = (GenerationReference) WeakGenRef.Target; if (generationReference == null) - WeakReference.Target = generationReference = new GenerationReference(this); + WeakGenRef.Target = generationReference = new GenerationReference(this); return generationReference; } public readonly long Gen; - public readonly WeakReference WeakReference; + public readonly WeakReference WeakGenRef; public int Count; public void Reference() @@ -673,12 +673,12 @@ namespace Umbraco.Web.PublishedCache.NuCache internal class GenerationReference { - public GenerationReference(GenerationObject generationObject) + public GenerationReference(GenObj genObj) { - GenerationObject = generationObject; + GenObj = genObj; } - public readonly GenerationObject GenerationObject; + public readonly GenObj GenObj; } #endregion From b7a03f01538c7ba5b42374146a87619913e2dca8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 22 Feb 2019 16:03:39 +0100 Subject: [PATCH 025/130] NuCache: reorg some classes --- .../Cache/SnapDictionaryTests.cs | 4 +- .../PublishedCache/NuCache/ContentStore.cs | 53 +----------- .../PublishedCache/NuCache/Snap/GenObj.cs | 37 +++++++++ .../PublishedCache/NuCache/Snap/GenRef.cs | 13 +++ .../PublishedCache/NuCache/Snap/LinkedNode.cs | 20 +++++ .../PublishedCache/NuCache/SnapDictionary.cs | 80 +++---------------- src/Umbraco.Web/Umbraco.Web.csproj | 3 + 7 files changed, 87 insertions(+), 123 deletions(-) create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index 0ee0d180c3..82c27ebc38 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -5,6 +5,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Scoping; using Umbraco.Web.PublishedCache.NuCache; +using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Tests.Cache { @@ -388,8 +389,7 @@ namespace Umbraco.Tests.Cache // collect liveGen GC.Collect(); - SnapDictionary.GenObj genObj; - Assert.IsTrue(d.Test.GenObjs.TryPeek(out genObj)); + Assert.IsTrue(d.Test.GenObjs.TryPeek(out var genObj)); genObj = null; // in Release mode, it works, but in Debug mode, the weak reference is still alive diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index e78c4d419e..dd5805daa1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -8,6 +8,7 @@ using CSharpTest.Net.Collections; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Scoping; +using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Web.PublishedCache.NuCache { @@ -1061,24 +1062,6 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Classes - private class LinkedNode - where TValue: class - { - public LinkedNode(TValue value, long gen, LinkedNode next = null) - { - Value = value; - Gen = gen; - Next = next; - } - - internal readonly long Gen; - - // reading & writing references is thread-safe on all .NET platforms - // mark as volatile to ensure we always read the correct value - internal volatile TValue Value; - internal volatile LinkedNode Next; - } - public class Snapshot : IDisposable { private readonly ContentStore _store; @@ -1210,40 +1193,6 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - internal class GenObj - { - public GenObj(long gen) - { - Gen = gen; - WeakGenRef = new WeakReference(null); - } - - public GenRef GetGenRef() - { - // not thread-safe but always invoked from within a lock - var genRef = (GenRef) WeakGenRef.Target; - if (genRef == null) - WeakGenRef.Target = genRef = new GenRef(this, Gen); - return genRef; - } - - public readonly long Gen; - public readonly WeakReference WeakGenRef; - public int Count; - } - - internal class GenRef - { - public GenRef(GenObj genObj, long gen) - { - GenObj = genObj; - Gen = gen; - } - - public readonly GenObj GenObj; - public readonly long Gen; - } - #endregion } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs b/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs new file mode 100644 index 0000000000..b69dab7dac --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenObj.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading; + +namespace Umbraco.Web.PublishedCache.NuCache.Snap +{ + internal class GenObj + { + public GenObj(long gen) + { + Gen = gen; + WeakGenRef = new WeakReference(null); + } + + public GenRef GetGenRef() + { + // not thread-safe but always invoked from within a lock + var genRef = (GenRef)WeakGenRef.Target; + if (genRef == null) + WeakGenRef.Target = genRef = new GenRef(this); + return genRef; + } + + public readonly long Gen; + public readonly WeakReference WeakGenRef; + public int Count; + + public void Reference() + { + Interlocked.Increment(ref Count); + } + + public void Release() + { + Interlocked.Decrement(ref Count); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs b/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs new file mode 100644 index 0000000000..ade0251b8d --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/Snap/GenRef.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Web.PublishedCache.NuCache.Snap +{ + internal class GenRef + { + public GenRef(GenObj genObj) + { + GenObj = genObj; + } + + public readonly GenObj GenObj; + public long Gen => GenObj.Gen; + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs new file mode 100644 index 0000000000..20d7e7ddcd --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Web.PublishedCache.NuCache.Snap +{ + internal class LinkedNode + where TValue : class + { + public LinkedNode(TValue value, long gen, LinkedNode next = null) + { + Value = value; + Gen = gen; + Next = next; + } + + public readonly long Gen; + + // reading & writing references is thread-safe on all .NET platforms + // mark as volatile to ensure we always read the correct value + public volatile TValue Value; + public volatile LinkedNode Next; + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs index eebbe3122f..f117a395b5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Umbraco.Core.Scoping; +using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Web.PublishedCache.NuCache { @@ -20,7 +21,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // This class is optimized for many readers, few writers // Readers are lock-free - private readonly ConcurrentDictionary _items; + private readonly ConcurrentDictionary> _items; private readonly ConcurrentQueue _genObjs; private GenObj _genObj; private readonly object _wlocko = new object(); @@ -40,7 +41,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public SnapDictionary() { - _items = new ConcurrentDictionary(); + _items = new ConcurrentDictionary>(); _genObjs = new ConcurrentQueue(); _genObj = null; // no initial gen exists _liveGen = _floorGen = 0; @@ -198,9 +199,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public int Count => _items.Count; - private LinkedNode GetHead(TKey key) + private LinkedNode GetHead(TKey key) { - _items.TryGetValue(key, out LinkedNode link); // else null + _items.TryGetValue(key, out var link); // else null return link; } @@ -221,7 +222,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // for an older gen - if value is different then insert a new // link for the new gen, with the new value if (link.Value != value) - _items.TryUpdate(key, new LinkedNode(value, _liveGen, link), link); + _items.TryUpdate(key, new LinkedNode(value, _liveGen, link), link); } else { @@ -235,7 +236,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { - _items.TryAdd(key, new LinkedNode(value, _liveGen)); + _items.TryAdd(key, new LinkedNode(value, _liveGen)); } } finally @@ -261,7 +262,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (kvp.Value.Gen < _liveGen) { - var link = new LinkedNode(null, _liveGen, kvp.Value); + var link = new LinkedNode(null, _liveGen, kvp.Value); _items.TryUpdate(kvp.Key, link, kvp.Value); } else @@ -425,7 +426,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Collect(_items); } - private void Collect(ConcurrentDictionary dict) + private void Collect(ConcurrentDictionary> dict) { // it is OK to enumerate a concurrent dictionary and it does not lock // it - and here it's not an issue if we skip some items, they will be @@ -460,7 +461,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // not live, null value, no next link = remove that one -- but only if // the dict has not been updated, have to do it via ICollection<> (thanks // Mr Toub) -- and if the dict has been updated there is nothing to collect - var idict = dict as ICollection>; + var idict = dict as ICollection>>; /*var removed =*/ idict.Remove(kvp); //Console.WriteLine("remove (" + (removed ? "true" : "false") + ")"); continue; @@ -526,7 +527,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public GenVal[] GetValues(TKey key) { - _dict._items.TryGetValue(key, out LinkedNode link); // else null + _dict._items.TryGetValue(key, out var link); // else null if (link == null) return new GenVal[0]; @@ -559,23 +560,6 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Classes - private class LinkedNode - { - public LinkedNode(TValue value, long gen, LinkedNode next = null) - { - Value = value; - Gen = gen; - Next = next; - } - - internal readonly long Gen; - - // reading & writing references is thread-safe on all .NET platforms - // mark as volatile to ensure we always read the correct value - internal volatile TValue Value; - internal volatile LinkedNode Next; - } - public class Snapshot : IDisposable { private readonly SnapDictionary _store; @@ -639,48 +623,6 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - internal class GenObj - { - public GenObj(long gen) - { - Gen = gen; - WeakGenRef = new WeakReference(null); - } - - public GenerationReference GetGenRef() - { - // not thread-safe but always invoked from within a lock - var generationReference = (GenerationReference) WeakGenRef.Target; - if (generationReference == null) - WeakGenRef.Target = generationReference = new GenerationReference(this); - return generationReference; - } - - public readonly long Gen; - public readonly WeakReference WeakGenRef; - public int Count; - - public void Reference() - { - Interlocked.Increment(ref Count); - } - - public void Release() - { - Interlocked.Decrement(ref Count); - } - } - - internal class GenerationReference - { - public GenerationReference(GenObj genObj) - { - GenObj = genObj; - } - - public readonly GenObj GenObj; - } - #endregion } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 09cc7d856a..c6cbc7cbaa 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -205,6 +205,9 @@ + + + From dc3e985fbbdd7d3049bfce837873a926b1ee1509 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 22 Feb 2019 15:43:37 +0100 Subject: [PATCH 026/130] NuCache: troubleshooting --- .../Cache/SnapDictionaryTests.cs | 174 ++++++++++++++++-- .../PublishedCache/NuCache/ContentStore.cs | 7 +- .../PublishedCache/NuCache/SnapDictionary.cs | 105 +++++++---- 3 files changed, 233 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index 82c27ebc38..eb034eec26 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -712,16 +712,102 @@ namespace Umbraco.Tests.Cache } [Test] - public void NestedWriteLocking() + public void NestedWriteLocking1() + { + var d = new SnapDictionary(); + var t = d.Test; + t.CollectAuto = false; + + Assert.AreEqual(0, d.CreateSnapshot().Gen); + + // no scope context: writers nest, last one to be disposed commits + + var scopeProvider = GetScopeProvider(); + + using (var w1 = d.GetWriter(scopeProvider)) + { + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.NextGen); + + using (var w2 = d.GetWriter(scopeProvider)) + { + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(2, t.WLocked); + Assert.IsTrue(t.NextGen); + + Assert.AreNotSame(w1, w2); // get a new writer each time + + d.Set(1, "one"); + + Assert.AreEqual(0, d.CreateSnapshot().Gen); + } + + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.NextGen); + + Assert.AreEqual(0, d.CreateSnapshot().Gen); + } + + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(0, t.WLocked); + Assert.IsTrue(t.NextGen); + + Assert.AreEqual(1, d.CreateSnapshot().Gen); + } + + [Test] + public void NestedWriteLocking2() { var d = new SnapDictionary(); d.Test.CollectAuto = false; - var scopeProvider = GetScopeProvider(); - using (d.GetWriter(scopeProvider)) + Assert.AreEqual(0, d.CreateSnapshot().Gen); + + // scope context: writers enlist + + var scopeContext = new ScopeContext(); + var scopeProvider = GetScopeProvider(scopeContext); + + using (var w1 = d.GetWriter(scopeProvider)) { - using (d.GetWriter(scopeProvider)) + using (var w2 = d.GetWriter(scopeProvider)) { + Assert.AreSame(w1, w2); + + d.Set(1, "one"); + } + } + } + + [Test] + public void NestedWriteLocking3() + { + var d = new SnapDictionary(); + var t = d.Test; + t.CollectAuto = false; + + Assert.AreEqual(0, d.CreateSnapshot().Gen); + + var scopeContext = new ScopeContext(); + var scopeProvider1 = GetScopeProvider(); + var scopeProvider2 = GetScopeProvider(scopeContext); + + using (var w1 = d.GetWriter(scopeProvider1)) + { + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(1, t.WLocked); + Assert.IsTrue(t.NextGen); + + using (var w2 = d.GetWriter(scopeProvider2)) + { + Assert.AreEqual(1, t.LiveGen); + Assert.AreEqual(2, t.WLocked); + Assert.IsTrue(t.NextGen); + + Assert.AreNotSame(w1, w2); + d.Set(1, "one"); } } @@ -846,7 +932,8 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(2, s2.Gen); Assert.AreEqual("uno", s2.Get(1)); - var scopeProvider = GetScopeProvider(true); + var scopeContext = new ScopeContext(); + var scopeProvider = GetScopeProvider(scopeContext); using (d.GetWriter(scopeProvider)) { @@ -867,7 +954,7 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(2, s4.Gen); Assert.AreEqual("uno", s4.Get(1)); - ((ScopeContext) scopeProvider.Context).ScopeExit(true); + scopeContext.ScopeExit(true); var s5 = d.CreateSnapshot(); Assert.AreEqual(3, s5.Gen); @@ -878,7 +965,8 @@ namespace Umbraco.Tests.Cache public void ScopeLocking2() { var d = new SnapDictionary(); - d.Test.CollectAuto = false; + var t = d.Test; + t.CollectAuto = false; // gen 1 d.Set(1, "one"); @@ -891,10 +979,11 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(2, s2.Gen); Assert.AreEqual("uno", s2.Get(1)); - var scopeProviderMock = new Mock(); + Assert.AreEqual(2, t.LiveGen); + Assert.IsFalse(t.NextGen); + var scopeContext = new ScopeContext(); - scopeProviderMock.Setup(x => x.Context).Returns(scopeContext); - var scopeProvider = scopeProviderMock.Object; + var scopeProvider = GetScopeProvider(scopeContext); using (d.GetWriter(scopeProvider)) { @@ -905,18 +994,72 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(2, s3.Gen); Assert.AreEqual("uno", s3.Get(1)); + // we made some changes, so a next gen is required + Assert.AreEqual(3, t.LiveGen); + Assert.IsTrue(t.NextGen); + Assert.AreEqual(1, t.WLocked); + // but live snapshot contains changes - var ls = d.Test.LiveSnapshot; + var ls = t.LiveSnapshot; Assert.AreEqual("ein", ls.Get(1)); Assert.AreEqual(3, ls.Gen); } + // nothing is committed until scope exits + Assert.AreEqual(3, t.LiveGen); + Assert.IsTrue(t.NextGen); + Assert.AreEqual(1, t.WLocked); + + // no changes until exit var s4 = d.CreateSnapshot(); Assert.AreEqual(2, s4.Gen); Assert.AreEqual("uno", s4.Get(1)); + // fixme - remove debugging code + /* + Exception caught = null; + var genFlip = 0; + var lckFlip = 0; + var thread = new System.Threading.Thread(() => + { + try + { + for (var i = 0; i < 20; i++) + { + if (t.LiveGen == 2 && genFlip == 0) genFlip = i; // flips at 1 + if (t.WLocked == 0 && lckFlip == 0) lckFlip = i; // flips at 10 ie 5s, as expected + d.CreateSnapshot(); + System.Threading.Thread.Sleep(500); + } + } + catch (Exception e) + { + caught = e; + } + }); + thread.Start(); + */ + scopeContext.ScopeExit(false); + // fixme - remove debugging code + /* + thread.Join(); + + Assert.IsNull(caught); // but then how can it be not null? + + Console.WriteLine(genFlip); + Console.WriteLine(lckFlip); + Assert.AreEqual(1, genFlip); + Assert.AreEqual(10, lckFlip); + */ + + // now things have changed + Assert.AreEqual(2, t.LiveGen); + Assert.IsFalse(t.NextGen); + Assert.AreEqual(0, t.WLocked); + + // no changes since not completed var s5 = d.CreateSnapshot(); Assert.AreEqual(2, s5.Gen); Assert.AreEqual("uno", s5.Get(1)); @@ -955,12 +1098,11 @@ namespace Umbraco.Tests.Cache Assert.AreEqual("four", all[3]); } - private IScopeProvider GetScopeProvider(bool withContext = false) + private IScopeProvider GetScopeProvider(ScopeContext scopeContext = null) { - var scopeProviderMock = new Mock(); - var scopeContext = withContext ? new ScopeContext() : null; - scopeProviderMock.Setup(x => x.Context).Returns(scopeContext); - var scopeProvider = scopeProviderMock.Object; + var scopeProvider = Mock.Of(); + Mock.Get(scopeProvider) + .Setup(x => x.Context).Returns(scopeContext); return scopeProvider; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index dd5805daa1..2548046e23 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -95,7 +95,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private class ContentStoreWriter : ScopeContextualBase { private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); - private ContentStore _store; + private readonly ContentStore _store; + private int _released; public ContentStoreWriter(ContentStore store, bool scoped) { @@ -105,9 +106,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public override void Release(bool completed) { - if (_store== null) return; + if (Interlocked.CompareExchange(ref _released, 1, 0) != 0) + return; _store.Release(_lockinfo, completed); - _store = null; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs index f117a395b5..1d462f1b76 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs @@ -90,7 +90,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private class SnapDictionaryWriter : ScopeContextualBase { private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); - private SnapDictionary _dictionary; + private readonly SnapDictionary _dictionary; + private int _released; public SnapDictionaryWriter(SnapDictionary dictionary, bool scoped) { @@ -100,14 +101,18 @@ namespace Umbraco.Web.PublishedCache.NuCache public override void Release(bool completed) { - if (_dictionary == null) return; + if (Interlocked.CompareExchange(ref _released, 1, 0) != 0) + return; _dictionary.Release(_lockinfo, completed); - _dictionary = null; } } // gets a scope contextual representing a locked writer to the dictionary - // GetScopedWriter? should the dict have a ref onto the scope provider? + // fixme GetScopedWriter? should the dict have a ref onto the scope provider? + // fixme this is not a "writer" but a "write lock" => rename GetWriteLock + // the dict is write-locked until the write-lock is released + // which happens when it is disposed (non-scoped) + // or when the scope context exits (scoped) public IDisposable GetWriter(IScopeProvider scopeProvider) { return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new SnapDictionaryWriter(this, scoped)); @@ -130,13 +135,22 @@ namespace Umbraco.Web.PublishedCache.NuCache //RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { + // increment the lock count, and register that this lock is counting _wlocked++; lockInfo.Count = true; + + // fixme - this comment: "ok to have holes in generation objects" is annoying + // 'cos if you have a hole, then doing _liveGen-1 in some places would be wrong + // besides, forceGen is used only when getting a scoped write lock, which is not reentrant? + // + // but... if _wlocked ==1 and we just incremented it, it means it was 0, so it wasnt locked, so wtf? + // this is the only place where _nextGen would turn true so ... this makes no sense at all! + if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects { // because we are changing things, a new generation // is created, which will trigger a new snapshot - _nextGen = true; + _nextGen = true; // this is the ONLY place where _nextGen becomes true _liveGen += 1; } } @@ -154,6 +168,10 @@ namespace Umbraco.Web.PublishedCache.NuCache private void Release(WriteLockInfo lockInfo, bool commit = true) { + // if the lock wasn't taken in the first place, do nothing + if (!lockInfo.Taken) + return; + if (commit == false) { var rtaken = false; @@ -162,6 +180,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Monitor.Enter(_rlocko, ref rtaken); try { } finally { + // forget about the temp. liveGen _nextGen = false; _liveGen -= 1; } @@ -184,8 +203,12 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + // fixme - pretend we need to do something that takes time + //System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); + + // decrement the lock count, if counting, then exit the lock if (lockInfo.Count) _wlocked--; - if (lockInfo.Taken) Monitor.Exit(_wlocko); + Monitor.Exit(_wlocko); } private void Release(ReadLockInfo lockInfo) @@ -338,12 +361,12 @@ namespace Umbraco.Web.PublishedCache.NuCache { Lock(lockInfo); - // if no next generation is required, and we already have one, - // use it and create a new snapshot + // if no next generation is required, and we already have a gen object, + // use it to create a new snapshot if (_nextGen == false && _genObj != null) return new Snapshot(this, _genObj.GetGenRef()); - // else we need to try to create a new gen ref + // else we need to try to create a new gen object // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe if (_wlocked > 0) // volatile, cannot ++ but could -- @@ -351,26 +374,32 @@ namespace Umbraco.Web.PublishedCache.NuCache // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; - // create a new gen ref unless we already have it + // create a new gen object if we don't already have one + // (happens the first time a snapshot is created) if (_genObj == null) _genObjs.Enqueue(_genObj = new GenObj(snapGen)); + + // fixme - getting a panic exception here + // means _genObj != null, means _nextGen == true + + // if we have one already, ensure it's consistent else if (_genObj.Gen != snapGen) throw new Exception("panic"); } else { - // not write-locked, can use latest gen, create a new gen ref + // not write-locked, can use latest gen (_liveGen), create a corresponding new gen object _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _nextGen = false; // this is the ONLY thing that triggers a _liveGen++ } // so... - // the genRefRef has a weak ref to the genRef, and is queued - // the snapshot has a ref to the genRef, which has a ref to the genRefRef - // when the snapshot is disposed, it decreases genRefRef counter + // the genObj has a weak ref to the genRef, and is queued + // the snapshot has a ref to the genRef, which has a ref to the genObj + // when the snapshot is disposed, it decreases genObj counter // so after a while, one of these conditions is going to be true: - // - the genRefRef counter is zero because all snapshots have properly been disposed - // - the genRefRef weak ref is dead because all snapshots have been collected + // - genObj.Count is zero because all snapshots have properly been disposed + // - genObj.WeakGenRef is dead because all snapshots have been collected // in both cases, we will dequeue and collect var snapshot = new Snapshot(this, _genObj.GetGenRef()); @@ -486,7 +515,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { task = _collectTask; } - return task ?? Task.FromResult(0); + return task ?? Task.CompletedTask; //if (task != null) // await task; } @@ -514,6 +543,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public long LiveGen => _dict._liveGen; public long FloorGen => _dict._floorGen; public bool NextGen => _dict._nextGen; + public int WLocked => _dict._wlocked; public bool CollectAuto { @@ -563,15 +593,20 @@ namespace Umbraco.Web.PublishedCache.NuCache public class Snapshot : IDisposable { private readonly SnapDictionary _store; - private readonly GenerationReference _generationReference; - private long _gen; // copied for perfs + private readonly GenRef _genRef; + private readonly long _gen; // copied for perfs + private int _disposed; - internal Snapshot(SnapDictionary store, GenerationReference generationReference) + //private static int _count; + //private readonly int _thisCount; + + internal Snapshot(SnapDictionary store, GenRef genRef) { _store = store; - _generationReference = generationReference; - _gen = generationReference.GenObj.Gen; - _generationReference.GenObj.Reference(); + _genRef = genRef; + _gen = genRef.GenObj.Gen; + _genRef.GenObj.Reference(); + //_thisCount = _count++; } internal Snapshot(SnapDictionary store, long gen) @@ -580,17 +615,21 @@ namespace Umbraco.Web.PublishedCache.NuCache _gen = gen; } + private void EnsureNotDisposed() + { + if (_disposed > 0) + throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + } + public TValue Get(TKey key) { - if (_gen < 0) - throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + EnsureNotDisposed(); return _store.Get(key, _gen); } public IEnumerable GetAll() { - if (_gen < 0) - throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + EnsureNotDisposed(); return _store.GetAll(_gen); } @@ -598,8 +637,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { get { - if (_gen < 0) - throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + EnsureNotDisposed(); return _store.IsEmpty(_gen); } } @@ -608,17 +646,16 @@ namespace Umbraco.Web.PublishedCache.NuCache { get { - if (_gen < 0) - throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + EnsureNotDisposed(); return _gen; } } public void Dispose() { - if (_gen < 0) return; - _gen = -1; - _generationReference?.GenObj.Release(); + if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0) + return; + _genRef?.GenObj.Release(); GC.SuppressFinalize(this); } } From 260a8ad8748c3d9eb2669590c58927865194c074 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 14 Mar 2019 18:56:23 +0100 Subject: [PATCH 027/130] NuCache: fix panic exception --- .../Cache/SnapDictionaryTests.cs | 81 +++++++++++++++++++ .../PublishedCache/NuCache/ContentStore.cs | 4 +- .../PublishedCache/NuCache/SnapDictionary.cs | 15 +--- 3 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index eb034eec26..e4ca35fbd3 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -1098,6 +1098,87 @@ namespace Umbraco.Tests.Cache Assert.AreEqual("four", all[3]); } + [Test] + public void DontPanic() + { + var d = new SnapDictionary(); + d.Test.CollectAuto = false; + + Assert.IsNull(d.Test.GenObj); // set with first snapshot or first lock, then never null + + // gen 1 + d.Set(1, "one"); + Assert.IsTrue(d.Test.NextGen); + Assert.AreEqual(1, d.Test.LiveGen); + Assert.IsNotNull(d.Test.GenObj); // set with lock + + var s1 = d.CreateSnapshot(); + Assert.IsFalse(d.Test.NextGen); + Assert.AreEqual(1, d.Test.LiveGen); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(1, d.Test.GenObj.Gen); + + Assert.AreEqual(1, s1.Gen); + Assert.AreEqual("one", s1.Get(1)); + + d.Set(1, "uno"); + Assert.IsTrue(d.Test.NextGen); + Assert.AreEqual(2, d.Test.LiveGen); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(1, d.Test.GenObj.Gen); + + var scopeContext = new ScopeContext(); + var scopeProvider = GetScopeProvider(scopeContext); + + // scopeProvider.Context == scopeContext -> writer is scoped + // writer is scope contextual and scoped + // when disposed, nothing happens + // when the context exists, the writer is released + using (d.GetWriter(scopeProvider)) + { + d.Set(1, "ein"); + Assert.IsTrue(d.Test.NextGen); + Assert.AreEqual(3, d.Test.LiveGen); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(2, d.Test.GenObj.Gen); + } + + // writer has not released + Assert.AreEqual(1, d.Test.WLocked); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(2, d.Test.GenObj.Gen); + + // nothing changed + Assert.IsTrue(d.Test.NextGen); + Assert.AreEqual(3, d.Test.LiveGen); + + // panic! + var s2 = d.CreateSnapshot(); + + Assert.AreEqual(1, d.Test.WLocked); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(2, d.Test.GenObj.Gen); + Assert.AreEqual(3, d.Test.LiveGen); + Assert.IsTrue(d.Test.NextGen); + + // release writer + scopeContext.ScopeExit(true); + + Assert.AreEqual(0, d.Test.WLocked); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(2, d.Test.GenObj.Gen); + Assert.AreEqual(3, d.Test.LiveGen); + Assert.IsTrue(d.Test.NextGen); + + var s3 = d.CreateSnapshot(); + + Assert.AreEqual(0, d.Test.WLocked); + Assert.IsNotNull(d.Test.GenObj); + Assert.AreEqual(3, d.Test.GenObj.Gen); + Assert.AreEqual(3, d.Test.LiveGen); + Assert.IsFalse(d.Test.NextGen); + } + private IScopeProvider GetScopeProvider(ScopeContext scopeContext = null) { var scopeProvider = Mock.Of(); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 2548046e23..2b9c9bee4d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -133,11 +133,12 @@ namespace Umbraco.Web.PublishedCache.NuCache { _wlocked++; lockInfo.Count = true; - if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects + if (_nextGen == false || (forceGen && _wlocked == 1)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot _nextGen = true; + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _liveGen += 1; } } @@ -214,7 +215,6 @@ namespace Umbraco.Web.PublishedCache.NuCache else dictionary.TryUpdate(key, link.Next, link); } - } #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs index 1d462f1b76..73904d1452 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs @@ -139,18 +139,12 @@ namespace Umbraco.Web.PublishedCache.NuCache _wlocked++; lockInfo.Count = true; - // fixme - this comment: "ok to have holes in generation objects" is annoying - // 'cos if you have a hole, then doing _liveGen-1 in some places would be wrong - // besides, forceGen is used only when getting a scoped write lock, which is not reentrant? - // - // but... if _wlocked ==1 and we just incremented it, it means it was 0, so it wasnt locked, so wtf? - // this is the only place where _nextGen would turn true so ... this makes no sense at all! - - if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects + if (_nextGen == false || (forceGen && _wlocked == 1)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot _nextGen = true; // this is the ONLY place where _nextGen becomes true + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _liveGen += 1; } } @@ -379,9 +373,6 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_genObj == null) _genObjs.Enqueue(_genObj = new GenObj(snapGen)); - // fixme - getting a panic exception here - // means _genObj != null, means _nextGen == true - // if we have one already, ensure it's consistent else if (_genObj.Gen != snapGen) throw new Exception("panic"); @@ -551,6 +542,8 @@ namespace Umbraco.Web.PublishedCache.NuCache set => _dict._collectAuto = value; } + public GenObj GenObj => _dict._genObj; + public ConcurrentQueue GenObjs => _dict._genObjs; public Snapshot LiveSnapshot => new Snapshot(_dict, _dict._liveGen); From e24b22f6bd1a06ba79b9420c51a38a18afef6f7c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 15 Mar 2019 12:20:24 +1100 Subject: [PATCH 028/130] Fixes the authorization for certain endpoints by non admins so that data cannot be seen and avatars cannot be changed --- src/Umbraco.Web/Editors/UsersController.cs | 3 ++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../Filters/AdminUsersAuthorizeAttribute.cs | 42 +++++++++++++++++++ .../UmbracoApplicationAuthorizeAttribute.cs | 3 +- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index dbfcf64ce2..932f10f343 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -82,6 +82,7 @@ namespace Umbraco.Web.Editors [AppendUserModifiedHeader("id")] [FileUploadCleanupFilter(false)] + [AdminUsersAuthorize] public async Task PostSetAvatar(int id) { return await PostSetAvatarInternal(Request, Services.UserService, ApplicationContext.ApplicationCache.StaticCache, id); @@ -145,6 +146,7 @@ namespace Umbraco.Web.Editors } [AppendUserModifiedHeader("id")] + [AdminUsersAuthorize] public HttpResponseMessage PostClearAvatar(int id) { var found = Services.UserService.GetUserById(id); @@ -183,6 +185,7 @@ namespace Umbraco.Web.Editors /// /// [OutgoingEditorModelEvent] + [AdminUsersAuthorize] public UserDisplay GetById(int id) { var user = Services.UserService.GetUserById(id); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 036cb96b50..759e6b47f3 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -832,6 +832,7 @@ + diff --git a/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs new file mode 100644 index 0000000000..8701eaf226 --- /dev/null +++ b/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs @@ -0,0 +1,42 @@ +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Controllers; +using Umbraco.Core; +using Umbraco.Web.Editors; + +namespace Umbraco.Web.WebApi.Filters +{ + /// + /// if the user being edited is an admin then we must ensure that the current user is also an admin + /// + public sealed class AdminUsersAuthorizeAttribute : AuthorizeAttribute + { + protected override bool IsAuthorized(HttpActionContext actionContext) + { + if (actionContext.ActionArguments.TryGetValue("id", out var userId) == false) + { + var queryString = actionContext.Request.GetQueryNameValuePairs(); + var ids = queryString.Where(x => x.Key == "id").ToArray(); + if (ids.Length == 0) + return base.IsAuthorized(actionContext); + userId = ids[0].Value; + } + + if (userId == null) return base.IsAuthorized(actionContext); + var intUserId = userId.TryConvertTo(); + if (intUserId.Success == false) + return base.IsAuthorized(actionContext); + + var user = ApplicationContext.Current.Services.UserService.GetUserById(intUserId.Result); + if (user == null) + return base.IsAuthorized(actionContext); + + //Perform authorization here to see if the current user can actually save this user with the info being requested + var authHelper = new UserEditorAuthorizationHelper(ApplicationContext.Current.Services.ContentService, ApplicationContext.Current.Services.MediaService, ApplicationContext.Current.Services.UserService, ApplicationContext.Current.Services.EntityService); + var canSaveUser = authHelper.IsAuthorized(UmbracoContext.Current.Security.CurrentUser, user, null, null, null); + return canSaveUser; + } + } +} diff --git a/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs index 1899584d6f..bc8370c0d5 100644 --- a/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Web.Http; using System.Web.Http.Controllers; namespace Umbraco.Web.WebApi.Filters @@ -41,4 +40,4 @@ namespace Umbraco.Web.WebApi.Filters return authorized; } } -} \ No newline at end of file +} From d74ec8f8cb531fa631caa2050cfcac91b9654e64 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 15 Mar 2019 12:39:46 +1100 Subject: [PATCH 029/130] Authorizes more endpoints for admin/non-admins --- src/Umbraco.Web/Editors/UsersController.cs | 9 +++- .../Filters/AdminUsersAuthorizeAttribute.cs | 44 ++++++++++++------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 932f10f343..0588eef2c0 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -605,12 +605,13 @@ namespace Umbraco.Web.Editors display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved")); return display; - } + } /// /// Disables the users with the given user ids /// /// + [AdminUsersAuthorize("userIds")] public HttpResponseMessage PostDisableUsers([FromUri]int[] userIds) { if (userIds.Contains(Security.GetUserId())) @@ -641,6 +642,7 @@ namespace Umbraco.Web.Editors /// Enables the users with the given user ids /// /// + [AdminUsersAuthorize("userIds")] public HttpResponseMessage PostEnableUsers([FromUri]int[] userIds) { var users = Services.UserService.GetUsersById(userIds).ToArray(); @@ -664,6 +666,7 @@ namespace Umbraco.Web.Editors /// Unlocks the users with the given user ids /// /// + [AdminUsersAuthorize("userIds")] public async Task PostUnlockUsers([FromUri]int[] userIds) { if (userIds.Length <= 0) @@ -696,6 +699,7 @@ namespace Umbraco.Web.Editors Services.TextService.Localize("speechBubbles/unlockUsersSuccess", new[] { userIds.Length.ToString() })); } + [AdminUsersAuthorize("userIds")] public HttpResponseMessage PostSetUserGroupsOnUsers([FromUri]string[] userGroupAliases, [FromUri]int[] userIds) { var users = Services.UserService.GetUsersById(userIds).ToArray(); @@ -721,7 +725,8 @@ namespace Umbraco.Web.Editors /// Limited to users that haven't logged in to avoid issues with related records constrained /// with a foreign key on the user Id /// - public async Task PostDeleteNonLoggedInUser(int id) + [AdminUsersAuthorize] + public HttpResponseMessage PostDeleteNonLoggedInUser(int id) { var user = Services.UserService.GetUserById(id); if (user == null) diff --git a/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs index 8701eaf226..e9cb1d8c6e 100644 --- a/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs @@ -9,34 +9,48 @@ using Umbraco.Web.Editors; namespace Umbraco.Web.WebApi.Filters { /// - /// if the user being edited is an admin then we must ensure that the current user is also an admin + /// if the users being edited is an admin then we must ensure that the current user is also an admin /// + /// + /// This will authorize against one or multiple ids + /// public sealed class AdminUsersAuthorizeAttribute : AuthorizeAttribute { + private readonly string _parameterName; + + public AdminUsersAuthorizeAttribute(string parameterName) + { + _parameterName = parameterName; + } + + public AdminUsersAuthorizeAttribute() : this("id") + { + } + protected override bool IsAuthorized(HttpActionContext actionContext) { - if (actionContext.ActionArguments.TryGetValue("id", out var userId) == false) + int[] userIds; + if (actionContext.ActionArguments.TryGetValue(_parameterName, out var userId)) + { + var intUserId = userId.TryConvertTo(); + if (intUserId) + userIds = new[] {intUserId.Result}; + else return base.IsAuthorized(actionContext); + } + else { var queryString = actionContext.Request.GetQueryNameValuePairs(); - var ids = queryString.Where(x => x.Key == "id").ToArray(); + var ids = queryString.Where(x => x.Key == _parameterName).ToArray(); if (ids.Length == 0) return base.IsAuthorized(actionContext); - userId = ids[0].Value; + userIds = ids.Select(x => x.Value.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray(); } - if (userId == null) return base.IsAuthorized(actionContext); - var intUserId = userId.TryConvertTo(); - if (intUserId.Success == false) - return base.IsAuthorized(actionContext); + if (userIds.Length == 0) return base.IsAuthorized(actionContext); - var user = ApplicationContext.Current.Services.UserService.GetUserById(intUserId.Result); - if (user == null) - return base.IsAuthorized(actionContext); - - //Perform authorization here to see if the current user can actually save this user with the info being requested + var users = ApplicationContext.Current.Services.UserService.GetUsersById(userIds); var authHelper = new UserEditorAuthorizationHelper(ApplicationContext.Current.Services.ContentService, ApplicationContext.Current.Services.MediaService, ApplicationContext.Current.Services.UserService, ApplicationContext.Current.Services.EntityService); - var canSaveUser = authHelper.IsAuthorized(UmbracoContext.Current.Security.CurrentUser, user, null, null, null); - return canSaveUser; + return users.All(user => authHelper.IsAuthorized(UmbracoContext.Current.Security.CurrentUser, user, null, null, null) != false); } } } From a6568e1952b8e71d451f062042b03d1a8de2c95a Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 14 Mar 2019 19:48:44 +0100 Subject: [PATCH 030/130] NuCache: better fixing, cleanup --- .../Scoping/ScopeContextualBase.cs | 7 +- .../Cache/SnapDictionaryTests.cs | 67 +++++-------------- .../PublishedCache/NuCache/ContentStore.cs | 13 ++-- .../NuCache/PublishedSnapshotService.cs | 18 ++--- .../PublishedCache/NuCache/SnapDictionary.cs | 18 ++--- 5 files changed, 39 insertions(+), 84 deletions(-) diff --git a/src/Umbraco.Core/Scoping/ScopeContextualBase.cs b/src/Umbraco.Core/Scoping/ScopeContextualBase.cs index 1f2b6155e6..25f176d471 100644 --- a/src/Umbraco.Core/Scoping/ScopeContextualBase.cs +++ b/src/Umbraco.Core/Scoping/ScopeContextualBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Scoping /// public abstract class ScopeContextualBase : IDisposable { - private bool _using, _scoped; + private bool _scoped; /// /// Gets a contextual object. @@ -38,9 +38,6 @@ namespace Umbraco.Core.Scoping () => ctor(true), (completed, item) => { item.Release(completed); }); - // the object can be 'used' only once at a time - if (w._using) throw new InvalidOperationException("panic: used."); - w._using = true; w._scoped = true; return w; @@ -52,8 +49,6 @@ namespace Umbraco.Core.Scoping /// public void Dispose() { - _using = false; - if (_scoped == false) Release(true); } diff --git a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs index e4ca35fbd3..b435af9e77 100644 --- a/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests/Cache/SnapDictionaryTests.cs @@ -632,7 +632,7 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(1, d.Test.LiveGen); Assert.IsTrue(d.Test.NextGen); - using (d.GetWriter(GetScopeProvider())) + using (d.GetScopedWriteLock(GetScopeProvider())) { var s1 = d.CreateSnapshot(); @@ -685,7 +685,7 @@ namespace Umbraco.Tests.Cache Assert.IsFalse(d.Test.NextGen); Assert.AreEqual("uno", s2.Get(1)); - using (d.GetWriter(GetScopeProvider())) + using (d.GetScopedWriteLock(GetScopeProvider())) { // gen 3 Assert.AreEqual(2, d.Test.GetValues(1).Length); @@ -724,13 +724,13 @@ namespace Umbraco.Tests.Cache var scopeProvider = GetScopeProvider(); - using (var w1 = d.GetWriter(scopeProvider)) + using (var w1 = d.GetScopedWriteLock(scopeProvider)) { Assert.AreEqual(1, t.LiveGen); Assert.AreEqual(1, t.WLocked); Assert.IsTrue(t.NextGen); - using (var w2 = d.GetWriter(scopeProvider)) + using (var w2 = d.GetScopedWriteLock(scopeProvider)) { Assert.AreEqual(1, t.LiveGen); Assert.AreEqual(2, t.WLocked); @@ -770,9 +770,9 @@ namespace Umbraco.Tests.Cache var scopeContext = new ScopeContext(); var scopeProvider = GetScopeProvider(scopeContext); - using (var w1 = d.GetWriter(scopeProvider)) + using (var w1 = d.GetScopedWriteLock(scopeProvider)) { - using (var w2 = d.GetWriter(scopeProvider)) + using (var w2 = d.GetScopedWriteLock(scopeProvider)) { Assert.AreSame(w1, w2); @@ -794,13 +794,13 @@ namespace Umbraco.Tests.Cache var scopeProvider1 = GetScopeProvider(); var scopeProvider2 = GetScopeProvider(scopeContext); - using (var w1 = d.GetWriter(scopeProvider1)) + using (var w1 = d.GetScopedWriteLock(scopeProvider1)) { Assert.AreEqual(1, t.LiveGen); Assert.AreEqual(1, t.WLocked); Assert.IsTrue(t.NextGen); - using (var w2 = d.GetWriter(scopeProvider2)) + using (var w2 = d.GetScopedWriteLock(scopeProvider2)) { Assert.AreEqual(1, t.LiveGen); Assert.AreEqual(2, t.WLocked); @@ -850,7 +850,7 @@ namespace Umbraco.Tests.Cache var scopeProvider = GetScopeProvider(); - using (d.GetWriter(scopeProvider)) + using (d.GetScopedWriteLock(scopeProvider)) { // gen 3 Assert.AreEqual(2, d.Test.GetValues(1).Length); @@ -895,7 +895,7 @@ namespace Umbraco.Tests.Cache var scopeProvider = GetScopeProvider(); - using (d.GetWriter(scopeProvider)) + using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release @@ -935,7 +935,7 @@ namespace Umbraco.Tests.Cache var scopeContext = new ScopeContext(); var scopeProvider = GetScopeProvider(scopeContext); - using (d.GetWriter(scopeProvider)) + using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release @@ -985,7 +985,7 @@ namespace Umbraco.Tests.Cache var scopeContext = new ScopeContext(); var scopeProvider = GetScopeProvider(scopeContext); - using (d.GetWriter(scopeProvider)) + using (d.GetScopedWriteLock(scopeProvider)) { // creating a snapshot in a write-lock does NOT return the "current" content // it uses the previous snapshot, so new snapshot created only on release @@ -1015,45 +1015,8 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(2, s4.Gen); Assert.AreEqual("uno", s4.Get(1)); - // fixme - remove debugging code - /* - Exception caught = null; - var genFlip = 0; - var lckFlip = 0; - var thread = new System.Threading.Thread(() => - { - try - { - for (var i = 0; i < 20; i++) - { - if (t.LiveGen == 2 && genFlip == 0) genFlip = i; // flips at 1 - if (t.WLocked == 0 && lckFlip == 0) lckFlip = i; // flips at 10 ie 5s, as expected - d.CreateSnapshot(); - System.Threading.Thread.Sleep(500); - } - } - catch (Exception e) - { - caught = e; - } - }); - thread.Start(); - */ - scopeContext.ScopeExit(false); - // fixme - remove debugging code - /* - thread.Join(); - - Assert.IsNull(caught); // but then how can it be not null? - - Console.WriteLine(genFlip); - Console.WriteLine(lckFlip); - Assert.AreEqual(1, genFlip); - Assert.AreEqual(10, lckFlip); - */ - // now things have changed Assert.AreEqual(2, t.LiveGen); Assert.IsFalse(t.NextGen); @@ -1104,13 +1067,13 @@ namespace Umbraco.Tests.Cache var d = new SnapDictionary(); d.Test.CollectAuto = false; - Assert.IsNull(d.Test.GenObj); // set with first snapshot or first lock, then never null + Assert.IsNull(d.Test.GenObj); // gen 1 d.Set(1, "one"); Assert.IsTrue(d.Test.NextGen); Assert.AreEqual(1, d.Test.LiveGen); - Assert.IsNotNull(d.Test.GenObj); // set with lock + Assert.IsNull(d.Test.GenObj); var s1 = d.CreateSnapshot(); Assert.IsFalse(d.Test.NextGen); @@ -1134,7 +1097,7 @@ namespace Umbraco.Tests.Cache // writer is scope contextual and scoped // when disposed, nothing happens // when the context exists, the writer is released - using (d.GetWriter(scopeProvider)) + using (d.GetScopedWriteLock(scopeProvider)) { d.Set(1, "ein"); Assert.IsTrue(d.Test.NextGen); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 2b9c9bee4d..7ab4a64f31 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -92,13 +92,13 @@ namespace Umbraco.Web.PublishedCache.NuCache } // a scope contextual that represents a locked writer to the dictionary - private class ContentStoreWriter : ScopeContextualBase + private class ScopedWriteLock : ScopeContextualBase { private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); private readonly ContentStore _store; private int _released; - public ContentStoreWriter(ContentStore store, bool scoped) + public ScopedWriteLock(ContentStore store, bool scoped) { _store = store; store.Lock(_lockinfo, scoped); @@ -114,9 +114,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // gets a scope contextual representing a locked writer to the dictionary // TODO: GetScopedWriter? should the dict have a ref onto the scope provider? - public IDisposable GetWriter(IScopeProvider scopeProvider) + public IDisposable GetScopedWriteLock(IScopeProvider scopeProvider) { - return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ContentStoreWriter(this, scoped)); + return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } private void Lock(WriteLockInfo lockInfo, bool forceGen = false) @@ -137,9 +137,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { // because we are changing things, a new generation // is created, which will trigger a new snapshot - _nextGen = true; - _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); + if (_nextGen) + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _liveGen += 1; + _nextGen = true; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 541ff2ea23..9c5587fbd5 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -333,7 +333,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // first get a writer, then a scope // if there already is a scope, the writer will attach to it // otherwise, it will only exist here - cheap - using (_contentStore.GetWriter(_scopeProvider)) + using (_contentStore.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.ContentTree); @@ -401,7 +401,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadMedia(Action action) { // see note in LockAndLoadContent - using (_mediaStore.GetWriter(_scopeProvider)) + using (_mediaStore.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.MediaTree); @@ -529,7 +529,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private void LockAndLoadDomains() { // see note in LockAndLoadContent - using (_domainStore.GetWriter(_scopeProvider)) + using (_domainStore.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.Domains); @@ -586,7 +586,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - using (_contentStore.GetWriter(_scopeProvider)) + using (_contentStore.GetScopedWriteLock(_scopeProvider)) { NotifyLocked(payloads, out bool draftChanged2, out bool publishedChanged2); draftChanged = draftChanged2; @@ -682,7 +682,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - using (_mediaStore.GetWriter(_scopeProvider)) + using (_mediaStore.GetScopedWriteLock(_scopeProvider)) { NotifyLocked(payloads, out bool anythingChanged2); anythingChanged = anythingChanged2; @@ -803,7 +803,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (removedIds.Count == 0 && refreshedIds.Count == 0 && otherIds.Count == 0 && newIds.Count == 0) return; - using (store.GetWriter(_scopeProvider)) + using (store.GetScopedWriteLock(_scopeProvider)) { // ReSharper disable AccessToModifiedClosure action(removedIds, refreshedIds, otherIds, newIds); @@ -824,8 +824,8 @@ namespace Umbraco.Web.PublishedCache.NuCache payload.Removed ? "Removed" : "Refreshed", payload.Id); - using (_contentStore.GetWriter(_scopeProvider)) - using (_mediaStore.GetWriter(_scopeProvider)) + using (_contentStore.GetScopedWriteLock(_scopeProvider)) + using (_mediaStore.GetScopedWriteLock(_scopeProvider)) { // TODO: need to add a datatype lock // this is triggering datatypes reload in the factory, and right after we create some @@ -858,7 +858,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return; // see note in LockAndLoadContent - using (_domainStore.GetWriter(_scopeProvider)) + using (_domainStore.GetScopedWriteLock(_scopeProvider)) { foreach (var payload in payloads) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs index 73904d1452..c5b1df1206 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/SnapDictionary.cs @@ -87,13 +87,13 @@ namespace Umbraco.Web.PublishedCache.NuCache } // a scope contextual that represents a locked writer to the dictionary - private class SnapDictionaryWriter : ScopeContextualBase + private class ScopedWriteLock : ScopeContextualBase { private readonly WriteLockInfo _lockinfo = new WriteLockInfo(); private readonly SnapDictionary _dictionary; private int _released; - public SnapDictionaryWriter(SnapDictionary dictionary, bool scoped) + public ScopedWriteLock(SnapDictionary dictionary, bool scoped) { _dictionary = dictionary; dictionary.Lock(_lockinfo, scoped); @@ -108,14 +108,12 @@ namespace Umbraco.Web.PublishedCache.NuCache } // gets a scope contextual representing a locked writer to the dictionary - // fixme GetScopedWriter? should the dict have a ref onto the scope provider? - // fixme this is not a "writer" but a "write lock" => rename GetWriteLock // the dict is write-locked until the write-lock is released // which happens when it is disposed (non-scoped) // or when the scope context exits (scoped) - public IDisposable GetWriter(IScopeProvider scopeProvider) + public IDisposable GetScopedWriteLock(IScopeProvider scopeProvider) { - return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new SnapDictionaryWriter(this, scoped)); + return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } private void Lock(WriteLockInfo lockInfo, bool forceGen = false) @@ -143,9 +141,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { // because we are changing things, a new generation // is created, which will trigger a new snapshot - _nextGen = true; // this is the ONLY place where _nextGen becomes true - _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); + if (_nextGen) + _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _liveGen += 1; + _nextGen = true; // this is the ONLY place where _nextGen becomes true } } } @@ -197,9 +196,6 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - // fixme - pretend we need to do something that takes time - //System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); - // decrement the lock count, if counting, then exit the lock if (lockInfo.Count) _wlocked--; Monitor.Exit(_wlocko); From 688173488d3af4632ea9b7088a3312eb63d84d62 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 15 Mar 2019 09:26:21 +0100 Subject: [PATCH 031/130] Automatically close "Cultures and Hostnames" on successful save --- .../src/views/content/content.assigndomain.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js index 0f27f3046c..165aec63cd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js @@ -116,6 +116,7 @@ if(response.valid) { vm.submitButtonState = "success"; + closeDialog(); // show validation messages for each domain } else { From 8f06603774468e975f3c0faf8f9a371aa85d1216 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 17 Mar 2019 20:51:42 +0100 Subject: [PATCH 032/130] Fix the "Language" dropdown in the "Culture and Hostnames" dialog --- .../src/views/content/content.assigndomain.controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js index 0f27f3046c..5fa1eebd0b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.assigndomain.controller.js @@ -46,8 +46,7 @@ if (data.language !== "undefined") { var lang = vm.languages.filter(function (l) { - return matchLanguageById(l, data.language.Id); - + return matchLanguageById(l, data.language); }); if (lang.length > 0) { vm.language = lang[0]; From 44beb81e719703b0b3a693def58dba70d665346b Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 18 Mar 2019 09:51:05 +0100 Subject: [PATCH 033/130] Avoid removing a public method (used by forms 8.0.0) --- src/Umbraco.Web/Editors/EditorModelEventManager.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs index b61a359e83..40c45ca8c3 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.ComponentModel; using System.Web.Http.Filters; using Umbraco.Core.Dashboards; using Umbraco.Core.Events; @@ -15,11 +17,15 @@ namespace Umbraco.Web.Editors public static event TypedEventHandler> SendingMediaModel; public static event TypedEventHandler> SendingMemberModel; public static event TypedEventHandler> SendingUserModel; - public static event TypedEventHandler>>> SendingDashboardModel; + + [Obsolete("Please Use SendingDashboardModelV2")] + [EditorBrowsable(EditorBrowsableState.Never)] + public static event TypedEventHandler>>> SendingDashboardModel; + public static event TypedEventHandler>>> SendingDashboardModelV2; private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs>> e) { - var handler = SendingDashboardModel; + var handler = SendingDashboardModelV2; handler?.Invoke(sender, e); } From 212b0ba93226988c8c904929bf260f320b22dcf3 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 18 Mar 2019 10:14:23 +0100 Subject: [PATCH 034/130] Missing injection --- src/Umbraco.Web.UI.Client/package-lock.json | 471 +++++------------- .../dashboard/dashboard.tabs.controller.js | 2 +- 2 files changed, 131 insertions(+), 342 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 487c50bddb..b996db214c 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -937,7 +937,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -955,7 +955,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1104,7 +1104,6 @@ "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", "dev": true, - "optional": true, "requires": { "file-type": "^3.1.0" }, @@ -1113,8 +1112,7 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true } } }, @@ -1172,7 +1170,7 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", "dev": true }, "array-sort": { @@ -1218,7 +1216,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1271,7 +1269,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -1525,7 +1523,6 @@ "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1535,15 +1532,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1559,7 +1554,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1748,8 +1742,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "optional": true + "dev": true }, "buffer-fill": { "version": "1.0.0", @@ -1768,7 +1761,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", "dev": true, - "optional": true, "requires": { "file-type": "^3.1.0", "readable-stream": "^2.0.2", @@ -1780,22 +1772,19 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1811,7 +1800,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1820,15 +1808,13 @@ "version": "2.0.3", "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true, - "optional": true + "dev": true }, "vinyl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, - "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -1949,8 +1935,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true, - "optional": true + "dev": true }, "caseless": { "version": "0.12.0", @@ -1963,7 +1948,6 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", "dev": true, - "optional": true, "requires": { "get-proxy": "^1.0.1", "is-obj": "^1.0.0", @@ -1975,8 +1959,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true + "dev": true } } }, @@ -2237,8 +2220,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true, - "optional": true + "dev": true }, "coa": { "version": "2.0.1", @@ -2334,7 +2316,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, - "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -2398,7 +2379,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2478,7 +2459,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "continuable-cache": { @@ -2521,7 +2502,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=", "dev": true }, "core-util-is": { @@ -2547,7 +2528,6 @@ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, - "optional": true, "requires": { "capture-stack-trace": "^1.0.0" } @@ -2802,7 +2782,6 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", "dev": true, - "optional": true, "requires": { "buffer-to-vinyl": "^1.0.0", "concat-stream": "^1.4.6", @@ -2820,7 +2799,6 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, - "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -2829,15 +2807,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true, - "optional": true + "dev": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, - "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -2849,7 +2825,6 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, - "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -2859,7 +2834,6 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2869,7 +2843,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, - "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -2883,7 +2856,6 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, - "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -2899,15 +2871,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -2919,15 +2889,13 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "optional": true + "dev": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, - "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -2939,22 +2907,19 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true, - "optional": true + "dev": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2963,15 +2928,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -2981,7 +2944,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, - "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -3002,15 +2964,13 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, - "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -3021,7 +2981,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3037,7 +2996,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3047,7 +3005,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3057,7 +3014,6 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, - "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3068,7 +3024,6 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, - "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3079,7 +3034,6 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, - "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3092,7 +3046,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, - "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3104,7 +3057,6 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, - "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3132,7 +3084,6 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", "dev": true, - "optional": true, "requires": { "is-tar": "^1.0.0", "object-assign": "^2.0.0", @@ -3146,22 +3097,19 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3174,7 +3122,6 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, - "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3185,7 +3132,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, - "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3198,7 +3144,6 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", "dev": true, - "optional": true, "requires": { "is-bzip2": "^1.0.0", "object-assign": "^2.0.0", @@ -3213,22 +3158,19 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3241,7 +3183,6 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, - "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3252,7 +3193,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, - "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3265,7 +3205,6 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", "dev": true, - "optional": true, "requires": { "is-gzip": "^1.0.0", "object-assign": "^2.0.0", @@ -3279,22 +3218,19 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3307,7 +3243,6 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, - "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3318,7 +3253,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, - "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3331,7 +3265,6 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", "dev": true, - "optional": true, "requires": { "is-zip": "^1.0.0", "read-all-stream": "^3.0.0", @@ -3347,7 +3280,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, - "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3360,8 +3292,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "optional": true + "dev": true }, "deep-is": { "version": "0.1.3", @@ -3494,7 +3425,7 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3560,7 +3491,6 @@ "resolved": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", "dev": true, - "optional": true, "requires": { "caw": "^1.0.1", "concat-stream": "^1.4.7", @@ -3584,7 +3514,6 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, - "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -3593,15 +3522,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true, - "optional": true + "dev": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, - "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -3613,7 +3540,6 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, - "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -3623,7 +3549,6 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3633,7 +3558,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, - "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -3647,7 +3571,6 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, - "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -3663,15 +3586,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3683,15 +3604,13 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "optional": true + "dev": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, - "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3703,22 +3622,19 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true, - "optional": true + "dev": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3727,15 +3643,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -3745,7 +3659,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, - "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -3766,15 +3679,13 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, - "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -3785,7 +3696,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3801,7 +3711,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3811,7 +3720,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3821,7 +3729,6 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, - "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3832,7 +3739,6 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, - "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3843,7 +3749,6 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, - "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3856,7 +3761,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, - "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3868,7 +3772,6 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, - "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3911,7 +3814,6 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, - "optional": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -3924,7 +3826,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, - "optional": true, "requires": { "once": "^1.4.0" } @@ -3933,15 +3834,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3951,7 +3850,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3967,7 +3865,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3979,7 +3876,6 @@ "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", "dev": true, - "optional": true, "requires": { "onetime": "^1.0.0", "set-immediate-shim": "^1.0.0" @@ -3989,8 +3885,7 @@ "version": "1.1.0", "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4058,7 +3953,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4094,7 +3989,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4258,7 +4153,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -4353,7 +4248,7 @@ "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4363,13 +4258,13 @@ "eslint-utils": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", "dev": true }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", "dev": true }, "espree": { @@ -4392,7 +4287,7 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", "dev": true, "requires": { "estraverse": "^4.0.0" @@ -4401,7 +4296,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -4461,7 +4356,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { @@ -4676,7 +4571,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -4930,7 +4825,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "optional": true, "requires": { "pend": "~1.2.0" } @@ -4978,15 +4872,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=", - "dev": true, - "optional": true + "dev": true }, "filenamify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", "dev": true, - "optional": true, "requires": { "filename-reserved-regex": "^1.0.0", "strip-outer": "^1.0.0", @@ -5233,8 +5125,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true, - "optional": true + "dev": true }, "fs-extra": { "version": "1.0.0", @@ -5298,8 +5189,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5320,14 +5210,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5342,20 +5230,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5472,8 +5357,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5485,7 +5369,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5500,7 +5383,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5508,14 +5390,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5534,7 +5414,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5615,8 +5494,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5628,7 +5506,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5714,8 +5591,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5751,7 +5627,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5771,7 +5646,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5815,14 +5689,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -5858,7 +5730,6 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", "dev": true, - "optional": true, "requires": { "rc": "^1.1.2" } @@ -6020,7 +5891,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6165,7 +6036,6 @@ "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true, - "optional": true, "requires": { "create-error-class": "^3.0.1", "duplexer2": "^0.1.4", @@ -6189,7 +6059,6 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -6198,22 +6067,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, - "optional": true, "requires": { "error-ex": "^1.2.0" } @@ -6223,7 +6089,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6239,7 +6104,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6259,8 +6123,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true, - "optional": true + "dev": true }, "growly": { "version": "1.3.0", @@ -6556,7 +6419,6 @@ "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", "dev": true, - "optional": true, "requires": { "archive-type": "^3.0.0", "decompress": "^3.0.0", @@ -6568,15 +6430,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6592,7 +6452,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6602,7 +6461,7 @@ "gulp-eslint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz", - "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", + "integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=", "dev": true, "requires": { "eslint": "^5.0.1", @@ -7210,7 +7069,6 @@ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", "dev": true, - "optional": true, "requires": { "convert-source-map": "^1.1.1", "graceful-fs": "^4.1.2", @@ -7223,15 +7081,13 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true, - "optional": true + "dev": true }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -7241,7 +7097,6 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, - "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -7560,7 +7415,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7711,7 +7566,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -8005,7 +7860,7 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -8072,8 +7927,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", "integrity": "sha1-XuWOqlounIDiFAe+3yOuWsCRs/w=", - "dev": true, - "optional": true + "dev": true }, "is-callable": { "version": "1.1.4", @@ -8208,8 +8062,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", - "dev": true, - "optional": true + "dev": true }, "is-jpg": { "version": "1.0.1", @@ -8222,8 +8075,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", "integrity": "sha1-fUxXKDd+84bD4ZSpkRv1fG3DNec=", - "dev": true, - "optional": true + "dev": true }, "is-number": { "version": "3.0.0", @@ -8289,8 +8141,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true, - "optional": true + "dev": true }, "is-regex": { "version": "1.0.4", @@ -8304,7 +8155,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -8313,15 +8164,14 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true, - "optional": true + "dev": true }, "is-stream": { "version": "1.1.0", @@ -8351,8 +8201,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", "integrity": "sha1-L2suF5LB9bs2UZrKqdZcDSb+hT0=", - "dev": true, - "optional": true + "dev": true }, "is-typedarray": { "version": "1.0.0", @@ -8363,7 +8212,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -8373,8 +8222,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true, - "optional": true + "dev": true }, "is-utf8": { "version": "0.2.1", @@ -8386,8 +8234,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", - "dev": true, - "optional": true + "dev": true }, "is-windows": { "version": "1.0.2", @@ -8405,8 +8252,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", "integrity": "sha1-R7Co/004p2QxzP2ZqOFaTIa6IyU=", - "dev": true, - "optional": true + "dev": true }, "isarray": { "version": "0.0.1", @@ -8523,7 +8369,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -8650,7 +8496,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -8737,7 +8583,6 @@ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.0.5" }, @@ -8746,15 +8591,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8770,7 +8613,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -9076,8 +8918,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true, - "optional": true + "dev": true }, "lodash.isobject": { "version": "2.4.1", @@ -9274,8 +9115,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true, - "optional": true + "dev": true }, "lpad-align": { "version": "1.1.2", @@ -9316,7 +9156,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9497,7 +9337,7 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimatch": { @@ -9671,8 +9511,7 @@ "version": "1.0.0", "resolved": "http://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", - "dev": true, - "optional": true + "dev": true }, "node.extend": { "version": "1.1.8", @@ -12722,8 +12561,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true + "dev": true }, "p-pipe": { "version": "1.2.0", @@ -13027,7 +12865,7 @@ "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", "dev": true }, "posix-character-classes": { @@ -13434,8 +13272,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true, - "optional": true + "dev": true }, "preserve": { "version": "0.2.0", @@ -13518,7 +13355,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -13567,7 +13404,6 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, - "optional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -13580,7 +13416,6 @@ "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", "dev": true, - "optional": true, "requires": { "pinkie-promise": "^2.0.0", "readable-stream": "^2.0.0" @@ -13590,15 +13425,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13614,7 +13447,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -13719,7 +13551,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -14217,7 +14049,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", "dev": true }, "sax": { @@ -14231,7 +14063,6 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, - "optional": true, "requires": { "commander": "~2.8.1" } @@ -14377,8 +14208,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true, - "optional": true + "dev": true }, "set-value": { "version": "2.0.0", @@ -14406,7 +14236,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "shebang-command": { @@ -14696,7 +14526,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14883,8 +14713,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", - "dev": true, - "optional": true + "dev": true }, "static-extend": { "version": "0.1.2", @@ -14928,7 +14757,6 @@ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, - "optional": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -14939,7 +14767,6 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -14948,15 +14775,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -14972,7 +14797,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14982,20 +14806,19 @@ "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", + "integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=", "dev": true }, "stream-shift": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true, - "optional": true + "dev": true }, "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", "dev": true, "requires": { "date-format": "^1.2.0", @@ -15022,7 +14845,7 @@ "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -15037,7 +14860,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -15054,7 +14877,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -15168,7 +14991,6 @@ "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "dev": true, - "optional": true, "requires": { "chalk": "^1.0.0", "get-stdin": "^4.0.1", @@ -15182,15 +15004,13 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true + "dev": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, - "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15204,7 +15024,6 @@ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", "integrity": "sha1-hHSREZ/MtftDYhfMc39/qtUPYD8=", "dev": true, - "optional": true, "requires": { "is-relative": "^0.1.0" } @@ -15213,15 +15032,13 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", "integrity": "sha1-kF/uiuhvRbPsYUvDwVyGnfCHboI=", - "dev": true, - "optional": true + "dev": true }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true + "dev": true } } }, @@ -15252,7 +15069,6 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15286,7 +15102,6 @@ "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", "dev": true, - "optional": true, "requires": { "chalk": "^1.0.0" }, @@ -15295,15 +15110,13 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true + "dev": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, - "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15316,8 +15129,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true + "dev": true } } }, @@ -15379,7 +15191,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15395,7 +15206,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, - "optional": true, "requires": { "once": "^1.4.0" } @@ -15404,15 +15214,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -15422,7 +15230,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15438,7 +15245,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15537,7 +15343,6 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, - "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -15562,8 +15367,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", - "dev": true, - "optional": true + "dev": true }, "timsort": { "version": "0.3.0", @@ -15615,7 +15419,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -15626,7 +15430,6 @@ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", "dev": true, - "optional": true, "requires": { "extend-shallow": "^2.0.1" }, @@ -15636,7 +15439,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -15653,8 +15455,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true, - "optional": true + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -15733,7 +15534,6 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15783,7 +15583,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15825,7 +15625,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -15982,19 +15782,18 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", - "dev": true, - "optional": true + "dev": true }, "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { "punycode": "^2.1.0" @@ -16011,7 +15810,6 @@ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, - "optional": true, "requires": { "prepend-http": "^1.0.1" } @@ -16097,8 +15895,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "dev": true, - "optional": true + "dev": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -16143,7 +15940,6 @@ "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", "dev": true, - "optional": true, "requires": { "object-assign": "^4.0.1", "readable-stream": "^2.0.0" @@ -16153,22 +15949,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -16184,7 +15977,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -16324,7 +16116,6 @@ "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", "dev": true, - "optional": true, "requires": { "wrap-fn": "^0.1.0" } @@ -16415,7 +16206,6 @@ "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", "integrity": "sha1-8htuQQFv9KfjFyDbxjoJAWvfmEU=", "dev": true, - "optional": true, "requires": { "co": "3.1.0" } @@ -16438,7 +16228,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { "async-limiter": "~1.0.0", @@ -16519,7 +16309,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index aed4bad87a..a35a404c24 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -22,7 +22,7 @@ function startUpVideosDashboardController($scope, dashboardResource) { angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController); -function startUpDynamicContentController($timeout, $scope, dashboardResource, assetsService, tourService, eventsService) { +function startUpDynamicContentController($q, $timeout, $scope, dashboardResource, assetsService, tourService, eventsService) { var vm = this; var evts = []; From 6e2c143d0060b1f56dc13e674c8075762a1512c1 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 18 Mar 2019 12:16:20 +0100 Subject: [PATCH 035/130] Improve the filtering in the datatype picker --- .../datatypepicker.controller.js | 23 +++++++++++++++---- .../datatypepicker/datatypepicker.html | 18 +++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.controller.js index 9836b72468..c86f55b255 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.controller.js @@ -10,7 +10,7 @@ (function() { "use strict"; - function DataTypePicker($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) { + function DataTypePicker($scope, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) { var vm = this; @@ -119,13 +119,28 @@ $scope.model.itemDetails = null; if (vm.searchTerm) { - vm.showFilterResult = true; vm.showTabs = false; + + var regex = new RegExp(vm.searchTerm, "i"); + vm.filterResult = { + userConfigured: filterCollection(vm.userConfigured, regex), + typesAndEditors: filterCollection(vm.typesAndEditors, regex) + }; } else { - vm.showFilterResult = false; + vm.filterResult = null; vm.showTabs = true; } + } + function filterCollection(collection, regex) { + return _.map(_.keys(collection), function (key) { + return { + group: key, + dataTypes: $filter('filter')(collection[key], function (dataType) { + return regex.test(dataType.name) || regex.test(dataType.alias); + }) + } + }); } function showDetailsOverlay(property) { @@ -201,4 +216,4 @@ angular.module("umbraco").controller("Umbraco.Editors.DataTypePickerController", DataTypePicker); -})(); \ No newline at end of file +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html index f3f991c63e..43933f8051 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypepicker/datatypepicker.html @@ -79,13 +79,13 @@
-
+
-
-
-
{{key}}
+
+
+
{{result.group}}
    -
  • @@ -101,11 +101,11 @@
-
-
-
{{key}}
+
+
+
{{result.group}}