From 4b4f571d143c584637d9da255d22a77910cb3582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Fri, 23 Aug 2019 15:18:53 +0200 Subject: [PATCH 001/109] Support for Segments in ContentTypeRepositoryBase --- .../Implement/ContentTypeRepositoryBase.cs | 187 +++++++----------- 1 file changed, 75 insertions(+), 112 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index f2efb03ba4..4428f35de7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -1,4 +1,5 @@ -using System; + +using System; using System.Collections.Generic; using System.Data; using System.Globalization; @@ -410,24 +411,8 @@ AND umbracoNode.id <> @id", // note: this only deals with *local* property types, we're dealing w/compositions later below foreach (var propertyType in entity.PropertyTypes) { - if (contentTypeVariationChanging) - { - // content type is changing - switch (newContentTypeVariation) - { - case ContentVariation.Nothing: // changing to Nothing - // all property types must change to Nothing - propertyType.Variations = ContentVariation.Nothing; - break; - case ContentVariation.Culture: // changing to Culture - // all property types can remain Nothing - break; - case ContentVariation.CultureAndSegment: - case ContentVariation.Segment: - default: - throw new NotSupportedException(); // TODO: Support this - } - } + // Update property variations + propertyType.Variations = newContentTypeVariation & propertyType.Variations; // then, track each property individually if (propertyType.IsPropertyDirty("Variations")) @@ -455,23 +440,17 @@ AND umbracoNode.id <> @id", // via composition, with their original variations (ie not filtered by this // content type variations - we need this true value to make decisions. + propertyTypeVariationChanges = propertyTypeVariationChanges ?? new Dictionary(); + foreach (var propertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes) { - if (propertyType.VariesBySegment() || newContentTypeVariation.VariesBySegment()) - throw new NotSupportedException(); // TODO: support this + if (propertyType.Variations == ContentVariation.Nothing) continue; - if (propertyType.Variations == ContentVariation.Culture) - { - if (propertyTypeVariationChanges == null) - propertyTypeVariationChanges = new Dictionary(); + var target = newContentTypeVariation & propertyType.Variations; - // if content type moves to Culture, property type becomes Culture here again - // if content type moves to Nothing, property type becomes Nothing here - if (newContentTypeVariation == ContentVariation.Culture) - propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Nothing, ContentVariation.Culture); - else if (newContentTypeVariation == ContentVariation.Nothing) - propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Culture, ContentVariation.Nothing); - } + // if content type moves to a different variant, property type becomes Culture here again + // if content type moves to Nothing, property type becomes Nothing here + propertyTypeVariationChanges[propertyType.Id] = (propertyType.Variations, target); } } @@ -512,7 +491,7 @@ AND umbracoNode.id <> @id", var impacted = GetImpactedContentTypes(entity, all); // if some property types have actually changed, move their variant data - if (propertyTypeVariationChanges != null) + if (propertyTypeVariationChanges?.Count > 0) MovePropertyTypeVariantData(propertyTypeVariationChanges, impacted); // deal with orphan properties: those that were in a deleted tab, @@ -661,27 +640,24 @@ AND umbracoNode.id <> @id", var impactedL = impacted.Select(x => x.Id).ToList(); //Group by the "To" variation so we can bulk update in the correct batches - foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation)) + foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value)) { var propertyTypeIds = grouping.Select(x => x.Key).ToList(); - var toVariation = grouping.Key; + var (FromVariation, ToVariation) = grouping.Key; - switch (toVariation) + if(!FromVariation.HasFlag(ContentVariation.Culture) && + ToVariation.HasFlag(ContentVariation.Culture)) { - case ContentVariation.Culture: - CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL); - CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL); - RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); - break; - case ContentVariation.Nothing: - CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL); - CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL); - RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); - break; - case ContentVariation.CultureAndSegment: - case ContentVariation.Segment: - default: - throw new NotSupportedException(); // TODO: Support this + CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL); + CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL); + RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); + } + else if (FromVariation.HasFlag(ContentVariation.Culture) && + !ToVariation.HasFlag(ContentVariation.Culture)) + { + CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL); + CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL); + RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); } } } @@ -693,78 +669,65 @@ AND umbracoNode.id <> @id", { var defaultLanguageId = GetDefaultLanguageId(); - switch (toVariation) + var cultureIsNotEnabled = !fromVariation.HasFlag(ContentVariation.Culture); + var cultureWillBeEnabled = toVariation.HasFlag(ContentVariation.Culture); + + if(cultureIsNotEnabled && cultureWillBeEnabled) { - case ContentVariation.Culture: + //move the names + //first clear out any existing names that might already exists under the default lang + //there's 2x tables to update - //move the names - //first clear out any existing names that might already exists under the default lang - //there's 2x tables to update + //clear out the versionCultureVariation table + var sqlSelect = Sql().Select(x => x.Id) + .From() + .InnerJoin().On(x => x.Id, x => x.VersionId) + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id) + .Where(x => x.LanguageId == defaultLanguageId); + var sqlDelete = Sql() + .Delete() + .WhereIn(x => x.Id, sqlSelect); - //clear out the versionCultureVariation table - var sqlSelect = Sql().Select(x => x.Id) - .From() - .InnerJoin().On(x => x.Id, x => x.VersionId) - .InnerJoin().On(x => x.NodeId, x => x.NodeId) - .Where(x => x.ContentTypeId == contentType.Id) - .Where(x => x.LanguageId == defaultLanguageId); - var sqlDelete = Sql() - .Delete() - .WhereIn(x => x.Id, sqlSelect); + Database.Execute(sqlDelete); - Database.Execute(sqlDelete); + //clear out the documentCultureVariation table + sqlSelect = Sql().Select(x => x.Id) + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id) + .Where(x => x.LanguageId == defaultLanguageId); + sqlDelete = Sql() + .Delete() + .WhereIn(x => x.Id, sqlSelect); - //clear out the documentCultureVariation table - sqlSelect = Sql().Select(x => x.Id) - .From() - .InnerJoin().On(x => x.NodeId, x => x.NodeId) - .Where(x => x.ContentTypeId == contentType.Id) - .Where(x => x.LanguageId == defaultLanguageId); - sqlDelete = Sql() - .Delete() - .WhereIn(x => x.Id, sqlSelect); + Database.Execute(sqlDelete); - Database.Execute(sqlDelete); + //now we need to insert names into these 2 tables based on the invariant data - //now we need to insert names into these 2 tables based on the invariant data + //insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang + var cols = Sql().Columns(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId); + sqlSelect = Sql().Select(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate) + .Append($", {defaultLanguageId}") //default language ID + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id); + var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect); - //insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang - var cols = Sql().Columns(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId); - sqlSelect = Sql().Select(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate) - .Append($", {defaultLanguageId}") //default language ID - .From() - .InnerJoin().On(x => x.NodeId, x => x.NodeId) - .Where(x => x.ContentTypeId == contentType.Id); - var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect); + Database.Execute(sqlInsert); - Database.Execute(sqlInsert); + //insert rows into the documentCultureVariation table + cols = Sql().Columns(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId); + sqlSelect = Sql().Select(x => x.NodeId, x => x.Edited, x => x.Published) + .AndSelect(x => x.Text) + .Append($", 1, {defaultLanguageId}") //make Available + default language ID + .From() + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .InnerJoin().On(x => x.NodeId, x => x.NodeId) + .Where(x => x.ContentTypeId == contentType.Id); + sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect); - //insert rows into the documentCultureVariation table - cols = Sql().Columns(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId); - sqlSelect = Sql().Select(x => x.NodeId, x => x.Edited, x => x.Published) - .AndSelect(x => x.Text) - .Append($", 1, {defaultLanguageId}") //make Available + default language ID - .From() - .InnerJoin().On(x => x.NodeId, x => x.NodeId) - .InnerJoin().On(x => x.NodeId, x => x.NodeId) - .Where(x => x.ContentTypeId == contentType.Id); - sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect); - - Database.Execute(sqlInsert); - - break; - case ContentVariation.Nothing: - - //we don't need to move the names! this is because we always keep the invariant names with the name of the default language. - - //however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :( - // if we want these SQL statements back, look into GIT history - - break; - case ContentVariation.CultureAndSegment: - case ContentVariation.Segment: - default: - throw new NotSupportedException(); // TODO: Support this + Database.Execute(sqlInsert); } } From 69d97bc984fab59e38f0a77841a73a5607cfb002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 2 Oct 2019 10:28:26 +0200 Subject: [PATCH 002/109] first step for changing the datatype edit view --- .../datatypes/datatype.edit.controller.js | 238 ++++++++++-------- .../src/views/datatypes/edit.html | 104 +++----- .../datatypes/views/datatype.relations.html | 34 +++ .../datatypes/views/datatype.settings.html | 34 +++ .../views/macros/macros.edit.controller.js | 32 +-- 5 files changed, 236 insertions(+), 206 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 6282ecb0b6..7ade362473 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -6,35 +6,17 @@ * @description * The controller for the content editor */ -function DataTypeEditController($scope, $routeParams, appState, navigationService, dataTypeResource, serverValidationManager, contentEditingHelper, formHelper, editorState, dataTypeHelper, eventsService) { - - //setup scope vars - $scope.page = {}; - $scope.page.loading = false; - $scope.page.nameLocked = false; - $scope.page.menu = {}; - $scope.page.menu.currentSection = appState.getSectionState("currentSection"); - $scope.page.menu.currentNode = null; +function DataTypeEditController($scope, $routeParams, appState, navigationService, dataTypeResource, serverValidationManager, contentEditingHelper, formHelper, editorState, dataTypeHelper, eventsService, localizationService) { + var evts = []; - - //method used to configure the pre-values when we retrieve them from the server - function createPreValueProps(preVals) { - $scope.preValues = []; - for (var i = 0; i < preVals.length; i++) { - $scope.preValues.push({ - hideLabel: preVals[i].hideLabel, - alias: preVals[i].key, - description: preVals[i].description, - label: preVals[i].label, - view: preVals[i].view, - value: preVals[i].value, - config: preVals[i].config - }); - } - } - + var vm = this; + + //setup scope vars + vm.page = {}; + vm.page.loading = false; + //set up the standard data type props - $scope.properties = { + vm.properties = { selectedEditor: { alias: "selectedEditor", description: "Select a property editor", @@ -47,52 +29,51 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic }; //setup the pre-values as props - $scope.preValues = []; - - if ($routeParams.create) { - - $scope.page.loading = true; - $scope.showIdentifier = false; - - //we are creating so get an empty data type item - dataTypeResource.getScaffold($routeParams.id) - .then(function(data) { - - $scope.preValuesLoaded = true; - $scope.content = data; - - setHeaderNameState($scope.content); - - //set a shared state - editorState.set($scope.content); - - $scope.page.loading = false; - + vm.preValues = []; + + + //method used to configure the pre-values when we retrieve them from the server + function createPreValueProps(preVals) { + vm.preValues = []; + for (var i = 0; i < preVals.length; i++) { + vm.preValues.push({ + hideLabel: preVals[i].hideLabel, + alias: preVals[i].key, + description: preVals[i].description, + label: preVals[i].label, + view: preVals[i].view, + value: preVals[i].value, + config: preVals[i].config }); + } } - else { - loadDataType(); + + + function setHeaderNameState(content) { + if(content.isSystem == 1) { + $scope.page.nameLocked = true; + } } - + + function loadDataType() { - $scope.page.loading = true; - - $scope.showIdentifier = true; + vm.page.loading = true; + vm.showIdentifier = true; //we are editing so get the content item from the server dataTypeResource.getById($routeParams.id) .then(function(data) { - $scope.preValuesLoaded = true; - $scope.content = data; + vm.preValuesLoaded = true; + vm.content = data; - createPreValueProps($scope.content.preValues); + createPreValueProps(vm.content.preValues); - setHeaderNameState($scope.content); + setHeaderNameState(vm.content); //share state - editorState.set($scope.content); + editorState.set(vm.content); //in one particular special case, after we've created a new item we redirect back to the edit // route but there might be server validation errors in the collection which we need to display @@ -101,49 +82,21 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic serverValidationManager.notifyAndClearAllSubscriptions(); navigationService.syncTree({ tree: "datatypes", path: data.path }).then(function (syncArgs) { - $scope.page.menu.currentNode = syncArgs.node; + vm.page.menu.currentNode = syncArgs.node; }); - $scope.page.loading = false; + vm.page.loading = false; }); } - $scope.$watch("content.selectedEditor", function (newVal, oldVal) { - - //when the value changes, we need to dynamically load in the new editor - if (newVal && (newVal != oldVal && (oldVal || $routeParams.create))) { - //we are editing so get the content item from the server - var currDataTypeId = $routeParams.create ? undefined : $routeParams.id; - dataTypeResource.getPreValues(newVal, currDataTypeId) - .then(function (data) { - $scope.preValuesLoaded = true; - $scope.content.preValues = data; - createPreValueProps($scope.content.preValues); - - setHeaderNameState($scope.content); - - //share state - editorState.set($scope.content); - }); - } - }); - - function setHeaderNameState(content) { - - if(content.isSystem == 1) { - $scope.page.nameLocked = true; - } - - } - - $scope.save = function() { + function saveDataType() { if (formHelper.submitForm({ scope: $scope })) { - $scope.page.saveButtonState = "busy"; + vm.page.saveButtonState = "busy"; - dataTypeResource.save($scope.content, $scope.preValues, $routeParams.create) + dataTypeResource.save(vm.content, vm.preValues, $routeParams.create) .then(function(data) { formHelper.resetForm({ scope: $scope }); @@ -156,18 +109,18 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic } }); - setHeaderNameState($scope.content); + setHeaderNameState(vm.content); //share state - editorState.set($scope.content); + editorState.set(vm.content); navigationService.syncTree({ tree: "datatypes", path: data.path, forceReload: true }).then(function (syncArgs) { - $scope.page.menu.currentNode = syncArgs.node; + vm.page.menu.currentNode = syncArgs.node; }); - $scope.page.saveButtonState = "success"; + vm.page.saveButtonState = "success"; - dataTypeHelper.rebindChangedProperties($scope.content, data); + dataTypeHelper.rebindChangedProperties(vm.content, data); }, function(err) { @@ -177,25 +130,108 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic err: err }); - $scope.page.saveButtonState = "error"; + vm.page.saveButtonState = "error"; //share state - editorState.set($scope.content); + editorState.set(vm.content); }); } }; - + + vm.save = saveDataType; + evts.push(eventsService.on("app.refreshEditor", function(name, error) { loadDataType(); })); - + //ensure to unregister from all events! $scope.$on('$destroy', function () { for (var e in evts) { eventsService.unsubscribe(evts[e]); } }); + + + + + + + + + function init() { + + $scope.$watch("vm.content.selectedEditor", function (newVal, oldVal) { + + //when the value changes, we need to dynamically load in the new editor + if (newVal && (newVal != oldVal && (oldVal || $routeParams.create))) { + //we are editing so get the content item from the server + var currDataTypeId = $routeParams.create ? undefined : $routeParams.id; + dataTypeResource.getPreValues(newVal, currDataTypeId) + .then(function (data) { + vm.preValuesLoaded = true; + vm.content.preValues = data; + createPreValueProps(vm.content.preValues); + + setHeaderNameState(vm.content); + + //share state + editorState.set(vm.content); + }); + } + }); + + if ($routeParams.create) { + + vm.page.loading = true; + vm.showIdentifier = false; + + //we are creating so get an empty data type item + dataTypeResource.getScaffold($routeParams.id) + .then(function(data) { + + vm.preValuesLoaded = true; + vm.content = data; + + setHeaderNameState(vm.content); + + //set a shared state + editorState.set(vm.content); + + vm.page.loading = false; + + }); + } + else { + loadDataType(); + } + + var labelKeys = [ + "general_settings", + "macro_parameters" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + + vm.page.navigation = [ + { + "name": values[0], + "alias": "settings", + "icon": "icon-settings", + "view": "views/datatypes/views/datatype.settings.html", + "active": true + }, + { + "name": values[1], + "alias": "parameters", + "icon": "icon-list", + "view": "views/datatypes/views/datatype.relations.html" + } + ]; + }); + } + + init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html index 2f061de83a..0bb2b01e31 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html @@ -1,81 +1,33 @@ -
+
- +
+ + + + - + + + + - + + + + + + + + - - - - - - - - - - - -
{{content.id}}
- {{content.key}} -
- - -
- - - Required - -
- -
- - -
{{content.selectedEditor}}
-
- - - - - -
- -
- -
- - - - - - - - - - - - - -
- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html new file mode 100644 index 0000000000..43266ddafc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -0,0 +1,34 @@ + + + + + +
{{vm.content.id}}
+ {{vm.content.key}} +
+ + +
+ + + Required + +
+ +
+ + +
{{vm.content.selectedEditor}}
+
+ + + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html new file mode 100644 index 0000000000..d5d49ca8fb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html @@ -0,0 +1,34 @@ + + + + + +
{{content.id}}
+ {{content.key}} +
+ + +
+ + + Required + +
+ +
+ + +
{{content.selectedEditor}}
+
+ + + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js index 79d837e516..dd0dd7499f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js @@ -108,36 +108,10 @@ function MacrosEditController($scope, $q, $routeParams, macroResource, editorSta function init() { vm.page.loading = true; - - vm.promises['partialViews'] = getPartialViews(); - vm.promises['parameterEditors'] = getParameterEditors(); - vm.promises['macro'] = getMacro(); - + vm.views = []; vm.node = null; - - $q.all(vm.promises).then(function (values) { - var keys = Object.keys(values); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - - if (key === 'partialViews') { - vm.views = values[key]; - } - - if (key === 'parameterEditors') { - vm.parameterEditors = values[key]; - } - - if (key === 'macro') { - bindMacro(values[key]); - } - } - - vm.page.loading = false; - }); - + var labelKeys = [ "general_settings", "macro_parameters" @@ -147,7 +121,7 @@ function MacrosEditController($scope, $q, $routeParams, macroResource, editorSta // navigation vm.labels.settings = values[0]; vm.labels.parameters = values[1]; - + vm.page.navigation = [ { "name": vm.labels.settings, From fc717d1e3735b80d94a31a655c0c5cd69d4ea220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Thu, 3 Oct 2019 14:01:00 +0200 Subject: [PATCH 003/109] Code comments --- .../Implement/ContentTypeRepositoryBase.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 4428f35de7..ab5c698b86 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -412,6 +412,11 @@ AND umbracoNode.id <> @id", foreach (var propertyType in entity.PropertyTypes) { // Update property variations + + // Determine target variation of the property type. + // The property is only considered culture variant when the base content type is also culture variant. + // The property is only considered segment variant when the base content type is also segment variant. + // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture propertyType.Variations = newContentTypeVariation & propertyType.Variations; // then, track each property individually @@ -442,15 +447,17 @@ AND umbracoNode.id <> @id", propertyTypeVariationChanges = propertyTypeVariationChanges ?? new Dictionary(); - foreach (var propertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes) + foreach (var composedPropertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes) { - if (propertyType.Variations == ContentVariation.Nothing) continue; + if (composedPropertyType.Variations == ContentVariation.Nothing) continue; - var target = newContentTypeVariation & propertyType.Variations; + // Determine target variation of the composed property type. + // The composed property is only considered culture variant when the base content type is also culture variant. + // The composed property is only considered segment variant when the base content type is also segment variant. + // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture + var target = newContentTypeVariation & composedPropertyType.Variations; - // if content type moves to a different variant, property type becomes Culture here again - // if content type moves to Nothing, property type becomes Nothing here - propertyTypeVariationChanges[propertyType.Id] = (propertyType.Variations, target); + propertyTypeVariationChanges[composedPropertyType.Id] = (composedPropertyType.Variations, target); } } @@ -645,16 +652,19 @@ AND umbracoNode.id <> @id", var propertyTypeIds = grouping.Select(x => x.Key).ToList(); var (FromVariation, ToVariation) = grouping.Key; - if(!FromVariation.HasFlag(ContentVariation.Culture) && - ToVariation.HasFlag(ContentVariation.Culture)) + var fromCultureEnabled = FromVariation.HasFlag(ContentVariation.Culture); + var toCultureEnabled = ToVariation.HasFlag(ContentVariation.Culture); + + if (!fromCultureEnabled && toCultureEnabled) { + // Culture has been enabled CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL); CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL); RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); } - else if (FromVariation.HasFlag(ContentVariation.Culture) && - !ToVariation.HasFlag(ContentVariation.Culture)) + else if (fromCultureEnabled && !toCultureEnabled) { + // Culture has been disabled CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL); CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL); RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL); @@ -672,7 +682,7 @@ AND umbracoNode.id <> @id", var cultureIsNotEnabled = !fromVariation.HasFlag(ContentVariation.Culture); var cultureWillBeEnabled = toVariation.HasFlag(ContentVariation.Culture); - if(cultureIsNotEnabled && cultureWillBeEnabled) + if (cultureIsNotEnabled && cultureWillBeEnabled) { //move the names //first clear out any existing names that might already exists under the default lang @@ -729,6 +739,13 @@ AND umbracoNode.id <> @id", Database.Execute(sqlInsert); } + else + { + //we don't need to move the names! this is because we always keep the invariant names with the name of the default language. + + //however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :( + // if we want these SQL statements back, look into GIT history + } } /// From 65b49f1a297dc23f6bb98d4eecac50e720cedf0a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 3 Oct 2019 15:06:16 +0200 Subject: [PATCH 004/109] Fixed adding/updating UmbracoMapper constructors --- src/Umbraco.Core/Mapping/UmbracoMapper.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Mapping/UmbracoMapper.cs b/src/Umbraco.Core/Mapping/UmbracoMapper.cs index e41a40e3d9..21164e8de9 100644 --- a/src/Umbraco.Core/Mapping/UmbracoMapper.cs +++ b/src/Umbraco.Core/Mapping/UmbracoMapper.cs @@ -343,16 +343,19 @@ namespace Umbraco.Core.Mapping if (ctor == null) return null; - if (_ctors.ContainsKey(sourceType)) - { + _ctors.AddOrUpdate(sourceType, sourceCtor, (k, v) => { + // Add missing constructors foreach (var c in sourceCtor) { - if (!_ctors[sourceType].TryGetValue(c.Key, out _)) - _ctors[sourceType].Add(c.Key, c.Value); - } - } - else - _ctors[sourceType] = sourceCtor; + if (!v.ContainsKey(c.Key)) + { + v.Add(c.Key, c.Value); + } + } + + return v; + }); + return ctor; } From d76ff8b9994f1ff040d88cbd8d3e579c4fc34623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Thu, 3 Oct 2019 16:40:44 +0200 Subject: [PATCH 005/109] Updated a couple unit tests for segments --- .../ContentTypeServiceVariantsTests.cs | 113 ++++++++++++------ 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 956de186be..6e55c16e64 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -107,12 +107,27 @@ namespace Umbraco.Tests.Services } } - [Test] - public void Change_Content_Type_Variation_Clears_Redirects() + [TestCase(ContentVariation.Nothing, ContentVariation.Nothing, false)] + [TestCase(ContentVariation.Nothing, ContentVariation.Culture, true)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment, true)] + [TestCase(ContentVariation.Nothing, ContentVariation.Segment, true)] + [TestCase(ContentVariation.Culture, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.Culture, ContentVariation.Culture, false)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment, true)] + [TestCase(ContentVariation.Culture, ContentVariation.CultureAndSegment, true)] + [TestCase(ContentVariation.Segment, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture, true)] + [TestCase(ContentVariation.Segment, ContentVariation.Segment, false)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Culture, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, false)] + public void Change_Content_Type_Variation_Clears_Redirects(ContentVariation startingContentTypeVariation, ContentVariation changedContentTypeVariation, bool shouldUrlRedirectsBeCleared) { - var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Nothing; - var properties = CreatePropertyCollection(("title", ContentVariation.Nothing)); + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Variations = startingContentTypeVariation; + var properties = CreatePropertyCollection(("title", startingContentTypeVariation)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); var contentType2 = MockedContentTypes.CreateBasicContentType("test"); @@ -121,6 +136,11 @@ namespace Umbraco.Tests.Services //create some content of this content type IContent doc = MockedContent.CreateBasicContent(contentType); doc.Name = "Hello1"; + if(startingContentTypeVariation.HasFlag(ContentVariation.Culture)) + { + doc.SetCultureName(doc.Name, "en-US"); + } + ServiceContext.ContentService.Save(doc); IContent doc2 = MockedContent.CreateBasicContent(contentType2); @@ -129,24 +149,27 @@ namespace Umbraco.Tests.Services ServiceContext.RedirectUrlService.Register("hello/world", doc.Key); ServiceContext.RedirectUrlService.Register("hello2/world2", doc2.Key); + // These 2 assertions should probably be moved to a test for the Register() method? Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count()); Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count()); //change variation - contentType.Variations = ContentVariation.Culture; + contentType.Variations = changedContentTypeVariation; ServiceContext.ContentTypeService.Save(contentType); - - Assert.AreEqual(0, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count()); + var expectedRedirectUrlCount = shouldUrlRedirectsBeCleared ? 0 : 1; + Assert.AreEqual(expectedRedirectUrlCount, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count()); Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count()); - } - [Test] - public void Change_Content_Type_From_Invariant_Variant() - { + [TestCase(ContentVariation.Nothing, ContentVariation.Culture)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment)] + public void Change_Content_Type_From_No_Culture_To_Culture(ContentVariation from, ContentVariation to) + { var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Nothing; - var properties = CreatePropertyCollection(("title", ContentVariation.Nothing)); + contentType.Variations = from; + var properties = CreatePropertyCollection(("title", from)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); @@ -161,12 +184,12 @@ namespace Umbraco.Tests.Services Assert.AreEqual("Hello1", doc.Name); Assert.AreEqual("hello world", doc.GetValue("title")); Assert.IsTrue(doc.Edited); - Assert.IsFalse (doc.IsCultureEdited("en-US")); + Assert.IsFalse(doc.IsCultureEdited("en-US")); //change the content type to be variant, we will also update the name here to detect the copy changes doc.Name = "Hello2"; ServiceContext.ContentService.Save(doc); - contentType.Variations = ContentVariation.Culture; + contentType.Variations = to; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -178,7 +201,7 @@ namespace Umbraco.Tests.Services //change back property type to be invariant, we will also update the name here to detect the copy changes doc.SetCultureName("Hello3", "en-US"); ServiceContext.ContentService.Save(doc); - contentType.Variations = ContentVariation.Nothing; + contentType.Variations = from; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -188,12 +211,15 @@ namespace Umbraco.Tests.Services Assert.IsFalse(doc.IsCultureEdited("en-US")); } - [Test] - public void Change_Content_Type_From_Variant_Invariant() + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + public void Change_Content_Type_From_Culture_To_No_Culture(ContentVariation startingContentTypeVariation, ContentVariation changeContentTypeVariationTo) { var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; - var properties = CreatePropertyCollection(("title", ContentVariation.Culture)); + contentType.Variations = startingContentTypeVariation; + var properties = CreatePropertyCollection(("title", startingContentTypeVariation)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); @@ -212,7 +238,7 @@ namespace Umbraco.Tests.Services //change the content type to be invariant, we will also update the name here to detect the copy changes doc.SetCultureName("Hello2", "en-US"); ServiceContext.ContentService.Save(doc); - contentType.Variations = ContentVariation.Nothing; + contentType.Variations = changeContentTypeVariationTo; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -224,19 +250,20 @@ namespace Umbraco.Tests.Services //change back property type to be variant, we will also update the name here to detect the copy changes doc.Name = "Hello3"; ServiceContext.ContentService.Save(doc); - contentType.Variations = ContentVariation.Culture; + contentType.Variations = startingContentTypeVariation; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get //at this stage all property types were switched to invariant so even though the variant value //exists it will not be returned because the property type is invariant, //so this check proves that null will be returned + Assert.AreEqual("Hello3", doc.Name); Assert.IsNull(doc.GetValue("title", "en-US")); Assert.IsTrue(doc.Edited); Assert.IsTrue(doc.IsCultureEdited("en-US")); // this is true because the name change is copied to the default language //we can now switch the property type to be variant and the value can be returned again - contentType.PropertyTypes.First().Variations = ContentVariation.Culture; + contentType.PropertyTypes.First().Variations = startingContentTypeVariation; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -244,24 +271,42 @@ namespace Umbraco.Tests.Services Assert.AreEqual("hello world", doc.GetValue("title", "en-US")); Assert.IsTrue(doc.Edited); Assert.IsTrue(doc.IsCultureEdited("en-US")); - } - - [Test] - public void Change_Property_Type_From_To_Variant_On_Invariant_Content_Type() + [TestCase(ContentVariation.Nothing, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.Nothing, ContentVariation.Culture, false)] + [TestCase(ContentVariation.Nothing, ContentVariation.Segment, false)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment, false)] + [TestCase(ContentVariation.Culture, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.Culture, ContentVariation.Culture, true)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment, false)] + [TestCase(ContentVariation.Culture, ContentVariation.CultureAndSegment, false)] + [TestCase(ContentVariation.Segment, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture, false)] + [TestCase(ContentVariation.Segment, ContentVariation.Segment, true)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment, false)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Culture, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment, true)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, true)] + public void Verify_If_Property_Type_Variation_Is_Allowed(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation, bool isAllowed) { var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Nothing; - var properties = CreatePropertyCollection(("title", ContentVariation.Nothing)); + contentType.Variations = contentTypeVariation; + var properties = CreatePropertyCollection(("title", contentTypeVariation)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); - //change the property type to be variant - contentType.PropertyTypes.First().Variations = ContentVariation.Culture; + contentType.PropertyTypes.First().Variations = propertyTypeVariation; - //Cannot change a property type to be variant if the content type itself is not variant - Assert.Throws(() => ServiceContext.ContentTypeService.Save(contentType)); + // property may only vary by segment if the content type itself is also varied by segment + // property may only vary by culture if the content type itself is also varied by culture + // properties without variance should always be allowed + + if (isAllowed) + Assert.DoesNotThrow(() => ServiceContext.ContentTypeService.Save(contentType)); + else + Assert.Throws(() => ServiceContext.ContentTypeService.Save(contentType)); } [Test] From c7d00b2c0083d47d37d6706c626d613101b97282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Fri, 4 Oct 2019 08:53:22 +0200 Subject: [PATCH 006/109] Corrected ValidateVariations method Triggered by a failing unit test that was updated for segments --- .../Implement/ContentTypeRepositoryBase.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index ab5c698b86..2d70e89aa0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -513,20 +513,18 @@ AND umbracoNode.id <> @id", /// /// Ensures that no property types are flagged for a variance that is not supported by the content type itself /// - /// + /// The entity for which the property types will be validated private void ValidateVariations(IContentTypeComposition entity) { - //if the entity does not vary at all, then the property cannot have a variance value greater than it - if (entity.Variations == ContentVariation.Nothing) + foreach (var prop in entity.PropertyTypes) { - foreach (var prop in entity.PropertyTypes) - { - if (prop.IsPropertyDirty(nameof(prop.Variations)) && prop.Variations > entity.Variations) - throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}"); - } - + // The variation of a property is only allowed if all its variation flags + // are also set on the entity itself. It cannot set anything that is not also set by the content type. + // For example, when entity.Variations is set to Culture a property cannot be set to Segment. + var isValid = entity.Variations.HasFlag(prop.Variations); + if (!isValid) + throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}"); } - } private IEnumerable GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable all) From 2e79a515c9ac83bee24b5354fe3bfb89d4d0819d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Fri, 4 Oct 2019 12:07:05 +0200 Subject: [PATCH 007/109] Updated tests --- .../ContentTypeServiceVariantsTests.cs | 82 ++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 2a94ba971e..48bcf3c928 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -124,9 +124,7 @@ namespace Umbraco.Tests.Services public void Change_Content_Type_Variation_Clears_Redirects(ContentVariation startingContentTypeVariation, ContentVariation changedContentTypeVariation, bool shouldUrlRedirectsBeCleared) { var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = startingContentTypeVariation; - var properties = CreatePropertyCollection(("title", startingContentTypeVariation)); - contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); + contentType.Variations = startingContentTypeVariation; ServiceContext.ContentTypeService.Save(contentType); var contentType2 = MockedContentTypes.CreateBasicContentType("test"); ServiceContext.ContentTypeService.Save(contentType2); @@ -271,6 +269,84 @@ namespace Umbraco.Tests.Services Assert.IsTrue(doc.IsCultureEdited("en-US")); } + [TestCase(ContentVariation.Nothing, ContentVariation.Nothing)] + [TestCase(ContentVariation.Nothing, ContentVariation.Culture)] + [TestCase(ContentVariation.Nothing, ContentVariation.Segment)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Culture)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.Culture, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Segment, ContentVariation.Nothing)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture)] + [TestCase(ContentVariation.Segment, ContentVariation.Segment)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Culture)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment)] + public void Preserve_Content_Name_After_Content_Type_Variation_Change(ContentVariation contentTypeVariationFrom, ContentVariation contentTypeVariationTo) + { + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Variations = contentTypeVariationFrom; + ServiceContext.ContentTypeService.Save(contentType); + + var invariantContentName = "Content Invariant"; + + var defaultCultureContentName = "Content en-US"; + var defaultCulture = "en-US"; + + var nlContentName = "Content nl-NL"; + var nlCulture = "nl-NL"; + + ServiceContext.LocalizationService.Save(new Language(nlCulture)); + + var includeCultureNames = contentType.Variations.HasFlag(ContentVariation.Culture); + + // Create some content of this content type + IContent doc = MockedContent.CreateBasicContent(contentType); + + doc.Name = invariantContentName; + if (includeCultureNames) + { + Assert.DoesNotThrow(() => doc.SetCultureName(defaultCultureContentName, defaultCulture)); + Assert.DoesNotThrow(() => doc.SetCultureName(nlContentName, nlCulture)); + } else + { + Assert.Throws(() => doc.SetCultureName(defaultCultureContentName, defaultCulture)); + Assert.Throws(() => doc.SetCultureName(nlContentName, nlCulture)); + } + + ServiceContext.ContentService.Save(doc); + doc = ServiceContext.ContentService.GetById(doc.Id); + + AssertAll(); + + // Change variation + contentType.Variations = contentTypeVariationTo; + ServiceContext.ContentService.Save(doc); + doc = ServiceContext.ContentService.GetById(doc.Id); + + AssertAll(); + + void AssertAll() + { + if (includeCultureNames) + { + // Invariant content name is not preserved when content type is set to culture + Assert.AreEqual(defaultCultureContentName, doc.Name); + Assert.AreEqual(doc.Name, doc.GetCultureName(defaultCulture)); + Assert.AreEqual(nlContentName, doc.GetCultureName(nlCulture)); + } + else + { + Assert.AreEqual(invariantContentName, doc.Name); + Assert.AreEqual(null, doc.GetCultureName(defaultCulture)); + Assert.AreEqual(null, doc.GetCultureName(nlCulture)); + } + } + } + [TestCase(ContentVariation.Nothing, ContentVariation.Nothing, true)] [TestCase(ContentVariation.Nothing, ContentVariation.Culture, false)] [TestCase(ContentVariation.Nothing, ContentVariation.Segment, false)] From 3858bd40adf5fc6c8181c1c8518d41b3e01fd824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Fri, 4 Oct 2019 14:28:13 +0200 Subject: [PATCH 008/109] Moved property type variation auto correct. --- .../Repositories/Implement/ContentTypeRepository.cs | 10 ++++++++++ .../Implement/ContentTypeRepositoryBase.cs | 10 +--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs index 359b967dab..7c7f39fa54 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -287,6 +287,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.SortOrder = maxSortOrder + 1; } + // Update property variations based on the content type variation + foreach (var propertyType in entity.PropertyTypes) + { + // Determine variation for the property type. + // The property is only considered culture variant when the base content type is also culture variant. + // The property is only considered segment variant when the base content type is also segment variant. + // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture + propertyType.Variations = entity.Variations & propertyType.Variations; + } + PersistUpdatedBaseContentType(entity); PersistTemplates(entity, true); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 2d70e89aa0..dec5805cf2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -411,15 +411,7 @@ AND umbracoNode.id <> @id", // note: this only deals with *local* property types, we're dealing w/compositions later below foreach (var propertyType in entity.PropertyTypes) { - // Update property variations - - // Determine target variation of the property type. - // The property is only considered culture variant when the base content type is also culture variant. - // The property is only considered segment variant when the base content type is also segment variant. - // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture - propertyType.Variations = newContentTypeVariation & propertyType.Variations; - - // then, track each property individually + // track each property individually if (propertyType.IsPropertyDirty("Variations")) { // allocate the list only when needed From 4a3b3c41517541a62bc791f0a00e07e613e770f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Fri, 4 Oct 2019 14:19:51 +0200 Subject: [PATCH 009/109] Property type correction test --- .../ContentTypeServiceVariantsTests.cs | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 48bcf3c928..7affe2de3f 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -347,40 +347,37 @@ namespace Umbraco.Tests.Services } } - [TestCase(ContentVariation.Nothing, ContentVariation.Nothing, true)] - [TestCase(ContentVariation.Nothing, ContentVariation.Culture, false)] - [TestCase(ContentVariation.Nothing, ContentVariation.Segment, false)] - [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment, false)] - [TestCase(ContentVariation.Culture, ContentVariation.Nothing, true)] - [TestCase(ContentVariation.Culture, ContentVariation.Culture, true)] - [TestCase(ContentVariation.Culture, ContentVariation.Segment, false)] - [TestCase(ContentVariation.Culture, ContentVariation.CultureAndSegment, false)] - [TestCase(ContentVariation.Segment, ContentVariation.Nothing, true)] - [TestCase(ContentVariation.Segment, ContentVariation.Culture, false)] - [TestCase(ContentVariation.Segment, ContentVariation.Segment, true)] - [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment, false)] - [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing, true)] - [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Culture, true)] - [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment, true)] - [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, true)] - public void Verify_If_Property_Type_Variation_Is_Allowed(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation, bool isAllowed) + [TestCase(ContentVariation.Nothing, ContentVariation.Nothing)] + [TestCase(ContentVariation.Nothing, ContentVariation.Culture)] + [TestCase(ContentVariation.Nothing, ContentVariation.Segment)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Culture)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.Culture, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Segment, ContentVariation.Nothing)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture)] + [TestCase(ContentVariation.Segment, ContentVariation.Segment)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Culture)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment)] + public void Verify_If_Property_Type_Variation_Is_Correctly_Corrected_When_Content_Type_Is_Updated(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation) { var contentType = MockedContentTypes.CreateBasicContentType(); + + // We test an updated content type so it has to be saved first. + ServiceContext.ContentTypeService.Save(contentType); + + // Update it contentType.Variations = contentTypeVariation; - var properties = CreatePropertyCollection(("title", contentTypeVariation)); + var properties = CreatePropertyCollection(("title", propertyTypeVariation)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); - contentType.PropertyTypes.First().Variations = propertyTypeVariation; - - // property may only vary by segment if the content type itself is also varied by segment - // property may only vary by culture if the content type itself is also varied by culture - // properties without variance should always be allowed - - if (isAllowed) - Assert.DoesNotThrow(() => ServiceContext.ContentTypeService.Save(contentType)); - else - Assert.Throws(() => ServiceContext.ContentTypeService.Save(contentType)); + // Check if property type variations have been updated correctly + Assert.AreEqual(properties.First().Variations, contentTypeVariation & propertyTypeVariation); } [Test] From ab1f9f3acb56a4ed55d0c9bc14a02f1d83a342e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 10:41:16 +0200 Subject: [PATCH 010/109] added fake test for this prototype --- .../src/common/resources/datatype.resource.js | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js index 7c4daedd38..f20debc9c3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js @@ -43,6 +43,112 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { "Failed to retrieve pre values for editor alias " + editorAlias); }, + /** + * @ngdoc method + * @name umbraco.resources.dataTypeResource#getRelations + * @methodOf umbraco.resources.dataTypeResource + * + * @description + * Retrieves relations of a given data type. + * + * ##usage + *
+         * dataTypeResource.getRelation(1234)
+         *    .then(function(relations) {
+         *        alert('its gone!');
+         *    });
+         * 
+ * + * @param {Int} id id of datatype to retrieve relations for + * @returns {Promise} resourcePromise object. + * + */ + getRelations: function (id) { + + return $q((resolve, reject) => { + + +var fakeData = { + "documentTypes": [ + { + "name": "Home", + "alias": "home", + "icon": "icon-home", + "udi": "document-type://B6C7EFA7-1D03-46BF-B6D3-9187EF4CD176", + "id": 1234, + "properties": [ + { + "alias": "bodyText", + "name": "Body Text" + }, + { + "alias": "description", + "name": "Description" + } + ] + }, + { + "name": "News", + "alias": "news", + "icon": "icon-newspaper", + "udi": "document-type://FAB4F78E-530E-4F5D-959A-82441958460C", + "id": 9876, + "properties": [ + { + "alias": "article", + "name": "Article" + } + ] + } + ], + "mediaTypes": [ + { + "name": "Image", + "alias": "image", + "icon": "icon-umb-media", + "udi": "media-type://488152AF-0C03-4F2D-9DE0-FDECC80212AD", + "id": 9999, + "properties": [ + { + "alias": "altText", + "name": "Alt Text" + } + ] + } + ], + "memberTypes": [ + { + "name": "Member", + "alias": "member", + "icon": "icon-people-alt-2", + "udi": "member-type://93773B7C-2D83-4529-92D9-B9759220384C", + "id": 7777, + "properties": [ + { + "alias": "bio", + "name": "Bio" + } + ] + } + ] +}; + + resolve(fakeData); + }); + /* + + TODO: get real data from server! + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "dataTypeApiBaseUrl", + "GetRelations", + [{ id: id }])), + "Failed to retrieve relations for data type of id " + id); + */ + }, + /** * @ngdoc method * @name umbraco.resources.dataTypeResource#getById From 3ff2996cb9acae20bec31a42d77703ec792dafa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 10:41:41 +0200 Subject: [PATCH 011/109] added a view for datatype relations --- .../datatypes/datatype.edit.controller.js | 12 +- .../views/datatype.relations.controller.js | 39 +++++++ .../datatypes/views/datatype.relations.html | 105 +++++++++++++----- .../datatypes/views/datatype.settings.html | 20 ++-- .../Umbraco/config/lang/en_us.xml | 10 ++ 5 files changed, 146 insertions(+), 40 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 7ade362473..bb45968d5b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -14,7 +14,10 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic //setup scope vars vm.page = {}; vm.page.loading = false; - + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + //set up the standard data type props vm.properties = { selectedEditor: { @@ -51,7 +54,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic function setHeaderNameState(content) { if(content.isSystem == 1) { - $scope.page.nameLocked = true; + vm.page.nameLocked = true; } } @@ -88,6 +91,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic vm.page.loading = false; }); + } function saveDataType() { @@ -208,7 +212,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic var labelKeys = [ "general_settings", - "macro_parameters" + "editdatatype_relations" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -224,7 +228,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic { "name": values[1], "alias": "parameters", - "icon": "icon-list", + "icon": "icon-molecular-network", "view": "views/datatypes/views/datatype.relations.html" } ]; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js new file mode 100644 index 0000000000..6fdcaf9650 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js @@ -0,0 +1,39 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.DataType.RelationsController + * @function + * + * @description + * The controller for the relations view of the datatype editor + */ +function DataTypeRelationsController($scope, $routeParams, dataTypeResource) { + + var vm = this; + + vm.relations = {}; + vm.hasRelations = false; + + vm.view = {}; + vm.view.loading = true; + + //we are editing so get the content item from the server + dataTypeResource.getRelations($routeParams.id) + .then(function(data) { + + console.log("got: ", data); + + vm.view.loading = false; + vm.relations = data; + + vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; + + + console.log("vm: ", vm); + + }); + + + +} + +angular.module("umbraco").controller("Umbraco.Editors.DataType.RelationsController", DataTypeRelationsController); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 43266ddafc..c2b8a305ff 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -1,34 +1,87 @@ - +
+ - + - -
{{vm.content.id}}
- {{vm.content.key}} -
+ - -
- - - Required - -
+ + This Data Type has no relations. + -
+ + - -
{{vm.content.selectedEditor}}
-
+
+ No relations for Document Types. +
+
+ + + + + + + + + + + + +
NameAlias
{{relation.name}}{{relation.alias}}Open
+
+ +
- - - + + -
+
+ No relations for Media Types. +
+
+ + + + + + + + + + + + +
NameAlias
{{relation.name}}{{relation.alias}}Open
+
+ + -
+ + + +
+ No relations for Member Types. +
+
+ + + + + + + + + + + + +
NameAlias
{{relation.name}}{{relation.alias}}Open
+
+ +
+ + + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html index d5d49ca8fb..d37aa2fcd0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.settings.html @@ -2,17 +2,17 @@ - -
{{content.id}}
- {{content.key}} + +
{{model.content.id}}
+ {{model.content.key}}
- +
+ ng-options="e.alias as e.name for e in model.content.availableEditors"> Required @@ -20,15 +20,15 @@ - -
{{content.selectedEditor}}
+ +
{{model.content.selectedEditor}}
+ ng-repeat="preValue in model.preValues"> - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 96387b7d26..9d6f40ee4f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2165,4 +2165,14 @@ To manage your website, simply open the Umbraco back office and start adding con Open/Close backoffice help Open/Close your profile options + + Relations + This Data Type has no relations. + Relations to Document Types + No relations to Document Types. + Relations to Media Types + No relations to Media Types. + Relations to Member Types + No relations to Member Types. + From 65c49895759af54edc25c7ca5d46a6431c36d1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 13:05:48 +0200 Subject: [PATCH 012/109] added more translations --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 11 +++++++++++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index ff299b8e7f..cb52f5ee44 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1713,4 +1713,15 @@ Mange hilsner fra Umbraco robotten Åben/Luk backoffice hjælp Åben/Luk dine profil indstillinger + + Relationer + Denne Data Type har ingen relationer. + Relationer til Dokument Typer + Ingen relationer til Dokument Typer. + Relationer til Medie Typer + Ingen relationer til Medie Typer. + RRelationer til Medlems Typer + Ingen relationer til Medlems Typer. + + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index d7d743e7e9..dbce62cb29 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2150,4 +2150,15 @@ To manage your website, simply open the Umbraco back office and start adding con Open/Close backoffice help Open/Close your profile options + + Relations + This Data Type has no relations. + Relations to Document Types + No relations to Document Types. + Relations to Media Types + No relations to Media Types. + Relations to Member Types + No relations to Member Types. + + From 3365d6fc88f749a16630c4e221e34aba2b314ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 13:10:17 +0200 Subject: [PATCH 013/109] correctied area key for translation --- .../views/datatypes/views/datatype.relations.html | 14 +++++++------- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index c2b8a305ff..9c7218f0c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -6,14 +6,14 @@ - This Data Type has no relations. + This Data Type has no relations. - +
- No relations for Document Types. + No relations for Document Types.
@@ -34,10 +34,10 @@ - +
- No relations for Media Types. + No relations for Media Types.
@@ -58,10 +58,10 @@ - +
- No relations for Member Types. + No relations for Member Types.
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index cb52f5ee44..0d675bd0ec 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1713,7 +1713,7 @@ Mange hilsner fra Umbraco robotten Åben/Luk backoffice hjælpÅben/Luk dine profil indstillinger - + RelationerDenne Data Type har ingen relationer.Relationer til Dokument Typer From 79d20bfddb0050519cec815906687c51bee2d1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 13:10:45 +0200 Subject: [PATCH 014/109] whitespace --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index dbce62cb29..f32e2dd602 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2150,7 +2150,7 @@ To manage your website, simply open the Umbraco back office and start adding con Open/Close backoffice help Open/Close your profile options - + Relations This Data Type has no relations. Relations to Document Types From dbee0da6a5c23745a0f921454aca1d362db4aceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 13:14:25 +0200 Subject: [PATCH 015/109] tabName translation --- .../src/views/datatypes/datatype.edit.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index bb45968d5b..85d48c8576 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -212,7 +212,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic var labelKeys = [ "general_settings", - "editdatatype_relations" + "relations_tabName" ]; localizationService.localizeMany(labelKeys).then(function (values) { From 8e17245dc0a3a634948e8b9ac27130664c5f2ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 13:58:34 +0200 Subject: [PATCH 016/109] changed look and translation keys --- .../datatypes/views/datatype.relations.html | 187 ++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 12 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 12 +- .../Umbraco/config/lang/en_us.xml | 12 +- 4 files changed, 126 insertions(+), 97 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 9c7218f0c3..37472120fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -1,87 +1,116 @@
- + + + + - - - - + This Data Type has no relations. - - - - -
- No relations for Document Types. -
-
-
- - - - - - - - - - - -
NameAlias
{{relation.name}}{{relation.alias}}Open
-
- -
- - - - -
- No relations for Media Types. -
-
- - - - - - - - - - - - -
NameAlias
{{relation.name}}{{relation.alias}}Open
-
- -
- - - - -
- No relations for Member Types. -
-
- - - - - - - - - - - - -
NameAlias
{{relation.name}}{{relation.alias}}Open
-
- -
- - + + +
+ + + +

+ +

+ +
+ + + No relations for Document Types. + + +
+
+
+
Name
+
Alias
+
+
+
+
+
+
{{relation.name}}
+
{{relation.alias}}
+ +
+
+
+
+ + + + +

+ +

+ +
+ + + No relations for Media Types. + + +
+
+
+
Name
+
Alias
+
+
+
+
+
+
{{relation.name}}
+
{{relation.alias}}
+ +
+
+ +
+
+ + + + +

+ +

+ +
+ + + No relations for Member Types. + + +
+
+
+
Name
+
Alias
+
+
+
+
+
+
{{relation.name}}
+
{{relation.alias}}
+ +
+
+ +
+
+ + + +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 0d675bd0ec..ea13684d61 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1716,12 +1716,12 @@ Mange hilsner fra Umbraco robotten Relationer Denne Data Type har ingen relationer. - Relationer til Dokument Typer - Ingen relationer til Dokument Typer. - Relationer til Medie Typer - Ingen relationer til Medie Typer. - RRelationer til Medlems Typer - Ingen relationer til Medlems Typer. + Brugt af følgende Dokument Typer + Ingen relationer til Dokument Typer. + Brugt af følgende Medie Typer + Ingen relationer til Medie Typer. + Brugt af følgende Medlems Typer + Ingen relationer til Medlems Typer. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index f32e2dd602..79fc8c10df 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2153,12 +2153,12 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Relations to Document Types - No relations to Document Types. - Relations to Media Types - No relations to Media Types. - Relations to Member Types - No relations to Member Types. + Use by following Document Types + No relations to Document Types. + Use by following Media Types + No relations to Media Types. + Use by following Member Types + No relations to Member Types. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index e334b980e1..4fcb25bd15 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2167,11 +2167,11 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Relations to Document Types - No relations to Document Types. - Relations to Media Types - No relations to Media Types. - Relations to Member Types - No relations to Member Types. + Use by following Document Types + No relations to Document Types. + Use by following Media Types + No relations to Media Types. + Use by following Member Types + No relations to Member Types. From 097ef82d41cd30a7b399fcdf49e12273c4c86e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:02:18 +0200 Subject: [PATCH 017/109] corrected working --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 6 +++--- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 79fc8c10df..a2959bb26b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2153,11 +2153,11 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Use by following Document Types + Used by following Document Types No relations to Document Types. - Use by following Media Types + Used by following Media Types No relations to Media Types. - Use by following Member Types + Used by following Member Types No relations to Member Types. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 4fcb25bd15..317dd28a32 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2167,11 +2167,11 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Use by following Document Types + Used by following Document Types No relations to Document Types. - Use by following Media Types + Used by following Media Types No relations to Media Types. - Use by following Member Types + Used by following Member Types No relations to Member Types. From 3da64651b0f78d5093583c81806815b011df491b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:02:30 +0200 Subject: [PATCH 018/109] changed to headlines to h5 --- .../datatypes/views/datatype.relations.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 37472120fc..b0faec9b86 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -16,9 +16,9 @@ -

- -

+
+ +
@@ -47,9 +47,9 @@ -

- -

+
+ +
@@ -79,9 +79,9 @@ -

- -

+
+ +
From bcc76b207220c84f55858f75c43673f4abadd119 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Oct 2019 23:09:45 +1100 Subject: [PATCH 019/109] Implements DataTypeService.FindUsages --- .../Repositories/IDataTypeRepository.cs | 7 +++ .../Implement/DataTypeRepository.cs | 41 ++++++++++++ src/Umbraco.Core/Services/IDataTypeService.cs | 7 +++ .../Services/Implement/DataTypeService.cs | 8 +++ .../DataTypeDefinitionRepositoryTest.cs | 62 +++++++++++++++++++ .../TestHelpers/TestObjects-Mocks.cs | 5 ++ 6 files changed, 130 insertions(+) diff --git a/src/Umbraco.Core/Persistence/Repositories/IDataTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDataTypeRepository.cs index afb419ebd6..3a44cb10b4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDataTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDataTypeRepository.cs @@ -7,5 +7,12 @@ namespace Umbraco.Core.Persistence.Repositories public interface IDataTypeRepository : IReadWriteQueryRepository { IEnumerable> Move(IDataType toMove, EntityContainer container); + + /// + /// Returns a dictionary of content type s and the property type aliases that use a + /// + /// + /// + IReadOnlyDictionary> FindUsages(int id); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs index dac8fda5ec..41d13007b2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -279,6 +279,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return moveInfo; } + public IReadOnlyDictionary> FindUsages(int id) + { + if (id == default) + return new Dictionary>(); + + var sql = Sql() + .Select(ct => ct.Select(node => node.NodeDto)) + .AndSelect(pt => Alias(pt.Alias, "ptAlias"), pt => Alias(pt.Name, "ptName")) + .From() + .InnerJoin().On(ct => ct.NodeId, pt => pt.ContentTypeId) + .InnerJoin().On(n => n.NodeId, ct => ct.NodeId) + .Where(pt => pt.DataTypeId == id) + .OrderBy(node => node.NodeId) + .AndBy(pt => pt.Alias); + + var dtos = Database.FetchOneToMany(ct => ct.PropertyTypes, sql); + + return dtos.ToDictionary( + x => (Udi)new GuidUdi(ObjectTypes.GetUdiType(x.NodeDto.NodeObjectType.Value), x.NodeDto.UniqueId).EnsureClosed(), + x => (IEnumerable)x.PropertyTypes.Select(x => x.Alias).ToList()); + } + private string EnsureUniqueNodeName(string nodeName, int id = 0) { var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql @@ -291,5 +313,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return SimilarNodeName.GetUniqueName(names, id, nodeName); } + + + [TableName(Constants.DatabaseSchema.Tables.ContentType)] + private class ContentTypeReferenceDto : ContentTypeDto + { + [ResultColumn] + [Reference(ReferenceType.Many)] + public List PropertyTypes { get; set; } + } + + [TableName(Constants.DatabaseSchema.Tables.PropertyType)] + private class PropertyTypeReferenceDto + { + [Column("ptAlias")] + public string Alias { get; set; } + + [Column("ptName")] + public string Name { get; set; } + } } } diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index bb56e110cd..77bf509498 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -10,6 +10,13 @@ namespace Umbraco.Core.Services /// public interface IDataTypeService : IService { + /// + /// Returns a dictionary of content type s and the property type aliases that use a + /// + /// + /// + IReadOnlyDictionary> FindUsages(int id); + Attempt> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); EntityContainer GetContainer(int containerId); diff --git a/src/Umbraco.Core/Services/Implement/DataTypeService.cs b/src/Umbraco.Core/Services/Implement/DataTypeService.cs index dc998b18dd..e06c49cf44 100644 --- a/src/Umbraco.Core/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/DataTypeService.cs @@ -466,6 +466,14 @@ namespace Umbraco.Core.Services.Implement } } + public IReadOnlyDictionary> FindUsages(int id) + { + using (var scope = ScopeProvider.CreateScope(autoComplete:true)) + { + return _dataTypeRepository.FindUsages(id); + } + } + private void Audit(AuditType type, int userId, int objectId) { _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType))); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index b558ce6c87..ca8ee29ee3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -28,6 +28,68 @@ namespace Umbraco.Tests.Persistence.Repositories return new EntityContainerRepository(scopeAccessor, AppCaches.Disabled, Logger, Constants.ObjectTypes.DataTypeContainer); } + [Test] + public void Can_Find_Usages() + { + var provider = TestObjects.GetScopeProvider(Logger); + + using (provider.CreateScope()) + { + var dtRepo = CreateRepository(); + IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt1" }; + dtRepo.Save(dataType1); + IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt2" }; + dtRepo.Save(dataType2); + + var ctRepo = Factory.GetInstance(); + IContentType ct = new ContentType(-1) + { + Alias = "ct1", + Name = "CT1", + AllowedAsRoot = true, + Icon = "icon-home", + PropertyGroups = new PropertyGroupCollection + { + new PropertyGroup(true) + { + Name = "PG1", + PropertyTypes = new PropertyTypeCollection(true) + { + new PropertyType(dataType1, "pt1") + { + Name = "PT1" + }, + new PropertyType(dataType1, "pt2") + { + Name = "PT2" + }, + new PropertyType(dataType2, "pt3") + { + Name = "PT3" + } + } + } + } + }; + ctRepo.Save(ct); + + var usages = dtRepo.FindUsages(dataType1.Id); + + var key = usages.First().Key; + Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid); + Assert.AreEqual(2, usages[key].Count()); + Assert.AreEqual("pt1", usages[key].ElementAt(0)); + Assert.AreEqual("pt2", usages[key].ElementAt(1)); + + usages = dtRepo.FindUsages(dataType2.Id); + + key = usages.First().Key; + Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid); + Assert.AreEqual(1, usages[key].Count()); + Assert.AreEqual("pt3", usages[key].ElementAt(0)); + } + } + [Test] public void Can_Move() { diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index dc8e35bb52..3600db32de 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -329,6 +329,11 @@ namespace Umbraco.Tests.TestHelpers { throw new NotImplementedException(); } + + public IReadOnlyDictionary> FindUsages(int id) + { + throw new NotImplementedException(); + } } #endregion From 69fb12a270f3177fe3a006435397bfa843d622df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:20:30 +0200 Subject: [PATCH 020/109] changed wording --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 6 +++--- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 6 +++--- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index ea13684d61..e414c04146 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1716,11 +1716,11 @@ Mange hilsner fra Umbraco robotten Relationer Denne Data Type har ingen relationer. - Brugt af følgende Dokument Typer + Brugt af Dokument Typer Ingen relationer til Dokument Typer. - Brugt af følgende Medie Typer + Brugt af Medie Typer Ingen relationer til Medie Typer. - Brugt af følgende Medlems Typer + Brugt af Medlems Typer Ingen relationer til Medlems Typer. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a2959bb26b..732586e3af 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2153,11 +2153,11 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Used by following Document Types + Used by Document Types No relations to Document Types. - Used by following Media Types + Used by Media Types No relations to Media Types. - Used by following Member Types + Used by Member Types No relations to Member Types. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 317dd28a32..1f2c1ddb9b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2167,11 +2167,11 @@ To manage your website, simply open the Umbraco back office and start adding con Relations This Data Type has no relations. - Used by following Document Types + Used by Document Types No relations to Document Types. - Used by following Media Types + Used by Media Types No relations to Media Types. - Used by following Member Types + Used by Member Types No relations to Member Types. From eff284cb04adc4e64c9c217170c89cf656cdf0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:20:42 +0200 Subject: [PATCH 021/109] corrected open links --- .../src/views/datatypes/views/datatype.relations.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index b0faec9b86..0bc5d9f51e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -38,7 +38,7 @@
{{relation.name}}
{{relation.alias}}
- +
@@ -69,7 +69,7 @@
{{relation.name}}
{{relation.alias}}
- +
@@ -101,7 +101,7 @@
{{relation.name}}
{{relation.alias}}
- +
From dfc642e7870f4e7c627317ef5b4c2a42beb00a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:32:41 +0200 Subject: [PATCH 022/109] seperate row for icons --- .../datatypes/views/datatype.relations.html | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 0bc5d9f51e..486276e52c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -16,11 +16,11 @@ -
+
-
+
No relations for Document Types. @@ -29,6 +29,7 @@
+
Name
Alias
@@ -36,7 +37,8 @@
-
{{relation.name}}
+
+
{{relation.name}}
{{relation.alias}}
@@ -47,11 +49,11 @@ -
+
-
+
No relations for Media Types. @@ -60,6 +62,7 @@
+
Name
Alias
@@ -67,7 +70,8 @@
-
{{relation.name}}
+
+
{{relation.name}}
{{relation.alias}}
@@ -79,11 +83,11 @@ -
+
-
+
No relations for Member Types. @@ -92,6 +96,7 @@
+
Name
Alias
@@ -99,7 +104,8 @@
-
{{relation.name}}
+
+
{{relation.name}}
{{relation.alias}}
From 5deaf996e0e9cc80449734881f04124a2cbad131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 14:48:42 +0200 Subject: [PATCH 023/109] auto width for actions --- .../datatypes/views/datatype.relations.html | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 486276e52c..4215d3e092 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -16,23 +16,19 @@ -
- -
+
-
- - - No relations for Document Types. - +
+ +
-
+
Name
Alias
-
+
@@ -40,7 +36,7 @@
{{relation.name}}
{{relation.alias}}
- +
@@ -48,24 +44,19 @@ +
-
- -
+
+ +
-
- - - No relations for Media Types. - - -
+
Name
Alias
-
+
@@ -73,33 +64,27 @@
{{relation.name}}
{{relation.alias}}
- +
-
+
-
- -
+
+ +
-
- - - No relations for Member Types. - - -
-
+
+
Name
Alias
-
+
@@ -107,13 +92,12 @@
{{relation.name}}
{{relation.alias}}
- +
-
-
+
From ab6f4af439e33d4c630fadf2dddff578b8ac31e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 15:45:14 +0200 Subject: [PATCH 024/109] dont know why this was removed, brain fart... --- .../views/macros/macros.edit.controller.js | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js index dd0dd7499f..2f14e0ceea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js @@ -108,10 +108,36 @@ function MacrosEditController($scope, $q, $routeParams, macroResource, editorSta function init() { vm.page.loading = true; - + + vm.promises['partialViews'] = getPartialViews(); + vm.promises['parameterEditors'] = getParameterEditors(); + vm.promises['macro'] = getMacro(); + vm.views = []; vm.node = null; - + + $q.all(vm.promises).then(function (values) { + var keys = Object.keys(values); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + + if (key === 'partialViews') { + vm.views = values[key]; + } + + if (key === 'parameterEditors') { + vm.parameterEditors = values[key]; + } + + if (key === 'macro') { + bindMacro(values[key]); + } + } + + vm.page.loading = false; + }); + var labelKeys = [ "general_settings", "macro_parameters" From a178a4d77b1bc669f5b1d756d93b974e9b74a065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 8 Oct 2019 15:45:25 +0200 Subject: [PATCH 025/109] removing console.logs --- .../views/datatypes/views/datatype.relations.controller.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js index 6fdcaf9650..83d6e4a379 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js @@ -20,16 +20,12 @@ function DataTypeRelationsController($scope, $routeParams, dataTypeResource) { dataTypeResource.getRelations($routeParams.id) .then(function(data) { - console.log("got: ", data); - vm.view.loading = false; vm.relations = data; vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; - console.log("vm: ", vm); - }); From ac300179e6caaefdaef294204cc3137a0dca1b87 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2019 15:19:03 +1100 Subject: [PATCH 026/109] Adds new endpoint to DataTypeController to return the correct data and wires that up to angular --- .../Services/IContentTypeServiceBase.cs | 3 +- src/Umbraco.Core/Services/IDataTypeService.cs | 2 +- ...peServiceBaseOfTRepositoryTItemTService.cs | 4 +- .../Services/Implement/DataTypeService.cs | 2 +- .../TestHelpers/TestObjects-Mocks.cs | 2 +- .../src/common/resources/datatype.resource.js | 94 ++----------------- .../views/datatype.relations.controller.js | 16 ++-- src/Umbraco.Web/Editors/DataTypeController.cs | 53 +++++++++++ .../ContentEditing/DataTypeReferences.cs | 36 +++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 10 files changed, 111 insertions(+), 102 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index b86494adb5..51e5d756eb 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Services /// /// Gets a content type. /// - TItem Get(int id); + new TItem Get(int id); /// /// Gets a content type. @@ -40,6 +40,7 @@ namespace Umbraco.Core.Services int Count(); IEnumerable GetAll(params int[] ids); + IEnumerable GetAll(IEnumerable ids); IEnumerable GetDescendants(int id, bool andSelf); // parent-child axis IEnumerable GetComposedOf(int id); // composition axis diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index 77bf509498..3ebfa95bfb 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Services /// /// /// - IReadOnlyDictionary> FindUsages(int id); + IReadOnlyDictionary> GetReferences(int id); Attempt> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 6ac8e1404a..705a876d83 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -252,12 +252,12 @@ namespace Umbraco.Core.Services.Implement } } - public IEnumerable GetAll(params Guid[] ids) + public IEnumerable GetAll(IEnumerable ids) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(ReadLockIds); - return Repository.GetMany(ids); + return Repository.GetMany(ids.ToArray()); } } diff --git a/src/Umbraco.Core/Services/Implement/DataTypeService.cs b/src/Umbraco.Core/Services/Implement/DataTypeService.cs index e06c49cf44..5a93fb91b1 100644 --- a/src/Umbraco.Core/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/DataTypeService.cs @@ -466,7 +466,7 @@ namespace Umbraco.Core.Services.Implement } } - public IReadOnlyDictionary> FindUsages(int id) + public IReadOnlyDictionary> GetReferences(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete:true)) { diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 3600db32de..dad3ec4fc0 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -330,7 +330,7 @@ namespace Umbraco.Tests.TestHelpers throw new NotImplementedException(); } - public IReadOnlyDictionary> FindUsages(int id) + public IReadOnlyDictionary> GetReferences(int id) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js index f20debc9c3..6400f7386f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js @@ -45,108 +45,26 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.dataTypeResource#getRelations + * @name umbraco.resources.dataTypeResource#getReferences * @methodOf umbraco.resources.dataTypeResource * * @description * Retrieves relations of a given data type. * - * ##usage - *
-         * dataTypeResource.getRelation(1234)
-         *    .then(function(relations) {
-         *        alert('its gone!');
-         *    });
-         * 
- * * @param {Int} id id of datatype to retrieve relations for * @returns {Promise} resourcePromise object. * */ - getRelations: function (id) { - - return $q((resolve, reject) => { - - -var fakeData = { - "documentTypes": [ - { - "name": "Home", - "alias": "home", - "icon": "icon-home", - "udi": "document-type://B6C7EFA7-1D03-46BF-B6D3-9187EF4CD176", - "id": 1234, - "properties": [ - { - "alias": "bodyText", - "name": "Body Text" - }, - { - "alias": "description", - "name": "Description" - } - ] - }, - { - "name": "News", - "alias": "news", - "icon": "icon-newspaper", - "udi": "document-type://FAB4F78E-530E-4F5D-959A-82441958460C", - "id": 9876, - "properties": [ - { - "alias": "article", - "name": "Article" - } - ] - } - ], - "mediaTypes": [ - { - "name": "Image", - "alias": "image", - "icon": "icon-umb-media", - "udi": "media-type://488152AF-0C03-4F2D-9DE0-FDECC80212AD", - "id": 9999, - "properties": [ - { - "alias": "altText", - "name": "Alt Text" - } - ] - } - ], - "memberTypes": [ - { - "name": "Member", - "alias": "member", - "icon": "icon-people-alt-2", - "udi": "member-type://93773B7C-2D83-4529-92D9-B9759220384C", - "id": 7777, - "properties": [ - { - "alias": "bio", - "name": "Bio" - } - ] - } - ] -}; - - resolve(fakeData); - }); - /* - - TODO: get real data from server! + getReferences: function (id) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "dataTypeApiBaseUrl", - "GetRelations", - [{ id: id }])), - "Failed to retrieve relations for data type of id " + id); - */ + "GetReferences", + { id: id })), + "Failed to retrieve usages for data type of id " + id); + }, /** diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js index 83d6e4a379..db97053064 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js @@ -7,26 +7,26 @@ * The controller for the relations view of the datatype editor */ function DataTypeRelationsController($scope, $routeParams, dataTypeResource) { - + var vm = this; vm.relations = {}; vm.hasRelations = false; - + vm.view = {}; vm.view.loading = true; //we are editing so get the content item from the server - dataTypeResource.getRelations($routeParams.id) - .then(function(data) { + dataTypeResource.getReferences($routeParams.id) + .then(function (data) { - vm.view.loading = false; - vm.relations = data; + vm.view.loading = false; + vm.relations = data; - vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; + vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; - }); + }); diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 30c4162252..ca261b3043 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -283,6 +283,59 @@ namespace Umbraco.Web.Editors : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } + /// + /// Returns the references (usages) for the data type + /// + /// + /// + public DataTypeReferences GetReferences(int id) + { + var result = new DataTypeReferences(); + var usages = Services.DataTypeService.GetReferences(id); + + foreach(var groupOfEntityType in usages.GroupBy(x => x.Key.EntityType)) + { + //get all the GUIDs for the content types to find + var guidsAndPropertyAliases = groupOfEntityType.ToDictionary(i => ((GuidUdi)i.Key).Guid, i => i.Value); + + if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType)) + result.DocumentTypes = GetContentTypeUsages(Services.ContentTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases); + else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType)) + result.MediaTypes = GetContentTypeUsages(Services.MediaTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases); + else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType)) + result.MemberTypes = GetContentTypeUsages(Services.MemberTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases); + } + + return result; + } + + /// + /// Maps the found content types and usages to the resulting model + /// + /// + /// + /// + private IEnumerable GetContentTypeUsages( + IEnumerable cts, + IReadOnlyDictionary> usages) + { + return cts.Select(x => new DataTypeReferences.ContentTypeReferences + { + Key = x.Key, + Alias = x.Alias, + Icon = x.Icon, + Name = x.Name, + Udi = new GuidUdi(ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType), x.Key), + //only select matching properties + Properties = x.PropertyTypes.Where(p => usages[x.Key].InvariantContains(p.Alias)) + .Select(p => new DataTypeReferences.ContentTypeReferences.PropertyTypeReferences + { + Alias = p.Alias, + Name = p.Name + }) + }); + } + #region ReadOnly actions to return basic data - allow access for: content ,media, members, settings, developer /// /// Gets the content json for all data types diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs b/src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs new file mode 100644 index 0000000000..dd042660a2 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "dataTypeUsages", Namespace = "")] + public class DataTypeReferences + { + [DataMember(Name = "documentTypes")] + public IEnumerable DocumentTypes { get; set; } = Enumerable.Empty(); + + [DataMember(Name = "mediaTypes")] + public IEnumerable MediaTypes { get; set; } = Enumerable.Empty(); + + [DataMember(Name = "memberTypes")] + public IEnumerable MemberTypes { get; set; } = Enumerable.Empty(); + + [DataContract(Name = "contentType", Namespace = "")] + public class ContentTypeReferences : EntityBasic + { + [DataMember(Name = "properties")] + public object Properties { get; set; } + + [DataContract(Name = "property", Namespace = "")] + public class PropertyTypeReferences + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "alias")] + public string Alias { get; set; } + } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a0ce64943a..f908fc7c30 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -212,6 +212,7 @@ + From 4c131d14fc3f45be9ecbdc3e352be3a95c12322d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2019 15:31:13 +1100 Subject: [PATCH 027/109] ensure the data type refs are only loaded when the tab is activated --- .../datatypes/datatype.edit.controller.js | 9 +---- .../views/datatype.relations.controller.js | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 85d48c8576..5cf7820b6e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -155,13 +155,6 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic eventsService.unsubscribe(evts[e]); } }); - - - - - - - function init() { @@ -227,7 +220,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic }, { "name": values[1], - "alias": "parameters", + "alias": "relations", "icon": "icon-molecular-network", "view": "views/datatypes/views/datatype.relations.html" } diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js index db97053064..84f2f5e7c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.controller.js @@ -6,9 +6,11 @@ * @description * The controller for the relations view of the datatype editor */ -function DataTypeRelationsController($scope, $routeParams, dataTypeResource) { +function DataTypeRelationsController($scope, $routeParams, dataTypeResource, eventsService, $timeout) { var vm = this; + var evts = []; + var relationsLoaded = false; vm.relations = {}; vm.hasRelations = false; @@ -16,18 +18,36 @@ function DataTypeRelationsController($scope, $routeParams, dataTypeResource) { vm.view = {}; vm.view.loading = true; - //we are editing so get the content item from the server - dataTypeResource.getReferences($routeParams.id) - .then(function (data) { - - vm.view.loading = false; - vm.relations = data; - - vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; - + /** Loads in the data type relations one time */ + function loadRelations() { + if (!relationsLoaded) { + relationsLoaded = true; + dataTypeResource.getReferences($routeParams.id) + .then(function (data) { + vm.view.loading = false; + vm.relations = data; + vm.hasRelations = vm.relations.documentTypes.length > 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; + }); + } + } + // load data type relations when the relations tab is activated + evts.push(eventsService.on("app.tabChange", function (event, args) { + $timeout(function () { + if (args.alias === "relations") { + loadRelations(); + } }); + })); + //ensure to unregister from all events! + $scope.$on('$destroy', function () { + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + }); + + } From cf9add767a24cc19e69585916c16bc9733c756d4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2019 15:55:09 +1100 Subject: [PATCH 028/109] Fixes navigating to other items via url --- src/Umbraco.Web.UI.Client/src/init.js | 4 ++-- src/Umbraco.Web/Editors/DataTypeController.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 2fca5b40d2..fc5b25c4af 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -134,14 +134,14 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params); - //if toRetain is not null it means that there are missing query strings and we need to update the current params + //if toRetain is not null it means that there are missing query strings and we need to update the current params. if (toRetain) { $route.updateParams(toRetain); } //check if the location being changed is only due to global/state query strings which means the location change //isn't actually going to cause a route change. - if (!toRetain && navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) { + if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) { //The location change will cause a route change, continue the route if the query strings haven't been updated. $route.reload(); diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index ca261b3043..31e0d70a42 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -321,6 +321,7 @@ namespace Umbraco.Web.Editors { return cts.Select(x => new DataTypeReferences.ContentTypeReferences { + Id = x.Id, Key = x.Key, Alias = x.Alias, Icon = x.Icon, From 4c344fcb9eb287a62279ab9cd457287670df8fa4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2019 16:07:43 +1100 Subject: [PATCH 029/109] fixes build --- .../Persistence/Repositories/Implement/DataTypeRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs index 41d13007b2..9ccf6e9623 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -298,7 +298,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return dtos.ToDictionary( x => (Udi)new GuidUdi(ObjectTypes.GetUdiType(x.NodeDto.NodeObjectType.Value), x.NodeDto.UniqueId).EnsureClosed(), - x => (IEnumerable)x.PropertyTypes.Select(x => x.Alias).ToList()); + x => (IEnumerable)x.PropertyTypes.Select(p => p.Alias).ToList()); } private string EnsureUniqueNodeName(string nodeName, int id = 0) From dd29fe574557fbed014e332eadec352b0b4289bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 11:37:45 +0200 Subject: [PATCH 030/109] added used by properties --- .../src/common/filters/joinArray.filter.js | 20 ++++++++++++++++ .../src/less/components/umb-table.less | 3 +++ .../datatypes/views/datatype.relations.html | 24 ++++++++++++------- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 6 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js new file mode 100644 index 0000000000..0aa406232c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js @@ -0,0 +1,20 @@ +/** + * @ngdoc filter + * @name umbraco.filters.filter:CMS_joinArray + * @namespace CMS_joinArrayFilter + * + * param {array} array of string or objects, if an object use the third argument to specify which prop to list. + * param {seperator} string containing the seperator to add between joined values. + * param {prop} string used if joining an array of objects, set the name of properties to join. + * + * @description + * Join an array of string or an array of objects, with a costum seperator. + * + */ +angular.module("umbraco.filters").filter('CMS_joinArray', function () { + return function join(array, separator, prop) { + return (!angular.isUndefined(prop) ? array.map(function (item) { + return item[prop]; + }) : array).join(separator); + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 27b64f85fb..963cc56212 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -264,6 +264,9 @@ input.umb-table__input { flex: 0 0 auto !important; } +.umb-table-cell--nano { + flex: 0 0 50px; +} .umb-table-cell--small { flex: .5 .5 1%; max-width: 12.5%; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 4215d3e092..18618ce246 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -28,7 +28,8 @@
Name
Alias
-
+
Used in
+
Open
@@ -36,7 +37,8 @@
{{relation.name}}
{{relation.alias}}
- +
{{relation.properties | CMS_joinArray:', ':'name'}}
+
@@ -51,20 +53,22 @@
-
+
Name
Alias
-
+
Used in
+
Open
-
+
{{relation.name}}
{{relation.alias}}
- +
{{relation.properties | CMS_joinArray:', ':'name'}}
+
@@ -84,15 +88,17 @@
Name
Alias
-
+
Used in
+
Open
-
+
{{relation.name}}
{{relation.alias}}
- +
{{relation.properties | CMS_joinArray:', ':'name'}}
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index e414c04146..40fd5bfdde 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1722,6 +1722,7 @@ Mange hilsner fra Umbraco robotten Ingen relationer til Medie Typer. Brugt af Medlems Typer Ingen relationer til Medlems Typer. + Brugt af diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 732586e3af..99a01f1b1d 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2159,6 +2159,7 @@ To manage your website, simply open the Umbraco back office and start adding con No relations to Media Types. Used by Member Types No relations to Member Types. + Used by diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 1f2c1ddb9b..8a2c03b2fc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2173,5 +2173,6 @@ To manage your website, simply open the Umbraco back office and start adding con No relations to Media Types. Used by Member Types No relations to Member Types. + Used by From fd7b85f447b1ec5b2454c04e0a84133647a15bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 12:54:00 +0200 Subject: [PATCH 031/109] added no overflow for properties --- .../src/less/components/umb-table.less | 11 ++++++--- .../datatypes/views/datatype.relations.html | 24 +++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 963cc56212..56d3b0b87f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -240,7 +240,7 @@ input.umb-table__input { .umb-table-cell { display: flex; flex-flow: row nowrap; - flex: 1 1 1%; //NOTE 1% is a Internet Explore hack, so that cells don't collapse + flex: 1 1 5%; //NOTE 1% is a Internet Explore hack, so that cells don't collapse position: relative; margin: auto 14px; padding: 6px 2px; @@ -253,6 +253,11 @@ input.umb-table__input { white-space: nowrap; //NOTE Disable/Enable this to keep textstring on one line text-overflow: ellipsis; } +.umb-table-cell.--noOverflow > * { + overflow: visible; + white-space: normal; //NOTE Disable/Enable this to keep textstring on one line + text-overflow: unset; +} .umb-table-cell:first-of-type:not(.not-fixed) { flex: 0 0 25px; @@ -283,8 +288,8 @@ input.umb-table__input { // Increases the space for the name cell .umb-table__name { - flex: 1 1 25%; - max-width: 25%; + flex: 1 1 20%; + max-width: 300px; } .umb-table__loading-overlay { diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 18618ce246..640d5dd1dc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -28,16 +28,16 @@
Name
Alias
-
Used in
+
Used in
Open
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
+
{{relation.name}}
+
{{relation.alias}}
+
{{relation.properties | CMS_joinArray:', ':'name'}}
@@ -58,16 +58,16 @@
Name
Alias
-
Used in
+
Used in
Open
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
+
{{relation.name}}
+
{{relation.alias}}
+
{{relation.properties | CMS_joinArray:', ':'name'}}
@@ -88,16 +88,16 @@
Name
Alias
-
Used in
+
Used in
Open
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
+
{{relation.name}}
+
{{relation.alias}}
+
{{relation.properties | CMS_joinArray:', ':'name'}}
From 7024bc495b0c177ce34c1a4a2427a3f10d4223ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 13:03:57 +0200 Subject: [PATCH 032/109] set template to cache output since its not going to update unless you reloaded the view. --- .../src/common/resources/datatype.resource.js | 2 +- .../datatypes/views/datatype.relations.html | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js index 6400f7386f..6b6fb2bb71 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js @@ -55,7 +55,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { * @returns {Promise} resourcePromise object. * */ - getReferences: function (id) { + getReferences: function (id) { return umbRequestHelper.resourcePromise( $http.get( diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index 640d5dd1dc..bbc451c568 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -35,10 +35,10 @@
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
- +
{{::relation.name}}
+
{{::relation.alias}}
+
{{::relation.properties | CMS_joinArray:', ':'name'}}
+
@@ -65,10 +65,10 @@
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
- +
{{::relation.name}}
+
{{::relation.alias}}
+
{{::relation.properties | CMS_joinArray:', ':'name'}}
+
@@ -95,10 +95,10 @@
-
{{relation.name}}
-
{{relation.alias}}
-
{{relation.properties | CMS_joinArray:', ':'name'}}
- +
{{::relation.name}}
+
{{::relation.alias}}
+
{{::relation.properties | CMS_joinArray:', ':'name'}}
+
From 2d693ddd350cecd093cfa2f0ea33b641cd462361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 13:05:47 +0200 Subject: [PATCH 033/109] ups, misspelling of localization key --- .../src/views/datatypes/views/datatype.relations.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html index bbc451c568..db1c2e5e40 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html @@ -28,7 +28,7 @@
Name
Alias
-
Used in
+
Used in
Open
@@ -58,7 +58,7 @@
Name
Alias
-
Used in
+
Used in
Open
@@ -88,7 +88,7 @@
Name
Alias
-
Used in
+
Used in
Open
From 18e8caeb7e80a6248371b93f3d6b3050d213f349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:00:34 +0200 Subject: [PATCH 034/109] adding new localizations --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4332 +++++++++-------- .../Umbraco/config/lang/en_us.xml | 2 + 3 files changed, 2171 insertions(+), 2165 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 40fd5bfdde..30c0bab6af 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -564,6 +564,8 @@ Vælg den mappe, der skal flyttes til i træstrukturen nedenfor blev flyttet under + %0% will have the following consequence]]> + I understand this action will delete the properties and data based on this Data Type Dine data er blevet gemt, men før du kan udgive denne side er der nogle fejl der skal rettes: diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 99a01f1b1d..978fa3b3f0 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1,2165 +1,2167 @@ - - - - The Umbraco community - https://our.umbraco.com/documentation/Extending-Umbraco/Language-Files - - - Culture and Hostnames - Audit Trail - Browse Node - Change Document Type - Copy - Create - Export - Create Package - Create group - Delete - Disable - Empty recycle bin - Enable - Export Document Type - Import Document Type - Import Package - Edit in Canvas - Exit - Move - Notifications - Public access - Publish - Unpublish - Reload - Republish entire site - Rename - Restore - Set permissions for the page %0% - Choose where to copy - Choose where to move - to in the tree structure below - was moved to - was copied to - was deleted - Permissions - Rollback - Send To Publish - Send To Translation - Set group - Sort - Translate - Update - Set permissions - Unlock - Create Content Template - Resend Invitation - - - Content - Administration - Structure - Other - - - Allow access to assign culture and hostnames - Allow access to view a node's history log - Allow access to view a node - Allow access to change document type for a node - Allow access to copy a node - Allow access to create nodes - Allow access to delete nodes - Allow access to move a node - Allow access to set and change public access for a node - Allow access to publish a node - Allow access to unpublish a node - Allow access to change permissions for a node - Allow access to roll back a node to a previous state - Allow access to send a node for approval before publishing - Allow access to send a node for translation - Allow access to change the sort order for nodes - Allow access to translate a node - Allow access to save a node - Allow access to create a Content Template - - - Content - Info - - - Permission denied. - Add new Domain - remove - Invalid node. - One or more domains have an invalid format. - Domain has already been assigned. - Language - Domain - New domain '%0%' has been created - Domain '%0%' is deleted - Domain '%0%' has already been assigned - Domain '%0%' has been updated - Edit Current Domains - - - Inherit - Culture - - or inherit culture from parent nodes. Will also apply
- to the current node, unless a domain below applies too.]]> -
- Domains - - - Clear selection - Select - Do something else - Bold - Cancel Paragraph Indent - Insert form field - Insert graphic headline - Edit Html - Indent Paragraph - Italic - Center - Justify Left - Justify Right - Insert Link - Insert local link (anchor) - Bullet List - Numeric List - Insert macro - Insert picture - Publish and close - Publish with descendants - Edit relations - Return to list - Save - Save and close - Save and publish - Save and schedule - Save and send for approval - Save list view - Schedule - Preview - Preview is disabled because there's no template assigned - Choose style - Show styles - Insert table - Save and generate models - Undo - Redo - Delete tag - Cancel - Confirm - More publishing options - - - Viewing for - Content deleted - Content unpublished - Content saved and Published - Content saved and published for languages: %0% - Content saved - Content saved for languages: %0% - Content moved - Content copied - Content rolled back - Content sent for publishing - Content sent for publishing for languages: %0% - Sort child items performed by user - Copy - Publish - Publish - Move - Save - Save - Delete - Unpublish - Rollback - Send To Publish - Send To Publish - Sort - History (all variants) - - - To change the document type for the selected content, first select from the list of valid types for this location. - Then confirm and/or amend the mapping of properties from the current type to the new, and click Save. - The content has been re-published. - Current Property - Current type - The document type cannot be changed, as there are no alternatives valid for this location. An alternative will be valid if it is allowed under the parent of the selected content item and that all existing child content items are allowed to be created under it. - Document Type Changed - Map Properties - Map to Property - New Template - New Type - none - Content - Select New Document Type - The document type of the selected content has been successfully changed to [new type] and the following properties mapped: - to - Could not complete property mapping as one or more properties have more than one mapping defined. - Only alternate types valid for the current location are displayed. - - - Failed to create a folder under parent with ID %0% - Failed to create a folder under parent with name %0% - The folder name cannot contain illegal characters. - Failed to delete item: %0% - - - Is Published - About this page - Alias - (how would you describe the picture over the phone) - Alternative Links - Click to edit this item - Created by - Original author - Updated by - Created - Date/time this document was created - Document Type - Editing - Remove at - This item has been changed after publication - This item is not published - Last published - There are no items to show - There are no items to show in the list. - No content has been added - No members have been added - Media Type - Link to media item(s) - Member Group - Role - Member Type - No changes have been made - No date chosen - Page title - This media item has no link - Properties - This document is published but is not visible because the parent '%0%' is unpublished - This culture is published but is not visible because it is unpublished on parent '%0%' - This document is published but is not in the cache - Could not get the url - This document is published but its url would collide with content %0% - This document is published but its url cannot be routed - Publish - Published - Published (pending changes) - Publication Status - Publish with descendants to publish %0% and all content items underneath and thereby making their content publicly available.]]> - Publish with descendants to publish the selected languages and the same languages of content items underneath and thereby making their content publicly available.]]> - Publish at - Unpublish at - Clear Date - Set date - Sortorder is updated - To sort the nodes, simply drag the nodes or click one of the column headers. You can select multiple nodes by holding the "shift" or "control" key while selecting - Statistics - Title (optional) - Alternative text (optional) - Type - Unpublish - Unpublished - Last edited - Date/time this document was edited - Remove file(s) - Link to document - Member of group(s) - Not a member of group(s) - Child items - Target - This translates to the following time on the server: - What does this mean?]]> - Are you sure you want to delete this item? - Property %0% uses editor %1% which is not supported by Nested Content. - No content types are configured for this property. - Add another text box - Remove this text box - Content root - Include drafts: also publish unpublished content items. - This value is hidden. If you need access to view this value please contact your website administrator. - This value is hidden. - What languages would you like to publish? All languages with content are saved! - What languages would you like to publish? - What languages would you like to save? - All languages with content are saved on creation! - What languages would you like to send for approval? - What languages would you like to schedule? - Select the languages to unpublish. Unpublishing a mandatory language will unpublish all languages. - Published Languages - Unpublished Languages - Unmodified Languages - These languages haven't been created - Ready to Publish? - Ready to Save? - Send for approval - Select the date and time to publish and/or unpublish the content item. - Create new - Paste from clipboard - - - Create a new Content Template from '%0%' - Blank - Select a Content Template - Content Template created - A Content Template was created from '%0%' - Another Content Template with the same name already exists - A Content Template is predefined content that an editor can select to use as the basis for creating new content - - - Click to upload - or click here to choose files - You can drag files here to upload - Cannot upload this file, it does not have an approved file type - Max file size is - Media root - Failed to move media - Failed to copy media - Failed to create a folder under parent id %0% - Failed to rename the folder with id %0% - Drag and drop your file(s) into the area - - - Create a new member - All Members - Member groups have no additional properties for editing. - - - Where do you want to create the new %0% - Create an item under - Select the document type you want to make a content template for - Enter a folder name - Choose a type and a title - Document Types within the Settings section, by editing the Allowed child node types under Permissions.]]> - The selected page in the content tree doesn't allow for any pages to be created below it. - Edit permissions for this document type - Media Types Types within the Settings section, by editing the Allowed child node types under Permissions.]]> - The selected media in the tree doesn't allow for any other media to be created below it. - Edit permissions for this media type - Document Type without a template - New folder - New data type - New JavaScript file - New empty partial view - New partial view macro - New partial view from snippet - New partial view macro from snippet - New partial view macro (without macro) - New style sheet file - New Rich Text Editor style sheet file - - - Browse your website - - Hide - If Umbraco isn't opening, you might need to allow popups from this site - has opened in a new window - Restart - Visit - Welcome - - - Stay - Discard changes - You have unsaved changes - Are you sure you want to navigate away from this page? - you have unsaved changes - Publishing will make the selected items visible on the site. - Unpublishing will remove the selected items and all their descendants from the site. - Unpublishing will remove this page and all its descendants from the site. - You have unsaved changes. Making changes to the Document Type will discard the changes. - - - Done - Deleted %0% item - Deleted %0% items - Deleted %0% out of %1% item - Deleted %0% out of %1% items - Published %0% item - Published %0% items - Published %0% out of %1% item - Published %0% out of %1% items - Unpublished %0% item - Unpublished %0% items - Unpublished %0% out of %1% item - Unpublished %0% out of %1% items - Moved %0% item - Moved %0% items - Moved %0% out of %1% item - Moved %0% out of %1% items - Copied %0% item - Copied %0% items - Copied %0% out of %1% item - Copied %0% out of %1% items - - - Link title - Link - Anchor / querystring - Name - Manage hostnames - Close this window - Are you sure you want to delete - Are you sure you want to disable - Are you sure? - Are you sure? - Cut - Edit Dictionary Item - Edit Language - Insert local link - Insert character - Insert graphic headline - Insert picture - Insert link - Click to add a Macro - Insert table - This will delete the language - Last Edited - Link - Internal link: - When using local links, insert "#" in front of link - Open in new window? - Macro Settings - This macro does not contain any properties you can edit - Paste - Edit permissions for - Set permissions for - Set permissions for %0% for user group %1% - Select the users groups you want to set permissions for - The items in the recycle bin are now being deleted. Please do not close this window while this operation takes place - The recycle bin is now empty - When items are deleted from the recycle bin, they will be gone forever - regexlib.com's webservice is currently experiencing some problems, which we have no control over. We are very sorry for this inconvenience.]]> - Search for a regular expression to add validation to a form field. Example: 'email, 'zip-code' 'url' - Remove Macro - Required Field - Site is reindexed - The website cache has been refreshed. All publish content is now up to date. While all unpublished content is still unpublished - The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished. - Number of columns - Number of rows - Click on the image to see full size - Pick item - View Cache Item - Relate to original - Include descendants - The friendliest community - Link to page - Opens the linked document in a new window or tab - Link to media - Select content start node - Select media - Select media type - Select icon - Select item - Select link - Select macro - Select content - Select content type - Select media start node - Select member - Select member group - Select member type - Select node - Select sections - Select users - No icons were found - There are no parameters for this macro - There are no macros available to insert - External login providers - Exception Details - Stacktrace - Inner Exception - Link your - Un-link your - account - Select editor - Select snippet - This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead. - - - There are no dictionary items. - - - %0%' below - ]]> - Culture Name - - Dictionary overview - - - Configured Searchers - Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher) - Field values - Health status - The health status of the index and if it can be read - Indexers - Index info - Lists the properties of the index - Manage Examine's indexes - Allows you to view the details of each index and provides some tools for managing the indexes - Rebuild index - - Depending on how much content there is in your site this could take a while.
- It is not recommended to rebuild an index during times of high website traffic or when editors are editing content. - ]]> -
- Searchers - Search the index and view the results - Tools - Tools to manage the index - - - Enter your username - Enter your password - Confirm your password - Name the %0%... - Enter a name... - Enter an email... - Enter a username... - Label... - Enter a description... - Type to search... - Type to filter... - Type to add tags (press enter after each tag)... - Enter your email - Enter a message... - Your username is usually your email - #value or ?key=value - Enter alias... - Generating alias... - Create item - Edit - Name - - - Create custom list view - Remove custom list view - A content type, media type or member type with this alias already exists - - - Renamed - Enter a new folder name here - %0% was renamed to %1% - - - Add prevalue - Database datatype - Property editor GUID - Property editor - Buttons - Enable advanced settings for - Enable context menu - Maximum default size of inserted images - Related stylesheets - Show label - Width and height - All property types & property data - using this data type will be deleted permanently, please confirm you want to delete these as well - Yes, delete - and all property types & property data using this data type - Select the folder to move - to in the tree structure below - was moved underneath - - - Your data has been saved, but before you can publish this page there are some errors you need to fix first: - The current membership provider does not support changing password (EnablePasswordRetrieval need to be true) - %0% already exists - There were errors: - There were errors: - The password should be a minimum of %0% characters long and contain at least %1% non-alpha numeric character(s) - %0% must be an integer - The %0% field in the %1% tab is mandatory - %0% is a mandatory field - %0% at %1% is not in a correct format - %0% is not in a correct format - - - Received an error from the server - The specified file type has been disallowed by the administrator - NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough. - Please fill both alias and name on the new property type! - There is a problem with read/write access to a specific file or folder - Error loading Partial View script (file: %0%) - Please enter a title - Please choose a type - You're about to make the picture larger than the original size. Are you sure that you want to proceed? - Startnode deleted, please contact your administrator - Please mark content before changing style - No active styles available - Please place cursor at the left of the two cells you wish to merge - You cannot split a cell that hasn't been merged. - This property is invalid - - - About - Action - Actions - Add - Alias - All - Are you sure? - Back - Back to overview - Border - by - Cancel - Cell margin - Choose - Close - Close Window - Comment - Confirm - Constrain - Constrain proportions - Content - Continue - Copy - Create - Database - Date - Default - Delete - Deleted - Deleting... - Design - Dictionary - Dimensions - Down - Download - Edit - Edited - Elements - Email - Error - Field - Find - First - General - Groups - Group - Height - Help - Hide - History - Icon - Id - Import - Include subfolders in search - Info - Inner margin - Insert - Install - Invalid - Justify - Label - Language - Last - Layout - Links - Loading - Locked - Login - Log off - Logout - Macro - Mandatory - Message - Move - Name - New - Next - No - of - Off - OK - Open - Options - On - or - Order by - Password - Path - One moment please... - Previous - Properties - Rebuild - Email to receive form data - Recycle Bin - Your recycle bin is empty - Reload - Remaining - Remove - Rename - Renew - Required - Retrieve - Retry - Permissions - Scheduled Publishing - Search - Sorry, we can not find what you are looking for. - No items have been added - Server - Settings - Show - Show page on Send - Size - Sort - Status - Submit - Type - Type to search... - under - Up - Update - Upgrade - Upload - Url - User - Username - Value - View - Welcome... - Width - Yes - Folder - Search results - Reorder - I am done reordering - Preview - Change password - to - List view - Saving... - current - Embed - selected - - - Blue - - - Add group - Add property - Add editor - Add template - Add child node - Add child - Edit data type - Navigate sections - Shortcuts - show shortcuts - Toggle list view - Toggle allow as root - Comment/Uncomment lines - Remove line - Copy Lines Up - Copy Lines Down - Move Lines Up - Move Lines Down - General - Editor - Toggle allow culture variants - - - Background colour - Bold - Text colour - Font - Text - - - Page - - - The installer cannot connect to the database. - Could not save the web.config file. Please modify the connection string manually. - Your database has been found and is identified as - Database configuration - - install button to install the Umbraco %0% database - ]]> - - Next to proceed.]]> - Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

-

To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

-

- Click the retry button when - done.
- More information on editing web.config here.

]]>
- - Please contact your ISP if necessary. - If you're installing on a local machine or server you might need information from your system administrator.]]> - - Press the upgrade button to upgrade your database to Umbraco %0%

-

- Don't worry - no content will be deleted and everything will continue working afterwards! -

- ]]>
- Press Next to - proceed. ]]> - next to continue the configuration wizard]]> - The Default users' password needs to be changed!]]> - The Default user has been disabled or has no access to Umbraco!

No further actions needs to be taken. Click Next to proceed.]]> - The Default user's password has been successfully changed since the installation!

No further actions needs to be taken. Click Next to proceed.]]> - The password is changed! - Get a great start, watch our introduction videos - By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this Umbraco distribution consists of two different licenses, the open source MIT license for the framework and the Umbraco freeware license that covers the UI. - Not installed yet. - Affected files and folders - More information on setting up permissions for Umbraco here - You need to grant ASP.NET modify permissions to the following files/folders - Your permission settings are almost perfect!

- You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
- How to Resolve - Click here to read the text version - video tutorial on setting up folder permissions for Umbraco or read the text version.]]> - Your permission settings might be an issue! -

- You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
- Your permission settings are not ready for Umbraco! -

- In order to run Umbraco, you'll need to update your permission settings.]]>
- Your permission settings are perfect!

- You are ready to run Umbraco and install packages!]]>
- Resolving folder issue - Follow this link for more information on problems with ASP.NET and creating folders - Setting up folder permissions - - I want to start from scratch - learn how) - You can still choose to install Runway later on. Please go to the Developer section and choose Packages. - ]]> - You've just set up a clean Umbraco platform. What do you want to do next? - Runway is installed - - This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules - ]]> - Only recommended for experienced users - I want to start with a simple website - - "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, - but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, - Runway offers an easy foundation based on best practices to get you started faster than ever. - If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. -

- - Included with Runway: Home page, Getting Started page, Installing Modules page.
- Optional Modules: Top Navigation, Sitemap, Contact, Gallery. -
- ]]>
- What is Runway - Step 1/5 Accept license - Step 2/5: Database configuration - Step 3/5: Validating File Permissions - Step 4/5: Check Umbraco security - Step 5/5: Umbraco is ready to get you started - Thank you for choosing Umbraco - Browse your new site -You installed Runway, so why not see how your new website looks.]]> - Further help and information -Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> - Umbraco %0% is installed and ready for use - /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> - started instantly by clicking the "Launch Umbraco" button below.
If you are new to Umbraco, -you can find plenty of resources on our getting started pages.]]>
- Launch Umbraco -To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]> - Connection to database failed. - Umbraco Version 3 - Umbraco Version 4 - Watch - Umbraco %0% for a fresh install or upgrading from version 3.0. -

- Press "next" to start the wizard.]]>
- - - Culture Code - Culture Name - - - You've been idle and logout will automatically occur in - Renew now to save your work - - - Happy super Sunday - Happy manic Monday - Happy tubular Tuesday - Happy wonderful Wednesday - Happy thunderous Thursday - Happy funky Friday - Happy Caturday - Log in below - Sign in with - Session timed out - © 2001 - %0%
Umbraco.com

]]>
- Forgotten password? - An email will be sent to the address specified with a link to reset your password - An email with password reset instructions will be sent to the specified address if it matched our records - Show password - Hide password - Return to login form - Please provide a new password - Your Password has been updated - The link you have clicked on is invalid or has expired - Umbraco: Reset Password - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Password reset requested -

-

- Your username to login to the Umbraco back-office is: %0% -

-

- - - - - - -
- - Click this link to reset your password - -
-

-

If you cannot click on the link, copy and paste this URL into your browser window:

- - - - -
- - %1% - -
-

-
-
-


-
-
- - - ]]>
- - - Dashboard - Sections - Content - - - Choose page above... - %0% has been copied to %1% - Select where the document %0% should be copied to below - %0% has been moved to %1% - Select where the document %0% should be moved to below - has been selected as the root of your new content, click 'ok' below. - No node selected yet, please select a node in the list above before clicking 'ok' - The current node is not allowed under the chosen node because of its type - The current node cannot be moved to one of its subpages - The current node cannot exist at the root - The action isn't allowed since you have insufficient permissions on 1 or more child documents. - Relate copied items to original - - - %0%]]> - Notification settings saved for - - The following languages have been modified %0% - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Hi %0%, -

-

- This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' -

- - - - - - -
- -
- EDIT
-
-

-

Update summary:

- %6% -

-

- Have a nice day!

- Cheers from the Umbraco robot -

-
-
-


-
-
- - - ]]>
- The following languages have been modified:

- %0% - ]]>
- [%0%] Notification about %1% performed on %2% - Notifications - - - Actions - Created - Create package - - button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. - ]]> - This will delete the package - Drop to upload - Include all child nodes - or click here to choose package file - Upload package - Install a local package by selecting it from your machine. Only install packages from sources you know and trust - Upload another package - Cancel and upload another package - I accept - terms of use - - Path to file - Absolute path to file (ie: /bin/umbraco.bin) - Installed - Installed packages - Install local - Finish - This package has no configuration view - No packages have been created yet - You don’t have any packages installed - 'Packages' icon in the top right of your screen]]> - Package Actions - Author URL - Package Content - Package Files - Icon URL - Install package - License - License URL - Package Properties - Search for packages - Results for - We couldn’t find anything for - Please try searching for another package or browse through the categories - Popular - New releases - has - karma points - Information - Owner - Contributors - Created - Current version - .NET version - Downloads - Likes - Compatibility - This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be guaranteed for versions reported below 100% - External sources - Author - Documentation - Package meta data - Package name - Package doesn't contain any items -
- You can safely remove this from the system by clicking "uninstall package" below.]]>
- Package options - Package readme - Package repository - Confirm package uninstall - Package was uninstalled - The package was successfully uninstalled - Uninstall package - - Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, - so uninstall with caution. If in doubt, contact the package author.]]> - Package version - Package already installed - This package cannot be installed, it requires a minimum Umbraco version of - Uninstalling... - Downloading... - Importing... - Installing... - Restarting, please wait... - All done, your browser will now refresh, please wait... - Please click 'Finish' to complete installation and reload the page. - Uploading package... - - - Paste with full formatting (Not recommended) - The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web. - Paste as raw text without any formatting at all - Paste, but remove formatting (Recommended) - - - Group based protection - If you want to grant access to all members of specific member groups - You need to create a member group before you can use group based authentication - Error Page - Used when people are logged on, but do not have access - %0%]]> - %0% is now protected]]> - %0%]]> - Login Page - Choose the page that contains the login form - Remove protection... - %0%?]]> - Select the pages that contain login form and error messages - %0%]]> - %0%]]> - Specific members protection - If you wish to grant access to specific members - - - - - - - - - Include unpublished subpages - Publishing in progress - please wait... - %0% out of %1% pages have been published... - %0% has been published - %0% and subpages have been published - Publish %0% and all its subpages - Publish to publish %0% and thereby making its content publicly available.

- You can publish this page and all its subpages by checking Include unpublished subpages below. - ]]>
- - - You have not configured any approved colours - - - You can only select items of type(s): %0% - You have picked a content item currently deleted or in the recycle bin - You have picked content items currently deleted or in the recycle bin - - - Deleted item - You have picked a media item currently deleted or in the recycle bin - You have picked media items currently deleted or in the recycle bin - Trashed - - - enter external link - choose internal page - Caption - Link - Open in new window - enter the display caption - Enter the link - - - Reset crop - Save crop - Add new crop - Done - Undo edits - - - Select a version to compare with the current version - Current version - Red text will not be shown in the selected version. , green means added]]> - Document has been rolled back - This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view - Rollback to - Select version - View - - - Edit script file - - - Concierge - Content - Courier - Developer - Forms - Help - Umbraco Configuration Wizard - Media - Members - Newsletters - Packages - Settings - Statistics - Translation - Users - - - The best Umbraco video tutorials - - - Default template - To import a document type, find the ".udt" file on your computer by clicking the "Browse" button and click "Import" (you'll be asked for confirmation on the next screen) - New Tab Title - Node type - Type - Stylesheet - Script - Tab - Tab Title - Tabs - Master Content Type enabled - This Content Type uses - as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself - No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. - Create matching template - Add icon - - - Sort order - Creation date - Sorting complete. - Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items - - - - Validation - Validation errors must be fixed before the item can be saved - Failed - Saved - Insufficient user permissions, could not complete the operation - Cancelled - Operation was cancelled by a 3rd party add-in - Publishing was cancelled by a 3rd party add-in - Property type already exists - Property type created - DataType: %1%]]> - Propertytype deleted - Document Type saved - Tab created - Tab deleted - Tab with id: %0% deleted - Stylesheet not saved - Stylesheet saved - Stylesheet saved without any errors - Datatype saved - Dictionary item saved - Publishing failed because the parent page isn't published - Content published - and visible on the website - Content saved - Remember to publish to make changes visible - Sent For Approval - Changes have been sent for approval - Media saved - Member group saved - Media saved without any errors - Member saved - Stylesheet Property Saved - Stylesheet saved - Template saved - Error saving user (check log) - User Saved - User type saved - User group saved - File not saved - file could not be saved. Please check file permissions - File saved - File saved without any errors - Language saved - Media Type saved - Member Type saved - Member Group saved - Template not saved - Please make sure that you do not have 2 templates with the same alias - Template saved - Template saved without any errors! - Content unpublished - Partial view saved - Partial view saved without any errors! - Partial view not saved - An error occurred saving the file. - Permissions saved for - Deleted %0% user groups - %0% was deleted - Enabled %0% users - Disabled %0% users - %0% is now enabled - %0% is now disabled - User groups have been set - Unlocked %0% users - %0% is now unlocked - Member was exported to file - An error occurred while exporting the member - User %0% was deleted - Invite user - Invitation has been re-sent to %0% - Document type was exported to file - An error occurred while exporting the document type - - - Add style - Edit style - Rich text editor styles - Define the styles that should be available in the rich text editor for this stylesheet - Edit stylesheet - Edit stylesheet property - The name displayed in the editor style selector - Preview - How the text will look like in the rich text editor. - Selector - Uses CSS syntax, e.g. "h1" or ".redHeader" - Styles - The CSS that should be applied in the rich text editor, e.g. "color:red;" - Code - Editor - - - Failed to delete template with ID %0% - Edit template - Sections - Insert content area - Insert content area placeholder - Insert - Choose what to insert into your template - Dictionary item - A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. - Macro - - A Macro is a configurable component which is great for - reusable parts of your design, where you need the option to provide parameters, - such as galleries, forms and lists. - - Value - Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. - Partial view - - A partial view is a separate template file which can be rendered inside another - template, it's great for reusing markup or for separating complex templates into separate files. - - Master template - No master - Render child template - @RenderBody() placeholder. - ]]> - Define a named section - @section { ... }. This can be rendered in a - specific area of the parent of this template, by using @RenderSection. - ]]> - Render a named section - @RenderSection(name) placeholder. - This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. - ]]> - Section Name - Section is mandatory - - If mandatory, the child template must contain a @section definition, otherwise an error is shown. - - Query builder - items returned, in - I want - all content - content of type "%0%" - from - my website - where - and - is - is not - before - before (including selected date) - after - after (including selected date) - equals - does not equal - contains - does not contain - greater than - greater than or equal to - less than - less than or equal to - Id - Name - Created Date - Last Updated Date - order by - ascending - descending - Template - - - Image - Macro - Choose type of content - Choose a layout - Add a row - Add content - Drop content - Settings applied - This content is not allowed here - This content is allowed here - Click to embed - Click to insert image - Image caption... - Write here... - Grid Layouts - Layouts are the overall work area for the grid editor, usually you only need one or two different layouts - Add Grid Layout - Adjust the layout by setting column widths and adding additional sections - Row configurations - Rows are predefined cells arranged horizontally - Add row configuration - Adjust the row by setting cell widths and adding additional cells - Columns - Total combined number of columns in the grid layout - Settings - Configure what settings editors can change - Styles - Configure what styling editors can change - Allow all editors - Allow all row configurations - Maximum items - Leave blank or set to 0 for unlimited - Set as default - Choose extra - Choose default - are added - - - Compositions - Group - You have not added any groups - Add group - Inherited from - Add property - Required label - Enable list view - Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree - Allowed Templates - Choose which templates editors are allowed to use on content of this type - Allow as root - Allow editors to create content of this type in the root of the content tree. - Allowed child node types - Allow content of the specified types to be created underneath content of this type. - Choose child node - Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. - This content type is used in a composition, and therefore cannot be composed itself. - There are no content types available to use as a composition. - Create new - Use existing - Editor settings - Configuration - Yes, delete - was moved underneath - was copied underneath - Select the folder to move - Select the folder to copy - to in the tree structure below - All Document types - All Documents - All media items - using this document type will be deleted permanently, please confirm you want to delete these as well. - using this media type will be deleted permanently, please confirm you want to delete these as well. - using this member type will be deleted permanently, please confirm you want to delete these as well - and all documents using this type - and all media items using this type - and all members using this type - Member can edit - Allow this property value to be edited by the member on their profile page - Is sensitive data - Hide this property value from content editors that don't have access to view sensitive information - Show on member profile - Allow this property value to be displayed on the member profile page - tab has no sort order - Where is this composition used? - This composition is currently used in the composition of the following content types: - Allow varying by culture - Allow editors to create content of this type in different languages. - Allow varying by culture - Element type - Is an Element type - An Element type is meant to be used for instance in Nested Content, and not in the tree. - This is not applicable for an Element type - You have made changes to this property. Are you sure you want to discard them? - - - Add language - Mandatory language - Properties on this language have to be filled out before the node can be published. - Default language - An Umbraco site can only have one default language set. - Switching default language may result in default content missing. - Falls back to - No fall back language - To allow multi-lingual content to fall back to another language if not present in the requested language, select it here. - Fall back language - - - - Add parameter - Edit parameter - Enter macro name - Parameters - Define the parameters that should be available when using this macro. - Select partial view macro file - - - Building models - this can take a bit of time, don't worry - Models generated - Models could not be generated - Models generation has failed, see exception in U log - - - Add fallback field - Fallback field - Add default value - Default value - Fallback field - Default value - Casing - Encoding - Choose field - Convert line breaks - Yes, convert line breaks - Replaces line breaks with 'br' html tag - Custom Fields - Date only - Format and encoding - Format as date - Format the value as a date, or a date with time, according to the active culture - HTML encode - Will replace special characters by their HTML equivalent. - Will be inserted after the field value - Will be inserted before the field value - Lowercase - Modify output - None - Output sample - Insert after field - Insert before field - Recursive - Yes, make it recursive - Separator - Standard Fields - Uppercase - URL encode - Will format special characters in URLs - Will only be used when the field values above are empty - This field will only be used if the primary field is empty - Date and time - - - Translation details - Download XML DTD - Fields - Include subpages - - No translator users found. Please create a translator user before you start sending content to translation - The page '%0%' has been send to translation - Send the page '%0%' to translation - Total words - Translate to - Translation completed. - You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages. - Translation failed, the XML file might be corrupt - Translation options - Translator - Upload translation XML - - - Content - Content Templates - Media - Cache Browser - Recycle Bin - Created packages - Data Types - Dictionary - Installed packages - Install skin - Install starter kit - Languages - Install local package - Macros - Media Types - Members - Member Groups - Member Roles - Member Types - Document Types - Relation Types - Packages - Packages - Partial Views - Partial View Macro Files - Install from repository - Install Runway - Runway modules - Scripting Files - Scripts - Stylesheets - Templates - Log Viewer - Users - Settings - Templating - Third Party - - - New update ready - %0% is ready, click here for download - No connection to server - Error checking for update. Please review trace-stack for further information - - - Access - Based on the assigned groups and start nodes, the user has access to the following nodes - Assign access - Administrator - Category field - User created - Change Your Password - Change photo - New password - hasn't been locked out - The password hasn't been changed - Confirm new password - You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button - Content Channel - Create another user - Create new users to give them access to Umbraco. When a new user is created a password will be generated that you can share with the user. - Description field - Disable User - Document Type - Editor - Excerpt field - Failed login attempts - Go to user profile - Add groups to assign access and permissions - Invite another user - Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours. - Language - Set the language you will see in menus and dialogs - Last lockout date - Last login - Password last changed - Username - Media start node - Limit the media library to a specific start node - Media start nodes - Limit the media library to specific start nodes - Sections - Disable Umbraco Access - has not logged in yet - Old password - Password - Reset password - Your password has been changed! - Please confirm the new password - Enter your new password - Your new password cannot be blank! - Current password - Invalid current password - There was a difference between the new password and the confirmed password. Please try again! - The confirmed password doesn't match the new password! - Replace child node permissions - You are currently modifying permissions for the pages: - Select pages to modify their permissions - Remove photo - Default permissions - Granular permissions - Set permissions for specific nodes - Profile - Search all children - Add sections to give users access - Select user groups - No start node selected - No start nodes selected - Content start node - Limit the content tree to a specific start node - Content start nodes - Limit the content tree to specific start nodes - User last updated - has been created - The new user has successfully been created. To log in to Umbraco use the password below. - User management - Name - User permissions - User group - has been invited - An invitation has been sent to the new user with details about how to log in to Umbraco. - Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar. - Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it. - Uploading a photo of yourself will make it easy for other users to recognize you. Click the circle above to upload your photo. - Writer - Change - Your profile - Your recent history - Session expires in - Invite user - Create user - Send invite - Back to users - Umbraco: Invitation - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Hi %0%, -

-

- You have been invited by %1% to the Umbraco Back Office. -

-

- Message from %1%: -
- %2% -

- - - - - - -
- - - - - - -
- - Click this link to accept the invite - -
-
-

If you cannot click on the link, copy and paste this URL into your browser window:

- - - - -
- - %3% - -
-

-
-
-


-
-
- - ]]>
- Invite - Resending invitation... - Delete User - Are you sure you wish to delete this user account? - All - Active - Disabled - Locked out - Invited - Inactive - Name (A-Z) - Name (Z-A) - Newest - Oldest - Last login - - - Validation - No validation - Validate as an email address - Validate as a number - Validate as a URL - ...or enter a custom validation - Field is mandatory - Enter a regular expression - You need to add at least - You can only have - items - items selected - Invalid date - Not a number - Invalid email - Custom validation - - - - Value is set to the recommended value: '%0%'. - Value was set to '%1%' for XPath '%2%' in configuration file '%3%'. - Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'. - Found unexpected value '%0%' for '%2%' in configuration file '%3%'. - - Custom errors are set to '%0%'. - Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live. - Custom errors successfully set to '%0%'. - MacroErrors are set to '%0%'. - MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'. - MacroErrors are now set to '%0%'. - - Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'. - Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%). - Try Skip IIS Custom Errors successfully set to '%0%'. - - File does not exist: '%0%'. - '%0%' in config file '%1%'.]]> - There was an error, check log for full error: %0%. - Database - The database schema is correct for this version of Umbraco - %0% problems were detected with your database schema (Check the log for details) - Some errors were detected while validating the database schema against the current version of Umbraco. - Your website's certificate is valid. - Certificate validation error: '%0%' - Your website's SSL certificate has expired. - Your website's SSL certificate is expiring in %0% days. - Error pinging the URL %0% - '%1%' - You are currently %0% viewing the site using the HTTPS scheme. - The appSetting 'Umbraco.Core.UseHttps' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'. - The appSetting 'Umbraco.Core.UseHttps' is set to '%0%' in your web.config file, your cookies are %1% marked as secure. - Could not update the 'Umbraco.Core.UseHttps' setting in your web.config file. Error: %0% - - Enable HTTPS - Sets umbracoSSL setting to true in the appSettings of the web.config file. - The appSetting 'Umbraco.Core.UseHttps' is now set to 'true' in your web.config file, your cookies will be marked as secure. - Fix - Cannot fix a check with a value comparison type of 'ShouldNotEqual'. - Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value. - Value to fix check not provided. - Debug compilation mode is disabled. - Debug compilation mode is currently enabled. It is recommended to disable this setting before go live. - Debug compilation mode successfully disabled. - Trace mode is disabled. - Trace mode is currently enabled. It is recommended to disable this setting before go live. - Trace mode successfully disabled. - All folders have the correct permissions set. - - %0%.]]> - %0%. If they aren't being written to no action need be taken.]]> - All files have the correct permissions set. - - %0%.]]> - %0%. If they aren't being written to no action need be taken.]]> - X-Frame-Options used to control whether a site can be IFRAMEd by another was found.]]> - X-Frame-Options used to control whether a site can be IFRAMEd by another was not found.]]> - Set Header in Config - Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMEd by other websites. - A setting to create a header preventing IFRAMEing of the site by other websites has been added to your web.config file. - Could not update web.config file. Error: %0% - X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was found.]]> - X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was not found.]]> - Adds a value to the httpProtocol/customHeaders section of web.config to protect against MIME sniffing vulnerabilities. - A setting to create a header protecting against MIME sniffing vulnerabilities has been added to your web.config file. - Strict-Transport-Security, also known as the HSTS-header, was found.]]> - Strict-Transport-Security was not found.]]> - Adds the header 'Strict-Transport-Security' with the value 'max-age=10886400' to the httpProtocol/customHeaders section of web.config. Use this fix only if you will have your domains running with https for the next 18 weeks (minimum). - The HSTS header has been added to your web.config file. - X-XSS-Protection was found.]]> - X-XSS-Protection was not found.]]> - Adds the header 'X-XSS-Protection' with the value '1; mode=block' to the httpProtocol/customHeaders section of web.config. - The X-XSS-Protection header has been added to your web.config file. - - %0%.]]> - No headers revealing information about the website technology were found. - In the Web.config file, system.net/mailsettings could not be found. - In the Web.config file system.net/mailsettings section, the host is not configured. - SMTP settings are configured correctly and the service is operating as expected. - The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct. - %0%.]]> - %0%.]]> -

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
- Umbraco Health Check Status: %0% - - - Disable URL tracker - Enable URL tracker - Original URL - Redirected To - Redirect Url Management - The following URLs redirect to this content item: - No redirects have been made - When a published page gets renamed or moved a redirect will automatically be made to the new page. - Are you sure you want to remove the redirect from '%0%' to '%1%'? - Redirect URL removed. - Error removing redirect URL. - This will remove the redirect - Are you sure you want to disable the URL tracker? - URL tracker has now been disabled. - Error disabling the URL tracker, more information can be found in your log file. - URL tracker has now been enabled. - Error enabling the URL tracker, more information can be found in your log file. - - - No Dictionary items to choose from - - - %0% characters left.]]> - %1% too many.]]> - - - Trashed content with Id: {0} related to original parent content with Id: {1} - Trashed media with Id: {0} related to original parent media item with Id: {1} - Cannot automatically restore this item - There is no location where this item can be automatically restored. You can move the item manually using the tree below. - was restored under - - - Direction - Parent to child - Bidirectional - Parent - Child - Count - Relations - Created - Comment - Name - No relations for this relation type. - Relation Type - Relations - - - Getting Started - Redirect URL Management - Content - Welcome - Examine Management - Published Status - Models Builder - Health Check - Profiling - Getting Started - Install Umbraco Forms - - - Go back - Active layout: - Jump to - group - passed - warning - failed - suggestion - Check passed - Check failed - Open backoffice search - Open/Close backoffice help - Open/Close your profile options - - - Relations - This Data Type has no relations. - Used by Document Types - No relations to Document Types. - Used by Media Types - No relations to Media Types. - Used by Member Types - No relations to Member Types. - Used by - - -
+ + + + The Umbraco community + https://our.umbraco.com/documentation/Extending-Umbraco/Language-Files + + + Culture and Hostnames + Audit Trail + Browse Node + Change Document Type + Copy + Create + Export + Create Package + Create group + Delete + Disable + Empty recycle bin + Enable + Export Document Type + Import Document Type + Import Package + Edit in Canvas + Exit + Move + Notifications + Public access + Publish + Unpublish + Reload + Republish entire site + Rename + Restore + Set permissions for the page %0% + Choose where to copy + Choose where to move + to in the tree structure below + was moved to + was copied to + was deleted + Permissions + Rollback + Send To Publish + Send To Translation + Set group + Sort + Translate + Update + Set permissions + Unlock + Create Content Template + Resend Invitation + + + Content + Administration + Structure + Other + + + Allow access to assign culture and hostnames + Allow access to view a node's history log + Allow access to view a node + Allow access to change document type for a node + Allow access to copy a node + Allow access to create nodes + Allow access to delete nodes + Allow access to move a node + Allow access to set and change public access for a node + Allow access to publish a node + Allow access to unpublish a node + Allow access to change permissions for a node + Allow access to roll back a node to a previous state + Allow access to send a node for approval before publishing + Allow access to send a node for translation + Allow access to change the sort order for nodes + Allow access to translate a node + Allow access to save a node + Allow access to create a Content Template + + + Content + Info + + + Permission denied. + Add new Domain + remove + Invalid node. + One or more domains have an invalid format. + Domain has already been assigned. + Language + Domain + New domain '%0%' has been created + Domain '%0%' is deleted + Domain '%0%' has already been assigned + Domain '%0%' has been updated + Edit Current Domains + + + Inherit + Culture + + or inherit culture from parent nodes. Will also apply
+ to the current node, unless a domain below applies too.]]> +
+ Domains + + + Clear selection + Select + Do something else + Bold + Cancel Paragraph Indent + Insert form field + Insert graphic headline + Edit Html + Indent Paragraph + Italic + Center + Justify Left + Justify Right + Insert Link + Insert local link (anchor) + Bullet List + Numeric List + Insert macro + Insert picture + Publish and close + Publish with descendants + Edit relations + Return to list + Save + Save and close + Save and publish + Save and schedule + Save and send for approval + Save list view + Schedule + Preview + Preview is disabled because there's no template assigned + Choose style + Show styles + Insert table + Save and generate models + Undo + Redo + Delete tag + Cancel + Confirm + More publishing options + + + Viewing for + Content deleted + Content unpublished + Content saved and Published + Content saved and published for languages: %0% + Content saved + Content saved for languages: %0% + Content moved + Content copied + Content rolled back + Content sent for publishing + Content sent for publishing for languages: %0% + Sort child items performed by user + Copy + Publish + Publish + Move + Save + Save + Delete + Unpublish + Rollback + Send To Publish + Send To Publish + Sort + History (all variants) + + + To change the document type for the selected content, first select from the list of valid types for this location. + Then confirm and/or amend the mapping of properties from the current type to the new, and click Save. + The content has been re-published. + Current Property + Current type + The document type cannot be changed, as there are no alternatives valid for this location. An alternative will be valid if it is allowed under the parent of the selected content item and that all existing child content items are allowed to be created under it. + Document Type Changed + Map Properties + Map to Property + New Template + New Type + none + Content + Select New Document Type + The document type of the selected content has been successfully changed to [new type] and the following properties mapped: + to + Could not complete property mapping as one or more properties have more than one mapping defined. + Only alternate types valid for the current location are displayed. + + + Failed to create a folder under parent with ID %0% + Failed to create a folder under parent with name %0% + The folder name cannot contain illegal characters. + Failed to delete item: %0% + + + Is Published + About this page + Alias + (how would you describe the picture over the phone) + Alternative Links + Click to edit this item + Created by + Original author + Updated by + Created + Date/time this document was created + Document Type + Editing + Remove at + This item has been changed after publication + This item is not published + Last published + There are no items to show + There are no items to show in the list. + No content has been added + No members have been added + Media Type + Link to media item(s) + Member Group + Role + Member Type + No changes have been made + No date chosen + Page title + This media item has no link + Properties + This document is published but is not visible because the parent '%0%' is unpublished + This culture is published but is not visible because it is unpublished on parent '%0%' + This document is published but is not in the cache + Could not get the url + This document is published but its url would collide with content %0% + This document is published but its url cannot be routed + Publish + Published + Published (pending changes) + Publication Status + Publish with descendants to publish %0% and all content items underneath and thereby making their content publicly available.]]> + Publish with descendants to publish the selected languages and the same languages of content items underneath and thereby making their content publicly available.]]> + Publish at + Unpublish at + Clear Date + Set date + Sortorder is updated + To sort the nodes, simply drag the nodes or click one of the column headers. You can select multiple nodes by holding the "shift" or "control" key while selecting + Statistics + Title (optional) + Alternative text (optional) + Type + Unpublish + Unpublished + Last edited + Date/time this document was edited + Remove file(s) + Link to document + Member of group(s) + Not a member of group(s) + Child items + Target + This translates to the following time on the server: + What does this mean?]]> + Are you sure you want to delete this item? + Property %0% uses editor %1% which is not supported by Nested Content. + No content types are configured for this property. + Add another text box + Remove this text box + Content root + Include drafts: also publish unpublished content items. + This value is hidden. If you need access to view this value please contact your website administrator. + This value is hidden. + What languages would you like to publish? All languages with content are saved! + What languages would you like to publish? + What languages would you like to save? + All languages with content are saved on creation! + What languages would you like to send for approval? + What languages would you like to schedule? + Select the languages to unpublish. Unpublishing a mandatory language will unpublish all languages. + Published Languages + Unpublished Languages + Unmodified Languages + These languages haven't been created + Ready to Publish? + Ready to Save? + Send for approval + Select the date and time to publish and/or unpublish the content item. + Create new + Paste from clipboard + + + Create a new Content Template from '%0%' + Blank + Select a Content Template + Content Template created + A Content Template was created from '%0%' + Another Content Template with the same name already exists + A Content Template is predefined content that an editor can select to use as the basis for creating new content + + + Click to upload + or click here to choose files + You can drag files here to upload + Cannot upload this file, it does not have an approved file type + Max file size is + Media root + Failed to move media + Failed to copy media + Failed to create a folder under parent id %0% + Failed to rename the folder with id %0% + Drag and drop your file(s) into the area + + + Create a new member + All Members + Member groups have no additional properties for editing. + + + Where do you want to create the new %0% + Create an item under + Select the document type you want to make a content template for + Enter a folder name + Choose a type and a title + Document Types within the Settings section, by editing the Allowed child node types under Permissions.]]> + The selected page in the content tree doesn't allow for any pages to be created below it. + Edit permissions for this document type + Media Types Types within the Settings section, by editing the Allowed child node types under Permissions.]]> + The selected media in the tree doesn't allow for any other media to be created below it. + Edit permissions for this media type + Document Type without a template + New folder + New data type + New JavaScript file + New empty partial view + New partial view macro + New partial view from snippet + New partial view macro from snippet + New partial view macro (without macro) + New style sheet file + New Rich Text Editor style sheet file + + + Browse your website + - Hide + If Umbraco isn't opening, you might need to allow popups from this site + has opened in a new window + Restart + Visit + Welcome + + + Stay + Discard changes + You have unsaved changes + Are you sure you want to navigate away from this page? - you have unsaved changes + Publishing will make the selected items visible on the site. + Unpublishing will remove the selected items and all their descendants from the site. + Unpublishing will remove this page and all its descendants from the site. + You have unsaved changes. Making changes to the Document Type will discard the changes. + + + Done + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + + + Link title + Link + Anchor / querystring + Name + Manage hostnames + Close this window + Are you sure you want to delete + Are you sure you want to disable + Are you sure? + Are you sure? + Cut + Edit Dictionary Item + Edit Language + Insert local link + Insert character + Insert graphic headline + Insert picture + Insert link + Click to add a Macro + Insert table + This will delete the language + Last Edited + Link + Internal link: + When using local links, insert "#" in front of link + Open in new window? + Macro Settings + This macro does not contain any properties you can edit + Paste + Edit permissions for + Set permissions for + Set permissions for %0% for user group %1% + Select the users groups you want to set permissions for + The items in the recycle bin are now being deleted. Please do not close this window while this operation takes place + The recycle bin is now empty + When items are deleted from the recycle bin, they will be gone forever + regexlib.com's webservice is currently experiencing some problems, which we have no control over. We are very sorry for this inconvenience.]]> + Search for a regular expression to add validation to a form field. Example: 'email, 'zip-code' 'url' + Remove Macro + Required Field + Site is reindexed + The website cache has been refreshed. All publish content is now up to date. While all unpublished content is still unpublished + The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished. + Number of columns + Number of rows + Click on the image to see full size + Pick item + View Cache Item + Relate to original + Include descendants + The friendliest community + Link to page + Opens the linked document in a new window or tab + Link to media + Select content start node + Select media + Select media type + Select icon + Select item + Select link + Select macro + Select content + Select content type + Select media start node + Select member + Select member group + Select member type + Select node + Select sections + Select users + No icons were found + There are no parameters for this macro + There are no macros available to insert + External login providers + Exception Details + Stacktrace + Inner Exception + Link your + Un-link your + account + Select editor + Select snippet + This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead. + + + There are no dictionary items. + + + %0%' below + ]]> + Culture Name + + Dictionary overview + + + Configured Searchers + Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher) + Field values + Health status + The health status of the index and if it can be read + Indexers + Index info + Lists the properties of the index + Manage Examine's indexes + Allows you to view the details of each index and provides some tools for managing the indexes + Rebuild index + + Depending on how much content there is in your site this could take a while.
+ It is not recommended to rebuild an index during times of high website traffic or when editors are editing content. + ]]> +
+ Searchers + Search the index and view the results + Tools + Tools to manage the index + + + Enter your username + Enter your password + Confirm your password + Name the %0%... + Enter a name... + Enter an email... + Enter a username... + Label... + Enter a description... + Type to search... + Type to filter... + Type to add tags (press enter after each tag)... + Enter your email + Enter a message... + Your username is usually your email + #value or ?key=value + Enter alias... + Generating alias... + Create item + Edit + Name + + + Create custom list view + Remove custom list view + A content type, media type or member type with this alias already exists + + + Renamed + Enter a new folder name here + %0% was renamed to %1% + + + Add prevalue + Database datatype + Property editor GUID + Property editor + Buttons + Enable advanced settings for + Enable context menu + Maximum default size of inserted images + Related stylesheets + Show label + Width and height + All property types & property data + using this data type will be deleted permanently, please confirm you want to delete these as well + Yes, delete + and all property types & property data using this data type + Select the folder to move + to in the tree structure below + was moved underneath + %0% will have the following consequence]]> + I understand this action will delete the properties and data based on this Data Type + + + Your data has been saved, but before you can publish this page there are some errors you need to fix first: + The current membership provider does not support changing password (EnablePasswordRetrieval need to be true) + %0% already exists + There were errors: + There were errors: + The password should be a minimum of %0% characters long and contain at least %1% non-alpha numeric character(s) + %0% must be an integer + The %0% field in the %1% tab is mandatory + %0% is a mandatory field + %0% at %1% is not in a correct format + %0% is not in a correct format + + + Received an error from the server + The specified file type has been disallowed by the administrator + NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough. + Please fill both alias and name on the new property type! + There is a problem with read/write access to a specific file or folder + Error loading Partial View script (file: %0%) + Please enter a title + Please choose a type + You're about to make the picture larger than the original size. Are you sure that you want to proceed? + Startnode deleted, please contact your administrator + Please mark content before changing style + No active styles available + Please place cursor at the left of the two cells you wish to merge + You cannot split a cell that hasn't been merged. + This property is invalid + + + About + Action + Actions + Add + Alias + All + Are you sure? + Back + Back to overview + Border + by + Cancel + Cell margin + Choose + Close + Close Window + Comment + Confirm + Constrain + Constrain proportions + Content + Continue + Copy + Create + Database + Date + Default + Delete + Deleted + Deleting... + Design + Dictionary + Dimensions + Down + Download + Edit + Edited + Elements + Email + Error + Field + Find + First + General + Groups + Group + Height + Help + Hide + History + Icon + Id + Import + Include subfolders in search + Info + Inner margin + Insert + Install + Invalid + Justify + Label + Language + Last + Layout + Links + Loading + Locked + Login + Log off + Logout + Macro + Mandatory + Message + Move + Name + New + Next + No + of + Off + OK + Open + Options + On + or + Order by + Password + Path + One moment please... + Previous + Properties + Rebuild + Email to receive form data + Recycle Bin + Your recycle bin is empty + Reload + Remaining + Remove + Rename + Renew + Required + Retrieve + Retry + Permissions + Scheduled Publishing + Search + Sorry, we can not find what you are looking for. + No items have been added + Server + Settings + Show + Show page on Send + Size + Sort + Status + Submit + Type + Type to search... + under + Up + Update + Upgrade + Upload + Url + User + Username + Value + View + Welcome... + Width + Yes + Folder + Search results + Reorder + I am done reordering + Preview + Change password + to + List view + Saving... + current + Embed + selected + + + Blue + + + Add group + Add property + Add editor + Add template + Add child node + Add child + Edit data type + Navigate sections + Shortcuts + show shortcuts + Toggle list view + Toggle allow as root + Comment/Uncomment lines + Remove line + Copy Lines Up + Copy Lines Down + Move Lines Up + Move Lines Down + General + Editor + Toggle allow culture variants + + + Background colour + Bold + Text colour + Font + Text + + + Page + + + The installer cannot connect to the database. + Could not save the web.config file. Please modify the connection string manually. + Your database has been found and is identified as + Database configuration + + install button to install the Umbraco %0% database + ]]> + + Next to proceed.]]> + Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.

+

To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

+

+ Click the retry button when + done.
+ More information on editing web.config here.

]]>
+ + Please contact your ISP if necessary. + If you're installing on a local machine or server you might need information from your system administrator.]]> + + Press the upgrade button to upgrade your database to Umbraco %0%

+

+ Don't worry - no content will be deleted and everything will continue working afterwards! +

+ ]]>
+ Press Next to + proceed. ]]> + next to continue the configuration wizard]]> + The Default users' password needs to be changed!]]> + The Default user has been disabled or has no access to Umbraco!

No further actions needs to be taken. Click Next to proceed.]]> + The Default user's password has been successfully changed since the installation!

No further actions needs to be taken. Click Next to proceed.]]> + The password is changed! + Get a great start, watch our introduction videos + By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this Umbraco distribution consists of two different licenses, the open source MIT license for the framework and the Umbraco freeware license that covers the UI. + Not installed yet. + Affected files and folders + More information on setting up permissions for Umbraco here + You need to grant ASP.NET modify permissions to the following files/folders + Your permission settings are almost perfect!

+ You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
+ How to Resolve + Click here to read the text version + video tutorial on setting up folder permissions for Umbraco or read the text version.]]> + Your permission settings might be an issue! +

+ You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
+ Your permission settings are not ready for Umbraco! +

+ In order to run Umbraco, you'll need to update your permission settings.]]>
+ Your permission settings are perfect!

+ You are ready to run Umbraco and install packages!]]>
+ Resolving folder issue + Follow this link for more information on problems with ASP.NET and creating folders + Setting up folder permissions + + I want to start from scratch + learn how) + You can still choose to install Runway later on. Please go to the Developer section and choose Packages. + ]]> + You've just set up a clean Umbraco platform. What do you want to do next? + Runway is installed + + This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules + ]]> + Only recommended for experienced users + I want to start with a simple website + + "Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, + but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, + Runway offers an easy foundation based on best practices to get you started faster than ever. + If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages. +

+ + Included with Runway: Home page, Getting Started page, Installing Modules page.
+ Optional Modules: Top Navigation, Sitemap, Contact, Gallery. +
+ ]]>
+ What is Runway + Step 1/5 Accept license + Step 2/5: Database configuration + Step 3/5: Validating File Permissions + Step 4/5: Check Umbraco security + Step 5/5: Umbraco is ready to get you started + Thank you for choosing Umbraco + Browse your new site +You installed Runway, so why not see how your new website looks.]]> + Further help and information +Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> + Umbraco %0% is installed and ready for use + /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> + started instantly by clicking the "Launch Umbraco" button below.
If you are new to Umbraco, +you can find plenty of resources on our getting started pages.]]>
+ Launch Umbraco +To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]> + Connection to database failed. + Umbraco Version 3 + Umbraco Version 4 + Watch + Umbraco %0% for a fresh install or upgrading from version 3.0. +

+ Press "next" to start the wizard.]]>
+ + + Culture Code + Culture Name + + + You've been idle and logout will automatically occur in + Renew now to save your work + + + Happy super Sunday + Happy manic Monday + Happy tubular Tuesday + Happy wonderful Wednesday + Happy thunderous Thursday + Happy funky Friday + Happy Caturday + Log in below + Sign in with + Session timed out + © 2001 - %0%
Umbraco.com

]]>
+ Forgotten password? + An email will be sent to the address specified with a link to reset your password + An email with password reset instructions will be sent to the specified address if it matched our records + Show password + Hide password + Return to login form + Please provide a new password + Your Password has been updated + The link you have clicked on is invalid or has expired + Umbraco: Reset Password + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Password reset requested +

+

+ Your username to login to the Umbraco back-office is: %0% +

+

+ + + + + + +
+ + Click this link to reset your password + +
+

+

If you cannot click on the link, copy and paste this URL into your browser window:

+ + + + +
+ + %1% + +
+

+
+
+


+
+
+ + + ]]>
+ + + Dashboard + Sections + Content + + + Choose page above... + %0% has been copied to %1% + Select where the document %0% should be copied to below + %0% has been moved to %1% + Select where the document %0% should be moved to below + has been selected as the root of your new content, click 'ok' below. + No node selected yet, please select a node in the list above before clicking 'ok' + The current node is not allowed under the chosen node because of its type + The current node cannot be moved to one of its subpages + The current node cannot exist at the root + The action isn't allowed since you have insufficient permissions on 1 or more child documents. + Relate copied items to original + + + %0%]]> + Notification settings saved for + + The following languages have been modified %0% + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Hi %0%, +

+

+ This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' +

+ + + + + + +
+ +
+ EDIT
+
+

+

Update summary:

+ %6% +

+

+ Have a nice day!

+ Cheers from the Umbraco robot +

+
+
+


+
+
+ + + ]]>
+ The following languages have been modified:

+ %0% + ]]>
+ [%0%] Notification about %1% performed on %2% + Notifications + + + Actions + Created + Create package + + button and locating the package. Umbraco packages usually have a ".umb" or ".zip" extension. + ]]> + This will delete the package + Drop to upload + Include all child nodes + or click here to choose package file + Upload package + Install a local package by selecting it from your machine. Only install packages from sources you know and trust + Upload another package + Cancel and upload another package + I accept + terms of use + + Path to file + Absolute path to file (ie: /bin/umbraco.bin) + Installed + Installed packages + Install local + Finish + This package has no configuration view + No packages have been created yet + You don’t have any packages installed + 'Packages' icon in the top right of your screen]]> + Package Actions + Author URL + Package Content + Package Files + Icon URL + Install package + License + License URL + Package Properties + Search for packages + Results for + We couldn’t find anything for + Please try searching for another package or browse through the categories + Popular + New releases + has + karma points + Information + Owner + Contributors + Created + Current version + .NET version + Downloads + Likes + Compatibility + This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be guaranteed for versions reported below 100% + External sources + Author + Documentation + Package meta data + Package name + Package doesn't contain any items +
+ You can safely remove this from the system by clicking "uninstall package" below.]]>
+ Package options + Package readme + Package repository + Confirm package uninstall + Package was uninstalled + The package was successfully uninstalled + Uninstall package + + Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, + so uninstall with caution. If in doubt, contact the package author.]]> + Package version + Package already installed + This package cannot be installed, it requires a minimum Umbraco version of + Uninstalling... + Downloading... + Importing... + Installing... + Restarting, please wait... + All done, your browser will now refresh, please wait... + Please click 'Finish' to complete installation and reload the page. + Uploading package... + + + Paste with full formatting (Not recommended) + The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web. + Paste as raw text without any formatting at all + Paste, but remove formatting (Recommended) + + + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication + Error Page + Used when people are logged on, but do not have access + %0%]]> + %0% is now protected]]> + %0%]]> + Login Page + Choose the page that contains the login form + Remove protection... + %0%?]]> + Select the pages that contain login form and error messages + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members + + + + + + + + + Include unpublished subpages + Publishing in progress - please wait... + %0% out of %1% pages have been published... + %0% has been published + %0% and subpages have been published + Publish %0% and all its subpages + Publish to publish %0% and thereby making its content publicly available.

+ You can publish this page and all its subpages by checking Include unpublished subpages below. + ]]>
+ + + You have not configured any approved colours + + + You can only select items of type(s): %0% + You have picked a content item currently deleted or in the recycle bin + You have picked content items currently deleted or in the recycle bin + + + Deleted item + You have picked a media item currently deleted or in the recycle bin + You have picked media items currently deleted or in the recycle bin + Trashed + + + enter external link + choose internal page + Caption + Link + Open in new window + enter the display caption + Enter the link + + + Reset crop + Save crop + Add new crop + Done + Undo edits + + + Select a version to compare with the current version + Current version + Red text will not be shown in the selected version. , green means added]]> + Document has been rolled back + This displays the selected version as HTML, if you wish to see the difference between 2 versions at the same time, use the diff view + Rollback to + Select version + View + + + Edit script file + + + Concierge + Content + Courier + Developer + Forms + Help + Umbraco Configuration Wizard + Media + Members + Newsletters + Packages + Settings + Statistics + Translation + Users + + + The best Umbraco video tutorials + + + Default template + To import a document type, find the ".udt" file on your computer by clicking the "Browse" button and click "Import" (you'll be asked for confirmation on the next screen) + New Tab Title + Node type + Type + Stylesheet + Script + Tab + Tab Title + Tabs + Master Content Type enabled + This Content Type uses + as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself + No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. + Create matching template + Add icon + + + Sort order + Creation date + Sorting complete. + Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items + + + + Validation + Validation errors must be fixed before the item can be saved + Failed + Saved + Insufficient user permissions, could not complete the operation + Cancelled + Operation was cancelled by a 3rd party add-in + Publishing was cancelled by a 3rd party add-in + Property type already exists + Property type created + DataType: %1%]]> + Propertytype deleted + Document Type saved + Tab created + Tab deleted + Tab with id: %0% deleted + Stylesheet not saved + Stylesheet saved + Stylesheet saved without any errors + Datatype saved + Dictionary item saved + Publishing failed because the parent page isn't published + Content published + and visible on the website + Content saved + Remember to publish to make changes visible + Sent For Approval + Changes have been sent for approval + Media saved + Member group saved + Media saved without any errors + Member saved + Stylesheet Property Saved + Stylesheet saved + Template saved + Error saving user (check log) + User Saved + User type saved + User group saved + File not saved + file could not be saved. Please check file permissions + File saved + File saved without any errors + Language saved + Media Type saved + Member Type saved + Member Group saved + Template not saved + Please make sure that you do not have 2 templates with the same alias + Template saved + Template saved without any errors! + Content unpublished + Partial view saved + Partial view saved without any errors! + Partial view not saved + An error occurred saving the file. + Permissions saved for + Deleted %0% user groups + %0% was deleted + Enabled %0% users + Disabled %0% users + %0% is now enabled + %0% is now disabled + User groups have been set + Unlocked %0% users + %0% is now unlocked + Member was exported to file + An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% + Document type was exported to file + An error occurred while exporting the document type + + + Add style + Edit style + Rich text editor styles + Define the styles that should be available in the rich text editor for this stylesheet + Edit stylesheet + Edit stylesheet property + The name displayed in the editor style selector + Preview + How the text will look like in the rich text editor. + Selector + Uses CSS syntax, e.g. "h1" or ".redHeader" + Styles + The CSS that should be applied in the rich text editor, e.g. "color:red;" + Code + Editor + + + Failed to delete template with ID %0% + Edit template + Sections + Insert content area + Insert content area placeholder + Insert + Choose what to insert into your template + Dictionary item + A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. + Macro + + A Macro is a configurable component which is great for + reusable parts of your design, where you need the option to provide parameters, + such as galleries, forms and lists. + + Value + Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. + Partial view + + A partial view is a separate template file which can be rendered inside another + template, it's great for reusing markup or for separating complex templates into separate files. + + Master template + No master + Render child template + @RenderBody() placeholder. + ]]> + Define a named section + @section { ... }. This can be rendered in a + specific area of the parent of this template, by using @RenderSection. + ]]> + Render a named section + @RenderSection(name) placeholder. + This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. + ]]> + Section Name + Section is mandatory + + If mandatory, the child template must contain a @section definition, otherwise an error is shown. + + Query builder + items returned, in + I want + all content + content of type "%0%" + from + my website + where + and + is + is not + before + before (including selected date) + after + after (including selected date) + equals + does not equal + contains + does not contain + greater than + greater than or equal to + less than + less than or equal to + Id + Name + Created Date + Last Updated Date + order by + ascending + descending + Template + + + Image + Macro + Choose type of content + Choose a layout + Add a row + Add content + Drop content + Settings applied + This content is not allowed here + This content is allowed here + Click to embed + Click to insert image + Image caption... + Write here... + Grid Layouts + Layouts are the overall work area for the grid editor, usually you only need one or two different layouts + Add Grid Layout + Adjust the layout by setting column widths and adding additional sections + Row configurations + Rows are predefined cells arranged horizontally + Add row configuration + Adjust the row by setting cell widths and adding additional cells + Columns + Total combined number of columns in the grid layout + Settings + Configure what settings editors can change + Styles + Configure what styling editors can change + Allow all editors + Allow all row configurations + Maximum items + Leave blank or set to 0 for unlimited + Set as default + Choose extra + Choose default + are added + + + Compositions + Group + You have not added any groups + Add group + Inherited from + Add property + Required label + Enable list view + Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree + Allowed Templates + Choose which templates editors are allowed to use on content of this type + Allow as root + Allow editors to create content of this type in the root of the content tree. + Allowed child node types + Allow content of the specified types to be created underneath content of this type. + Choose child node + Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists. + This content type is used in a composition, and therefore cannot be composed itself. + There are no content types available to use as a composition. + Create new + Use existing + Editor settings + Configuration + Yes, delete + was moved underneath + was copied underneath + Select the folder to move + Select the folder to copy + to in the tree structure below + All Document types + All Documents + All media items + using this document type will be deleted permanently, please confirm you want to delete these as well. + using this media type will be deleted permanently, please confirm you want to delete these as well. + using this member type will be deleted permanently, please confirm you want to delete these as well + and all documents using this type + and all media items using this type + and all members using this type + Member can edit + Allow this property value to be edited by the member on their profile page + Is sensitive data + Hide this property value from content editors that don't have access to view sensitive information + Show on member profile + Allow this property value to be displayed on the member profile page + tab has no sort order + Where is this composition used? + This composition is currently used in the composition of the following content types: + Allow varying by culture + Allow editors to create content of this type in different languages. + Allow varying by culture + Element type + Is an Element type + An Element type is meant to be used for instance in Nested Content, and not in the tree. + This is not applicable for an Element type + You have made changes to this property. Are you sure you want to discard them? + + + Add language + Mandatory language + Properties on this language have to be filled out before the node can be published. + Default language + An Umbraco site can only have one default language set. + Switching default language may result in default content missing. + Falls back to + No fall back language + To allow multi-lingual content to fall back to another language if not present in the requested language, select it here. + Fall back language + + + + Add parameter + Edit parameter + Enter macro name + Parameters + Define the parameters that should be available when using this macro. + Select partial view macro file + + + Building models + this can take a bit of time, don't worry + Models generated + Models could not be generated + Models generation has failed, see exception in U log + + + Add fallback field + Fallback field + Add default value + Default value + Fallback field + Default value + Casing + Encoding + Choose field + Convert line breaks + Yes, convert line breaks + Replaces line breaks with 'br' html tag + Custom Fields + Date only + Format and encoding + Format as date + Format the value as a date, or a date with time, according to the active culture + HTML encode + Will replace special characters by their HTML equivalent. + Will be inserted after the field value + Will be inserted before the field value + Lowercase + Modify output + None + Output sample + Insert after field + Insert before field + Recursive + Yes, make it recursive + Separator + Standard Fields + Uppercase + URL encode + Will format special characters in URLs + Will only be used when the field values above are empty + This field will only be used if the primary field is empty + Date and time + + + Translation details + Download XML DTD + Fields + Include subpages + + No translator users found. Please create a translator user before you start sending content to translation + The page '%0%' has been send to translation + Send the page '%0%' to translation + Total words + Translate to + Translation completed. + You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages. + Translation failed, the XML file might be corrupt + Translation options + Translator + Upload translation XML + + + Content + Content Templates + Media + Cache Browser + Recycle Bin + Created packages + Data Types + Dictionary + Installed packages + Install skin + Install starter kit + Languages + Install local package + Macros + Media Types + Members + Member Groups + Member Roles + Member Types + Document Types + Relation Types + Packages + Packages + Partial Views + Partial View Macro Files + Install from repository + Install Runway + Runway modules + Scripting Files + Scripts + Stylesheets + Templates + Log Viewer + Users + Settings + Templating + Third Party + + + New update ready + %0% is ready, click here for download + No connection to server + Error checking for update. Please review trace-stack for further information + + + Access + Based on the assigned groups and start nodes, the user has access to the following nodes + Assign access + Administrator + Category field + User created + Change Your Password + Change photo + New password + hasn't been locked out + The password hasn't been changed + Confirm new password + You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button + Content Channel + Create another user + Create new users to give them access to Umbraco. When a new user is created a password will be generated that you can share with the user. + Description field + Disable User + Document Type + Editor + Excerpt field + Failed login attempts + Go to user profile + Add groups to assign access and permissions + Invite another user + Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours. + Language + Set the language you will see in menus and dialogs + Last lockout date + Last login + Password last changed + Username + Media start node + Limit the media library to a specific start node + Media start nodes + Limit the media library to specific start nodes + Sections + Disable Umbraco Access + has not logged in yet + Old password + Password + Reset password + Your password has been changed! + Please confirm the new password + Enter your new password + Your new password cannot be blank! + Current password + Invalid current password + There was a difference between the new password and the confirmed password. Please try again! + The confirmed password doesn't match the new password! + Replace child node permissions + You are currently modifying permissions for the pages: + Select pages to modify their permissions + Remove photo + Default permissions + Granular permissions + Set permissions for specific nodes + Profile + Search all children + Add sections to give users access + Select user groups + No start node selected + No start nodes selected + Content start node + Limit the content tree to a specific start node + Content start nodes + Limit the content tree to specific start nodes + User last updated + has been created + The new user has successfully been created. To log in to Umbraco use the password below. + User management + Name + User permissions + User group + has been invited + An invitation has been sent to the new user with details about how to log in to Umbraco. + Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar. + Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it. + Uploading a photo of yourself will make it easy for other users to recognize you. Click the circle above to upload your photo. + Writer + Change + Your profile + Your recent history + Session expires in + Invite user + Create user + Send invite + Back to users + Umbraco: Invitation + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Hi %0%, +

+

+ You have been invited by %1% to the Umbraco Back Office. +

+

+ Message from %1%: +
+ %2% +

+ + + + + + +
+ + + + + + +
+ + Click this link to accept the invite + +
+
+

If you cannot click on the link, copy and paste this URL into your browser window:

+ + + + +
+ + %3% + +
+

+
+
+


+
+
+ + ]]>
+ Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? + All + Active + Disabled + Locked out + Invited + Inactive + Name (A-Z) + Name (Z-A) + Newest + Oldest + Last login + + + Validation + No validation + Validate as an email address + Validate as a number + Validate as a URL + ...or enter a custom validation + Field is mandatory + Enter a regular expression + You need to add at least + You can only have + items + items selected + Invalid date + Not a number + Invalid email + Custom validation + + + + Value is set to the recommended value: '%0%'. + Value was set to '%1%' for XPath '%2%' in configuration file '%3%'. + Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'. + Found unexpected value '%0%' for '%2%' in configuration file '%3%'. + + Custom errors are set to '%0%'. + Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live. + Custom errors successfully set to '%0%'. + MacroErrors are set to '%0%'. + MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'. + MacroErrors are now set to '%0%'. + + Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'. + Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%). + Try Skip IIS Custom Errors successfully set to '%0%'. + + File does not exist: '%0%'. + '%0%' in config file '%1%'.]]> + There was an error, check log for full error: %0%. + Database - The database schema is correct for this version of Umbraco + %0% problems were detected with your database schema (Check the log for details) + Some errors were detected while validating the database schema against the current version of Umbraco. + Your website's certificate is valid. + Certificate validation error: '%0%' + Your website's SSL certificate has expired. + Your website's SSL certificate is expiring in %0% days. + Error pinging the URL %0% - '%1%' + You are currently %0% viewing the site using the HTTPS scheme. + The appSetting 'Umbraco.Core.UseHttps' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'. + The appSetting 'Umbraco.Core.UseHttps' is set to '%0%' in your web.config file, your cookies are %1% marked as secure. + Could not update the 'Umbraco.Core.UseHttps' setting in your web.config file. Error: %0% + + Enable HTTPS + Sets umbracoSSL setting to true in the appSettings of the web.config file. + The appSetting 'Umbraco.Core.UseHttps' is now set to 'true' in your web.config file, your cookies will be marked as secure. + Fix + Cannot fix a check with a value comparison type of 'ShouldNotEqual'. + Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value. + Value to fix check not provided. + Debug compilation mode is disabled. + Debug compilation mode is currently enabled. It is recommended to disable this setting before go live. + Debug compilation mode successfully disabled. + Trace mode is disabled. + Trace mode is currently enabled. It is recommended to disable this setting before go live. + Trace mode successfully disabled. + All folders have the correct permissions set. + + %0%.]]> + %0%. If they aren't being written to no action need be taken.]]> + All files have the correct permissions set. + + %0%.]]> + %0%. If they aren't being written to no action need be taken.]]> + X-Frame-Options used to control whether a site can be IFRAMEd by another was found.]]> + X-Frame-Options used to control whether a site can be IFRAMEd by another was not found.]]> + Set Header in Config + Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMEd by other websites. + A setting to create a header preventing IFRAMEing of the site by other websites has been added to your web.config file. + Could not update web.config file. Error: %0% + X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was found.]]> + X-Content-Type-Options used to protect against MIME sniffing vulnerabilities was not found.]]> + Adds a value to the httpProtocol/customHeaders section of web.config to protect against MIME sniffing vulnerabilities. + A setting to create a header protecting against MIME sniffing vulnerabilities has been added to your web.config file. + Strict-Transport-Security, also known as the HSTS-header, was found.]]> + Strict-Transport-Security was not found.]]> + Adds the header 'Strict-Transport-Security' with the value 'max-age=10886400' to the httpProtocol/customHeaders section of web.config. Use this fix only if you will have your domains running with https for the next 18 weeks (minimum). + The HSTS header has been added to your web.config file. + X-XSS-Protection was found.]]> + X-XSS-Protection was not found.]]> + Adds the header 'X-XSS-Protection' with the value '1; mode=block' to the httpProtocol/customHeaders section of web.config. + The X-XSS-Protection header has been added to your web.config file. + + %0%.]]> + No headers revealing information about the website technology were found. + In the Web.config file, system.net/mailsettings could not be found. + In the Web.config file system.net/mailsettings section, the host is not configured. + SMTP settings are configured correctly and the service is operating as expected. + The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct. + %0%.]]> + %0%.]]> +

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
+ Umbraco Health Check Status: %0% + + + Disable URL tracker + Enable URL tracker + Original URL + Redirected To + Redirect Url Management + The following URLs redirect to this content item: + No redirects have been made + When a published page gets renamed or moved a redirect will automatically be made to the new page. + Are you sure you want to remove the redirect from '%0%' to '%1%'? + Redirect URL removed. + Error removing redirect URL. + This will remove the redirect + Are you sure you want to disable the URL tracker? + URL tracker has now been disabled. + Error disabling the URL tracker, more information can be found in your log file. + URL tracker has now been enabled. + Error enabling the URL tracker, more information can be found in your log file. + + + No Dictionary items to choose from + + + %0% characters left.]]> + %1% too many.]]> + + + Trashed content with Id: {0} related to original parent content with Id: {1} + Trashed media with Id: {0} related to original parent media item with Id: {1} + Cannot automatically restore this item + There is no location where this item can be automatically restored. You can move the item manually using the tree below. + was restored under + + + Direction + Parent to child + Bidirectional + Parent + Child + Count + Relations + Created + Comment + Name + No relations for this relation type. + Relation Type + Relations + + + Getting Started + Redirect URL Management + Content + Welcome + Examine Management + Published Status + Models Builder + Health Check + Profiling + Getting Started + Install Umbraco Forms + + + Go back + Active layout: + Jump to + group + passed + warning + failed + suggestion + Check passed + Check failed + Open backoffice search + Open/Close backoffice help + Open/Close your profile options + + + Relations + This Data Type has no relations. + Used by Document Types + No relations to Document Types. + Used by Media Types + No relations to Media Types. + Used by Member Types + No relations to Member Types. + Used by + + +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 8a2c03b2fc..fe7f232e48 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -568,6 +568,8 @@ Select the folder to move to in the tree structure below was moved underneath + %0% will have the following consequence]]> + I understand this action will delete the properties and data based on this Data Type Your data has been saved, but before you can publish this page there are some errors you need to fix first: From dda99272aab211e3a0cd8f0b7eb4bf149847df22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:01:01 +0200 Subject: [PATCH 035/109] Delete dialog for datatype that has relations --- .../components/umbconfirm.directive.js | 3 +- .../src/less/tables.less | 2 +- .../src/views/components/umb-confirm.html | 2 +- .../datatypes/datatype.delete.controller.js | 41 +++++- .../src/views/datatypes/delete.html | 127 +++++++++++++++--- 5 files changed, 151 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js index ce6b90c1ec..1ddd09357a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbconfirm.directive.js @@ -55,7 +55,8 @@ function confirmDirective() { onConfirm: '=', onCancel: '=', caption: '@', - confirmButtonStyle: '@' + confirmButtonStyle: '@', + confirmLabelKey: '@' }, link: function (scope, element, attr, ctrl) { scope.showCancel = false; diff --git a/src/Umbraco.Web.UI.Client/src/less/tables.less b/src/Umbraco.Web.UI.Client/src/less/tables.less index cd6304ef49..7afbdb12d9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tables.less +++ b/src/Umbraco.Web.UI.Client/src/less/tables.less @@ -62,7 +62,7 @@ table { } -.table tr > td:first-child { +.table:not(.table-bordered) tr > td:first-child { border-left: 4px solid transparent; } .table tr.--selected > td:first-child { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm.html index b89b7f559b..384c5ccaf7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-confirm.html @@ -14,7 +14,7 @@ action="confirm()" button-style="{{confirmButtonStyle || 'primary'}}" state="confirmButtonState" - label-key="general_ok"> + label-key="{{confirmLabelKey || 'general_ok'}}"> diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js index 2a1fd5255a..2407d57219 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js @@ -8,7 +8,12 @@ */ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService, localizationService) { - $scope.performDelete = function() { + var vm = this; + + vm.hasRelations = false; + vm.relations = []; + + vm.performDelete = function() { //mark it for deletion (used in the UI) $scope.currentNode.loading = true; @@ -24,7 +29,7 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat }); }; - $scope.performContainerDelete = function () { + vm.performContainerDelete = function () { //mark it for deletion (used in the UI) $scope.currentNode.loading = true; @@ -41,16 +46,40 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat }; - $scope.cancel = function() { + vm.cancel = function() { navigationService.hideDialog(); }; - $scope.labels = {}; + vm.labels = {}; localizationService - .format(["editdatatype_yesDelete", "editdatatype_andAllRelated"], "%0% " + $scope.currentNode.name + " %1%") + .localize("editdatatype_acceptDeleteConsequence", [$scope.currentNode.name]) .then(function (data) { - $scope.labels.deleteConfirm = data; + vm.labels.deleteConfirm = data; }); + + var init = function() { + + console.log($scope.currentNode); + + if($scope.currentNode.nodeType === "dataTypes") { + + vm.loading = true; + + dataTypeResource.getReferences($scope.currentNode.id) + .then(function (data) { + vm.loading = false; + vm.relations = data; + + console.log(data); + + vm.hasRelations = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0; + }); + + } + + } + + init(); } angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index 5db72deaa5..d29141a070 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -1,29 +1,126 @@ -
+
- -

- Are you sure you want to delete {{currentNode.name}}? -

- + +

+ Are you sure you want to delete {{currentNode.name}}? +

+
-

- All property types & property data - using this data type will be deleted permanently, please confirm you want to delete these as well. -

-
- - + - +
+

+ Are you sure you want to delete {{currentNode.name}}? +

+
+ +
+ +

+ Deleting {{currentNode.name}} will have the following consequence +

+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + + +
NameUsed in
{{::relation.name}}{{::relation.properties | CMS_joinArray:', ':'name'}}
+
+ + + +
+ +
+ +
+ +
+
+
+
+
Name
+
Used in
+
+
+
+
+
+
{{::relation.name}}
+
{{::relation.properties | CMS_joinArray:', ':'name'}}
+
+
+
+
+ + + +
+ +
+ +
+ +
+
+
+
+
Name
+
Used in
+
+
+
+
+
+
{{::relation.name}}
+
{{::relation.properties | CMS_joinArray:', ':'name'}}
+
+
+
+ +
+ + + + +
+ + +
From 042971103fad2cc9a2d1294751af936bbdb3359e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:01:54 +0200 Subject: [PATCH 036/109] rename to Reference --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 30c0bab6af..98916dfeb8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -564,7 +564,7 @@ Vælg den mappe, der skal flyttes til i træstrukturen nedenfor blev flyttet under - %0% will have the following consequence]]> + %0% will have the following consequence]]> I understand this action will delete the properties and data based on this Data Type diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 978fa3b3f0..d054f99005 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -565,7 +565,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will have the following consequence]]> + %0% will have the following consequence]]> I understand this action will delete the properties and data based on this Data Type diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index fe7f232e48..3020e732bc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -568,7 +568,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will have the following consequence]]> + %0% will have the following consequence]]> I understand this action will delete the properties and data based on this Data Type From f658c15a0613d2c1a7e364b6125b7188451c7c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:22:58 +0200 Subject: [PATCH 037/109] rename relations to references, to avoid misunderstanding. --- .../src/common/resources/datatype.resource.js | 4 +- .../datatypes/datatype.delete.controller.js | 12 ++-- .../datatypes/datatype.edit.controller.js | 6 +- .../src/views/datatypes/delete.html | 32 ++++----- ...r.js => datatype.references.controller.js} | 28 ++++---- ...elations.html => datatype.references.html} | 66 +++++++++---------- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 20 +++--- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 20 +++--- .../Umbraco/config/lang/en_us.xml | 20 +++--- 9 files changed, 102 insertions(+), 106 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/datatypes/views/{datatype.relations.controller.js => datatype.references.controller.js} (50%) rename src/Umbraco.Web.UI.Client/src/views/datatypes/views/{datatype.relations.html => datatype.references.html} (63%) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js index 6b6fb2bb71..7aedfccacf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js @@ -49,9 +49,9 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { * @methodOf umbraco.resources.dataTypeResource * * @description - * Retrieves relations of a given data type. + * Retrieves references of a given data type. * - * @param {Int} id id of datatype to retrieve relations for + * @param {Int} id id of datatype to retrieve references for * @returns {Promise} resourcePromise object. * */ diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js index 2407d57219..0595fb7e88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js @@ -10,8 +10,8 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat var vm = this; - vm.hasRelations = false; - vm.relations = []; + vm.hasReferences = false; + vm.references = []; vm.performDelete = function() { @@ -59,8 +59,6 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat var init = function() { - console.log($scope.currentNode); - if($scope.currentNode.nodeType === "dataTypes") { vm.loading = true; @@ -68,11 +66,9 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat dataTypeResource.getReferences($scope.currentNode.id) .then(function (data) { vm.loading = false; - vm.relations = data; + vm.references = data; - console.log(data); - - vm.hasRelations = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0; + vm.hasReferences = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 5cf7820b6e..15fb103ecd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -205,7 +205,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic var labelKeys = [ "general_settings", - "relations_tabName" + "references_tabName" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -220,9 +220,9 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic }, { "name": values[1], - "alias": "relations", + "alias": "references", "icon": "icon-molecular-network", - "view": "views/datatypes/views/datatype.relations.html" + "view": "views/datatypes/views/datatype.references.html" } ]; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index d29141a070..449423a088 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -19,37 +19,37 @@ -
+

Are you sure you want to delete {{currentNode.name}}?

-
+

- Deleting {{currentNode.name}} will have the following consequence + Deleting {{currentNode.name}} will have the following consequence


-
+
- +
- + - + @@ -59,10 +59,10 @@ -
+
- +
@@ -70,11 +70,11 @@
Name
-
Used in
+
Used in
-
+
{{::relation.name}}
{{::relation.properties | CMS_joinArray:', ':'name'}}
@@ -85,10 +85,10 @@ -
+
- +
@@ -96,11 +96,11 @@
Name
-
Used in
+
Used in
-
+
{{::relation.name}}
{{::relation.properties | CMS_joinArray:', ':'name'}}
@@ -116,7 +116,7 @@
- 0 || vm.relations.mediaTypes.length > 0 || vm.relations.memberTypes.length > 0; + vm.references = data; + vm.hasReferences = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0; }); } } - // load data type relations when the relations tab is activated + // load data type references when the references tab is activated evts.push(eventsService.on("app.tabChange", function (event, args) { $timeout(function () { - if (args.alias === "relations") { + if (args.alias === "references") { loadRelations(); } }); @@ -52,4 +52,4 @@ function DataTypeRelationsController($scope, $routeParams, dataTypeResource, eve } -angular.module("umbraco").controller("Umbraco.Editors.DataType.RelationsController", DataTypeRelationsController); +angular.module("umbraco").controller("Umbraco.Editors.DataType.ReferencesController", DataTypeReferencesController); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html similarity index 63% rename from src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html rename to src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html index db1c2e5e40..3704833533 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.relations.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html @@ -1,25 +1,25 @@ -
+
- - + + - This Data Type has no relations. + This Data Type has no references. -
+
-
+
- +
@@ -28,17 +28,17 @@
Name
Alias
-
Used in
+
Used in
Open
-
-
-
{{::relation.name}}
-
{{::relation.alias}}
-
{{::relation.properties | CMS_joinArray:', ':'name'}}
- +
+
+
{{::reference.name}}
+
{{::reference.alias}}
+
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
@@ -46,10 +46,10 @@ -
+
- +
@@ -58,17 +58,17 @@
Name
Alias
-
Used in
+
Used in
Open
-
-
-
{{::relation.name}}
-
{{::relation.alias}}
-
{{::relation.properties | CMS_joinArray:', ':'name'}}
- +
+
+
{{::reference.name}}
+
{{::reference.alias}}
+
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
@@ -76,10 +76,10 @@ -
+
- +
@@ -88,17 +88,17 @@
Name
Alias
-
Used in
+
Used in
Open
-
-
-
{{::relation.name}}
-
{{::relation.alias}}
-
{{::relation.properties | CMS_joinArray:', ':'name'}}
- +
+
+
{{::reference.name}}
+
{{::reference.alias}}
+
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
@@ -109,4 +109,4 @@
-
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 98916dfeb8..4c71989dbd 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -564,7 +564,7 @@ Vælg den mappe, der skal flyttes til i træstrukturen nedenfor blev flyttet under - %0% will have the following consequence]]> + %0% fjernes egnskaber baseret på denne og egnskabernes data fra følgende elementer]]> I understand this action will delete the properties and data based on this Data Type @@ -1715,15 +1715,15 @@ Mange hilsner fra Umbraco robotten Åben/Luk backoffice hjælp Åben/Luk dine profil indstillinger - - Relationer - Denne Data Type har ingen relationer. - Brugt af Dokument Typer - Ingen relationer til Dokument Typer. - Brugt af Medie Typer - Ingen relationer til Medie Typer. - Brugt af Medlems Typer - Ingen relationer til Medlems Typer. + + Referencer + Denne Data Type har ingen referencer. + Brugt i Dokument Typer + Ingen referencer til Dokument Typer. + Brugt i Medie Typer + Ingen referencer til Medie Typer. + Brugt i Medlems Typer + Ingen referencer til Medlems Typer. Brugt af diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index d054f99005..f7491b2750 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -565,7 +565,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will have the following consequence]]> + %0% will delete the properties based on it and their data from the following items]]> I understand this action will delete the properties and data based on this Data Type @@ -2152,15 +2152,15 @@ To manage your website, simply open the Umbraco back office and start adding con Open/Close backoffice help Open/Close your profile options - - Relations - This Data Type has no relations. - Used by Document Types - No relations to Document Types. - Used by Media Types - No relations to Media Types. - Used by Member Types - No relations to Member Types. + + References + This Data Type has no references. + Used in Document Types + No references to Document Types. + Used in Media Types + No references to Media Types. + Used in Member Types + No references to Member Types. Used by diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 3020e732bc..71c30aeb91 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -568,7 +568,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will have the following consequence]]> + %0% will delete the properties based on it and their data from the following items]]> I understand this action will delete the properties and data based on this Data Type @@ -2166,15 +2166,15 @@ To manage your website, simply open the Umbraco back office and start adding con Open/Close backoffice help Open/Close your profile options - - Relations - This Data Type has no relations. - Used by Document Types - No relations to Document Types. - Used by Media Types - No relations to Media Types. - Used by Member Types - No relations to Member Types. + + References + This Data Type has no references. + Used in Document Types + No references to Document Types. + Used in Media Types + No references to Media Types. + Used in Member Types + No references to Member Types. Used by From e7363adc2d7282ddabbeb0b999a9aac79034b576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:29:29 +0200 Subject: [PATCH 038/109] highlight properties begin deleted + corrected array --- .../src/views/datatypes/delete.html | 68 +++++++++---------- .../datatypes/views/datatype.references.html | 4 +- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index 449423a088..a1b85823db 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -38,7 +38,7 @@
- +
NameUsed inUsed in
{{::relation.name}} {{::relation.properties | CMS_joinArray:', ':'name'}}
@@ -51,7 +51,7 @@ - +
{{::relation.name}}{{::relation.properties | CMS_joinArray:', ':'name'}}{{::relation.properties | CMS_joinArray:', ':'name'}}
@@ -62,25 +62,23 @@
- +
-
-
-
-
-
Name
-
Used in
-
-
-
-
-
-
{{::relation.name}}
-
{{::relation.properties | CMS_joinArray:', ':'name'}}
-
-
-
+ + + + + + + + + + + + + +
NameUsed in
{{::relation.name}}{{::relation.properties | CMS_joinArray:', ':'name'}}
@@ -88,25 +86,23 @@
- +
-
-
-
-
-
Name
-
Used in
-
-
-
-
-
-
{{::relation.name}}
-
{{::relation.properties | CMS_joinArray:', ':'name'}}
-
-
-
+ + + + + + + + + + + + + +
NameUsed in
{{::relation.name}}{{::relation.properties | CMS_joinArray:', ':'name'}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html index 3704833533..2c80d1019c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html @@ -63,7 +63,7 @@
-
+
{{::reference.name}}
{{::reference.alias}}
@@ -93,7 +93,7 @@
-
+
{{::reference.name}}
{{::reference.alias}}
From e7faa149564901e55a9a0a7246a3cd7137fa912f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:31:04 +0200 Subject: [PATCH 039/109] changed wording --- src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index a1b85823db..351b5da570 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -45,7 +45,7 @@ Name - Used in + Properties @@ -69,7 +69,7 @@ Name - Used in + Properties @@ -93,7 +93,7 @@ Name - Used in + Properties From 5035438a28aa6cf2287ccc4a947e7998c24ebb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:33:18 +0200 Subject: [PATCH 040/109] simplified wording by removing "based on it" --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 4c71989dbd..a7d819b20f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -564,7 +564,7 @@ Vælg den mappe, der skal flyttes til i træstrukturen nedenfor blev flyttet under - %0% fjernes egnskaber baseret på denne og egnskabernes data fra følgende elementer]]> + %0% fjernes egnskaber og egnskabernes data fra følgende elementer]]> I understand this action will delete the properties and data based on this Data Type diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index f7491b2750..2fc0c43eb8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -565,7 +565,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will delete the properties based on it and their data from the following items]]> + %0% will delete the properties and their data from the following items]]> I understand this action will delete the properties and data based on this Data Type diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 71c30aeb91..9cfc3692cc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -568,7 +568,7 @@ Select the folder to move to in the tree structure below was moved underneath - %0% will delete the properties based on it and their data from the following items]]> + %0% will delete the properties and their data from the following items]]> I understand this action will delete the properties and data based on this Data Type From 188cc2a9fdba618e3f63a16d230a73817ee0cab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:43:52 +0200 Subject: [PATCH 041/109] listing out properties with exclamation mark icon in front of each --- .../src/views/datatypes/datatype.delete.controller.js | 1 + src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js index 0595fb7e88..c6c58d9fa6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.delete.controller.js @@ -10,6 +10,7 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat var vm = this; + vm.propertyJoinSeparator = ', '; vm.hasReferences = false; vm.references = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index 351b5da570..f022bed63d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -51,7 +51,7 @@ {{::relation.name}} - {{::relation.properties | CMS_joinArray:', ':'name'}} +

{{::property.name}}{{$last ? '' : ', '}}

@@ -75,7 +75,7 @@ {{::relation.name}} - {{::relation.properties | CMS_joinArray:', ':'name'}} +

{{::property.name}}{{$last ? '' : ', '}}

@@ -99,7 +99,7 @@ {{::relation.name}} - {{::relation.properties | CMS_joinArray:', ':'name'}} +

{{::property.name}}{{$last ? '' : ', '}}

From c091d210346da7ea4767c126488bd9391fec903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 9 Oct 2019 16:53:30 +0200 Subject: [PATCH 042/109] align icons if the are in a umb-table --- src/Umbraco.Web.UI.Client/lib/bootstrap/less/tables.less | 4 ++++ .../src/less/components/umb-table.less | 1 + src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/tables.less b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/tables.less index f88b66bd18..ff1f96296a 100644 --- a/src/Umbraco.Web.UI.Client/lib/bootstrap/less/tables.less +++ b/src/Umbraco.Web.UI.Client/lib/bootstrap/less/tables.less @@ -241,3 +241,7 @@ table th[class*="span"], background-color: darken(@infoBackground, 5%); } } + +.table .icon { + vertical-align: bottom; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 56d3b0b87f..953815512c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -155,6 +155,7 @@ input.umb-table__input { font-size: 20px; line-height: 20px; color: @ui-option-type; + vertical-align: bottom; } .umb-table-body__checkicon, diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html index f022bed63d..6c415e9f38 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -51,7 +51,7 @@ {{::relation.name}} -

{{::property.name}}{{$last ? '' : ', '}}

+ {{::property.name}}{{$last ? '' : ', '}} @@ -75,7 +75,7 @@ {{::relation.name}} -

{{::property.name}}{{$last ? '' : ', '}}

+ {{::property.name}}{{$last ? '' : ', '}} @@ -99,7 +99,7 @@ {{::relation.name}} -

{{::property.name}}{{$last ? '' : ', '}}

+ {{::property.name}}{{$last ? '' : ', '}} From b400a72e77fa2f6aeeeca9e7f563469a2ed0c07d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2019 17:56:53 +1100 Subject: [PATCH 043/109] Changes filter name --- .../{joinArray.filter.js => umbCmsJoinArray.filter.js} | 6 +++--- .../src/views/datatypes/views/datatype.references.html | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/Umbraco.Web.UI.Client/src/common/filters/{joinArray.filter.js => umbCmsJoinArray.filter.js} (85%) diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js similarity index 85% rename from src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js rename to src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js index 0aa406232c..75b99cb974 100644 --- a/src/Umbraco.Web.UI.Client/src/common/filters/joinArray.filter.js +++ b/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js @@ -1,7 +1,7 @@ /** * @ngdoc filter * @name umbraco.filters.filter:CMS_joinArray - * @namespace CMS_joinArrayFilter + * @namespace umbCmsJoinArray * * param {array} array of string or objects, if an object use the third argument to specify which prop to list. * param {seperator} string containing the seperator to add between joined values. @@ -11,10 +11,10 @@ * Join an array of string or an array of objects, with a costum seperator. * */ -angular.module("umbraco.filters").filter('CMS_joinArray', function () { +angular.module("umbraco.filters").filter('umbCmsJoinArray', function () { return function join(array, separator, prop) { return (!angular.isUndefined(prop) ? array.map(function (item) { return item[prop]; }) : array).join(separator); }; -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html index 2c80d1019c..dfa633ad16 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html @@ -37,7 +37,7 @@
{{::reference.name}}
{{::reference.alias}}
-
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
{{::reference.properties | umbCmsJoinArray:', ':'name'}}
@@ -67,7 +67,7 @@
{{::reference.name}}
{{::reference.alias}}
-
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
{{::reference.properties | umbCmsJoinArray:', ':'name'}}
@@ -97,7 +97,7 @@
{{::reference.name}}
{{::reference.alias}}
-
{{::reference.properties | CMS_joinArray:', ':'name'}}
+
{{::reference.properties | umbCmsJoinArray:', ':'name'}}
From 2675516b96e25e3964160f89aa2dfc690292239a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2019 20:45:16 +1100 Subject: [PATCH 044/109] Adds logic to ensure you can't change to a culture that is already assigned + test --- .../Implement/LanguageRepository.cs | 13 ++++++++++++ .../Repositories/LanguageRepositoryTest.cs | 21 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 1a8b2b8821..a905294417 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -182,6 +182,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement throw new InvalidOperationException($"Cannot save the default language ({entity.IsoCode}) as non-default. Make another language the default language instead."); } + if (entity.IsPropertyDirty(nameof(ILanguage.IsoCode))) + { + //if the iso code is changing, ensure there's not another lang with the same code already assigned + var sameCode = Sql() + .SelectCount() + .From() + .Where(x => x.IsoCode == entity.IsoCode && x.Id != entity.Id); + + var countOfSameCode = Database.ExecuteScalar(sameCode); + if (countOfSameCode > 0) + throw new InvalidOperationException($"Cannot update the language to a new culture: {entity.IsoCode} since that culture is already assigned to another language entity."); + } + // fallback cycles are detected at service level // update diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index 03c1713268..85a3374cea 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System; +using System.Globalization; using System.Linq; using Moq; using NUnit.Framework; @@ -297,6 +298,24 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Perform_Update_With_Existing_Culture() + { + // Arrange + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider); + + // Act + var language = repository.Get(5); + language.IsoCode = "da-DK"; + language.CultureName = "da-DK"; + + Assert.Throws(() => repository.Save(language)); + } + } + [Test] public void Can_Perform_Delete_On_LanguageRepository() { From 261dc2af9881b8bba754368d0cd335e9e81ad311 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 11 Oct 2019 13:32:23 +0200 Subject: [PATCH 045/109] AB2913 - Added unit tests for the umbCmsJoinArray filter, and a small change when using null for separator --- .../common/filters/umbCmsJoinArray.filter.js | 2 +- .../filters/umbCmsJoinArray.filter.spec.js | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/filters/umbCmsJoinArray.filter.spec.js diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js index 75b99cb974..de340bab27 100644 --- a/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js +++ b/src/Umbraco.Web.UI.Client/src/common/filters/umbCmsJoinArray.filter.js @@ -15,6 +15,6 @@ angular.module("umbraco.filters").filter('umbCmsJoinArray', function () { return function join(array, separator, prop) { return (!angular.isUndefined(prop) ? array.map(function (item) { return item[prop]; - }) : array).join(separator); + }) : array).join(separator || ''); }; }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/filters/umbCmsJoinArray.filter.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/filters/umbCmsJoinArray.filter.spec.js new file mode 100644 index 0000000000..4a44b17bff --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/filters/umbCmsJoinArray.filter.spec.js @@ -0,0 +1,43 @@ +(function () { + + describe('umbCmsJoinArray filter', function() { + var $umbCmsJoinArray; + + var testCases = [ + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:', ', prop:'param' , expectedResult: 'a, b, c'}, + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:' ', prop:'param' , expectedResult: 'a b c'}, + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:'', prop:'param' , expectedResult: 'abc'}, + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:'', prop:'wrong' , expectedResult: ''}, + {input:[], separator:', ', prop:'param' , expectedResult: ''}, + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:', ', prop:null , expectedResult: ', , '}, + {input:[{param:'a'},{param:'b'},{param:'c'}], separator:null, prop:'param' , expectedResult: 'abc'}, + ]; + + var testCasesWithExpectedError = [ + {input:'test', separator:', ', prop:'param'}, + {input:null, separator:', ', prop:'param'}, + {input:undefined, separator:', ', prop:'param'}, + ]; + + beforeEach(module('umbraco')); + + beforeEach(inject(function($filter) { + $umbCmsJoinArray = $filter('umbCmsJoinArray'); + })); + + + testCases.forEach(function(test){ + it('Blackbox tests with expected result=\''+test.expectedResult+'\'', function() { + expect($umbCmsJoinArray(test.input, test.separator, test.prop)).toBe(test.expectedResult); + }); + }); + + testCasesWithExpectedError.forEach(function(test){ + it('Blackbox tests with expected error. Input=\''+test.input+'\'', function() { + expect(function() { $umbCmsJoinArray(test.input, test.separator, test.prop)}).toThrow(); + }); + }); + + }); + +}()); From 348afd3d0aa437b4d80234934b3b3a644188df98 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 15:21:00 +1100 Subject: [PATCH 046/109] updates logic for cache refreshers for languages to deal with updating nucache and examine --- .../Cache/ContentCacheRefresher.cs | 45 ++++---- .../Cache/LanguageCacheRefresher.cs | 108 ++++++++++++++++-- src/Umbraco.Web/Search/ExamineComponent.cs | 26 ++++- 3 files changed, 142 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs index 423e0e3cea..d9a6518493 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs @@ -100,13 +100,7 @@ namespace Umbraco.Web.Cache //if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache) // ... - _publishedSnapshotService.Notify(payloads, out _, out var publishedChanged); - - if (payloads.Any(x => x.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) || publishedChanged) - { - // when a public version changes - AppCaches.ClearPartialViewCache(); - } + NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, payloads); base.Refresh(payloads); } @@ -114,30 +108,35 @@ namespace Umbraco.Web.Cache // these events should never trigger // everything should be PAYLOAD/JSON - public override void RefreshAll() - { - throw new NotSupportedException(); - } + public override void RefreshAll() => throw new NotSupportedException(); - public override void Refresh(int id) - { - throw new NotSupportedException(); - } + public override void Refresh(int id) => throw new NotSupportedException(); - public override void Refresh(Guid id) - { - throw new NotSupportedException(); - } + public override void Refresh(Guid id) => throw new NotSupportedException(); - public override void Remove(int id) - { - throw new NotSupportedException(); - } + public override void Remove(int id) => throw new NotSupportedException(); #endregion #region Json + /// + /// Refreshes the publish snapshot service and if there are published changes ensures that partial view caches are refreshed too + /// + /// + /// + /// + internal static void NotifyPublishedSnapshotService(IPublishedSnapshotService service, AppCaches appCaches, JsonPayload[] payloads) + { + service.Notify(payloads, out _, out var publishedChanged); + + if (payloads.Any(x => x.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) || publishedChanged) + { + // when a public version changes + appCaches.ClearPartialViewCache(); + } + } + public class JsonPayload { public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes) diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index 9093124609..03102ec969 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -8,7 +8,9 @@ using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Cache { - public sealed class LanguageCacheRefresher : CacheRefresherBase + public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase + + //CacheRefresherBase { public LanguageCacheRefresher(AppCaches appCaches, IPublishedSnapshotService publishedSnapshotService, IDomainService domainService) : base(appCaches) @@ -33,21 +35,62 @@ namespace Umbraco.Web.Cache #region Refresher - public override void Refresh(int id) + public override void Refresh(JsonPayload[] payloads) { + if (payloads.Length == 0) return; + + var clearDictionary = false; + var clearContent = false; + + //clear all no matter what type of payload ClearAllIsolatedCacheByEntityType(); - RefreshDomains(id); - base.Refresh(id); + + foreach (var payload in payloads) + { + RefreshDomains(payload.Id); + + switch (payload.ChangeType) + { + case JsonPayload.LanguageChangeType.Update: + clearDictionary = true; + break; + case JsonPayload.LanguageChangeType.Remove: + clearDictionary = true; + clearContent = true; + break; + case JsonPayload.LanguageChangeType.ChangeCulture: + clearDictionary = true; + clearContent = true; + break; + } + } + + if (clearDictionary) + { + ClearAllIsolatedCacheByEntityType(); + } + + //if this flag is set, we will tell the published snapshot service to refresh ALL content + if (clearContent) + { + var clearContentPayload = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }; + ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload); + } + + // then trigger event + base.Refresh(payloads); } - public override void Remove(int id) - { - ClearAllIsolatedCacheByEntityType(); - //if a language is removed, then all dictionary cache needs to be removed - ClearAllIsolatedCacheByEntityType(); - RefreshDomains(id); - base.Remove(id); - } + // these events should never trigger + // everything should be PAYLOAD/JSON + + public override void RefreshAll() => throw new NotSupportedException(); + + public override void Refresh(int id) => throw new NotSupportedException(); + + public override void Refresh(Guid id) => throw new NotSupportedException(); + + public override void Remove(int id) => throw new NotSupportedException(); #endregion @@ -69,5 +112,46 @@ namespace Umbraco.Web.Cache _publishedSnapshotService.Notify(assignedDomains.Select(x => new DomainCacheRefresher.JsonPayload(x.Id, DomainChangeTypes.Remove)).ToArray()); } } + + #region Json + + public class JsonPayload + { + public JsonPayload(int id, string isoCode, LanguageChangeType changeType) + { + Id = id; + IsoCode = isoCode; + ChangeType = changeType; + } + + public int Id { get; } + public string IsoCode { get; } + public LanguageChangeType ChangeType { get; } + + public enum LanguageChangeType + { + /// + /// A new languages has been added + /// + Add = 0, + + /// + /// A language has been deleted + /// + Remove = 1, + + /// + /// A language has been updated - but it's culture remains the same + /// + Update = 2, + + /// + /// A language has been updated - it's culture has changed + /// + ChangeCulture = 3 + } + } + + #endregion } } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index f34b1f862b..149b4d1436 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -27,6 +27,7 @@ namespace Umbraco.Web.Search private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; private readonly IValueSetBuilder _mediaValueSetBuilder; private readonly IValueSetBuilder _memberValueSetBuilder; + private readonly BackgroundIndexRebuilder _backgroundIndexRebuilder; private static object _isConfiguredLocker = new object(); private readonly IScopeProvider _scopeProvider; private readonly ServiceContext _services; @@ -47,7 +48,8 @@ namespace Umbraco.Web.Search IContentValueSetBuilder contentValueSetBuilder, IPublishedContentValueSetBuilder publishedContentValueSetBuilder, IValueSetBuilder mediaValueSetBuilder, - IValueSetBuilder memberValueSetBuilder) + IValueSetBuilder memberValueSetBuilder, + BackgroundIndexRebuilder backgroundIndexRebuilder) { _services = services; _scopeProvider = scopeProvider; @@ -56,7 +58,7 @@ namespace Umbraco.Web.Search _publishedContentValueSetBuilder = publishedContentValueSetBuilder; _mediaValueSetBuilder = mediaValueSetBuilder; _memberValueSetBuilder = memberValueSetBuilder; - + _backgroundIndexRebuilder = backgroundIndexRebuilder; _mainDom = mainDom; _logger = profilingLogger; _indexCreator = indexCreator; @@ -111,6 +113,7 @@ namespace Umbraco.Web.Search ContentTypeCacheRefresher.CacheUpdated += ContentTypeCacheRefresherUpdated; MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; + LanguageCacheRefresher.CacheUpdated += LanguageCacheRefresherUpdated; } public void Terminate() @@ -320,6 +323,25 @@ namespace Umbraco.Web.Search } } + private void LanguageCacheRefresherUpdated(LanguageCacheRefresher sender, CacheRefresherEventArgs e) + { + if (!(e.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) + return; + + if (payloads.Length == 0) return; + + var removedOrCultureChanged = payloads.Any(x => + x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture + || x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); + + if (removedOrCultureChanged) + { + //if a lang is removed or it's culture has changed, we need to rebuild the indexes since + //field names and values in the index have a string culture value. + _backgroundIndexRebuilder.RebuildIndexes(false); + } + } + /// /// Updates indexes based on content type changes /// From ae0ad2b8977daf245428c2728a08ce73f05f596f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 19:04:51 +1100 Subject: [PATCH 047/109] Updates UI to allow updating a culture for a language and warn the user if they are changing it, updates the controller to be able to udpate a culture on a lang --- .../src/views/languages/edit.controller.js | 119 ++++++++++++------ .../src/views/languages/overlays/change.html | 7 ++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + .../Cache/DistributedCacheExtensions.cs | 12 +- src/Umbraco.Web/Editors/LanguageController.cs | 35 +++--- 6 files changed, 117 insertions(+), 58 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index a11aa8bff8..5f1c46de4c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function LanguagesEditController($scope, $timeout, $location, $routeParams, navigationService, notificationsService, localizationService, languageResource, contentEditingHelper, formHelper, eventsService) { + function LanguagesEditController($scope, $q, $timeout, $location, $routeParams, overlayService, navigationService, notificationsService, localizationService, languageResource, contentEditingHelper, formHelper, eventsService) { var vm = this; @@ -20,6 +20,8 @@ vm.toggleMandatory = toggleMandatory; vm.toggleDefault = toggleDefault; + var currCulture = null; + function init() { // localize labels @@ -32,7 +34,8 @@ "languages_addLanguage", "languages_noFallbackLanguageOption", "languages_fallbackLanguageDescription", - "languages_fallbackLanguage" + "languages_fallbackLanguage", + "defaultdialogs_confirmSure" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -43,6 +46,7 @@ vm.labels.defaultLanguageHelp = values[4]; vm.labels.addLanguage = values[5]; vm.labels.noFallbackLanguageOption = values[6]; + vm.labels.areYouSure = values[9]; $scope.properties = { fallbackLanguage: { @@ -53,46 +57,56 @@ }; if ($routeParams.create) { - vm.page.name = vm.labels.addLanguage; - languageResource.getCultures().then(function (culturesDictionary) { - var cultures = []; - angular.forEach(culturesDictionary, function (value, key) { - cultures.push({ - name: key, - displayName: value - }); - }); - vm.availableCultures = cultures; - }); + vm.page.name = vm.labels.addLanguage; } }); vm.loading = true; - languageResource.getAll().then(function (languages) { + + var promises = []; + + //load all culture/languages + promises.push(languageResource.getCultures().then(function (culturesDictionary) { + var cultures = []; + angular.forEach(culturesDictionary, function (value, key) { + cultures.push({ + name: key, + displayName: value + }); + }); + vm.availableCultures = cultures; + })); + + //load all possible fallback languages + promises.push(languageResource.getAll().then(function (languages) { vm.availableLanguages = languages.filter(function (l) { return $routeParams.id != l.id; }); vm.loading = false; - }); + })); if (!$routeParams.create) { - vm.loading = true; - - languageResource.getById($routeParams.id).then(function(lang) { + promises.push(languageResource.getById($routeParams.id).then(function(lang) { vm.language = lang; vm.page.name = vm.language.name; - /* we need to store the initial default state so we can disabel the toggle if it is the default. + /* we need to store the initial default state so we can disable the toggle if it is the default. we need to prevent from not having a default language. */ vm.initIsDefault = angular.copy(vm.language.isDefault); - vm.loading = false; makeBreadcrumbs(); - }); + + //store to check if we are changing the lang culture + currCulture = vm.language.culture; + })); } + $q.all(promises, function () { + vm.loading = false; + }); + $timeout(function () { navigationService.syncTree({ tree: "languages", path: "-1" }); }); @@ -103,31 +117,54 @@ if (formHelper.submitForm({ scope: $scope })) { vm.page.saveButtonState = "busy"; - languageResource.save(vm.language).then(function (lang) { + //check if the culture is being changed + if (currCulture && vm.language.culture !== currCulture) { - formHelper.resetForm({ scope: $scope }); + const changeCultureAlert = { + title: vm.labels.areYouSure, + view: "views/languages/overlays/change.html", + submitButtonLabelKey: "general_continue", + submit: function (model) { + saveLanguage(); + overlayService.close(); + }, + close: function () { + overlayService.close(); + vm.page.saveButtonState = "init"; + } + }; - vm.language = lang; - vm.page.saveButtonState = "success"; - localizationService.localize("speechBubbles_languageSaved").then(function(value){ - notificationsService.success(value); - }); - - // emit event when language is created or updated/saved - var args = { language: lang, isNew: $routeParams.create ? true : false }; - eventsService.emit("editors.languages.languageSaved", args); - - back(); - - }, function (err) { - vm.page.saveButtonState = "error"; - - formHelper.handleError(err); - - }); + overlayService.open(changeCultureAlert); + } + else { + saveLanguage(); + } } + } + function saveLanguage() { + languageResource.save(vm.language).then(function (lang) { + formHelper.resetForm({ scope: $scope }); + + vm.language = lang; + vm.page.saveButtonState = "success"; + localizationService.localize("speechBubbles_languageSaved").then(function (value) { + notificationsService.success(value); + }); + + // emit event when language is created or updated/saved + var args = { language: lang, isNew: $routeParams.create ? true : false }; + eventsService.emit("editors.languages.languageSaved", args); + + back(); + + }, function (err) { + vm.page.saveButtonState = "error"; + + formHelper.handleError(err); + + }); } function back() { diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html b/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html new file mode 100644 index 0000000000..c5e813fa51 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html @@ -0,0 +1,7 @@ +
+ +
+ Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt. +
+ +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index d7d743e7e9..86283310fd 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -414,6 +414,7 @@ Click to add a Macro Insert table This will delete the language + Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt Last Edited Link Internal link: diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index eff344d4f3..5fa5ad6ea3 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -417,6 +417,7 @@ Click to add a Macro Insert table This will delete the language + Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt Last Edited Link Internal link: diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 65b29aabc6..b00a4818f6 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -266,13 +266,21 @@ namespace Umbraco.Web.Cache public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language) { if (language == null) return; - dc.Refresh(LanguageCacheRefresher.UniqueId, language.Id); + + var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode, + language.WasPropertyDirty(nameof(ILanguage.IsoCode)) + ? LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture + : LanguageCacheRefresher.JsonPayload.LanguageChangeType.Update); + + dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload }); } public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language) { if (language == null) return; - dc.Remove(LanguageCacheRefresher.UniqueId, language.Id); + + var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode, LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); + dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload }); } #endregion diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index 650dcea6e9..cb7fde23db 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -99,24 +99,28 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); // this is prone to race conditions but the service will not let us proceed anyways - var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode); + var existingByCulture = 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) + if (existingByCulture?.IsoCode != language.IsoCode) { - existing = null; + existingByCulture = null; } - if (existing != null && language.Id != existing.Id) + if (existingByCulture != null && language.Id != existingByCulture.Id) { //someone is trying to create a language that already exist ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists"); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - if (existing == null) + var existingById = language.Id != default ? Services.LocalizationService.GetLanguageById(language.Id) : null; + + if (existingById == null) { + //Creating a new lang... + CultureInfo culture; try { @@ -141,38 +145,39 @@ namespace Umbraco.Web.Editors return Mapper.Map(newLang); } - existing.IsMandatory = language.IsMandatory; + existingById.IsMandatory = language.IsMandatory; // note that the service will prevent the default language from being "un-defaulted" // but does not hurt to test here - though the UI should prevent it too - if (existing.IsDefault && !language.IsDefault) + if (existingById.IsDefault && !language.IsDefault) { ModelState.AddModelError("IsDefault", "Cannot un-default the default language."); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - existing.IsDefault = language.IsDefault; - existing.FallbackLanguageId = language.FallbackLanguageId; + existingById.IsDefault = language.IsDefault; + existingById.FallbackLanguageId = language.FallbackLanguageId; + existingById.IsoCode = language.IsoCode; // modifying an existing language can create a fallback, verify // note that the service will check again, dealing with race conditions - if (existing.FallbackLanguageId.HasValue) + if (existingById.FallbackLanguageId.HasValue) { var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x); - if (!languages.ContainsKey(existing.FallbackLanguageId.Value)) + if (!languages.ContainsKey(existingById.FallbackLanguageId.Value)) { ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist."); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } - if (CreatesCycle(existing, languages)) + if (CreatesCycle(existingById, languages)) { - ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path."); + ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existingById.FallbackLanguageId.Value].IsoCode} would create a circular path."); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } } - Services.LocalizationService.Save(existing); - return Mapper.Map(existing); + Services.LocalizationService.Save(existingById); + return Mapper.Map(existingById); } // see LocalizationService From 90086419655c88c819146f9c4ad3a782289e5012 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 22:02:42 +1100 Subject: [PATCH 048/109] ensures the domain cache is totally refreshed when changing langs --- src/Umbraco.Web/Cache/DomainCacheRefresher.cs | 20 ++------- .../Cache/LanguageCacheRefresher.cs | 45 +++++++++---------- .../NuCache/PublishedSnapshotService.cs | 18 ++++++++ 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs index 37b0a483a6..4ffe4e2717 100644 --- a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs @@ -47,25 +47,13 @@ namespace Umbraco.Web.Cache // these events should never trigger // everything should be PAYLOAD/JSON - public override void RefreshAll() - { - throw new NotSupportedException(); - } + public override void RefreshAll() => throw new NotSupportedException(); - public override void Refresh(int id) - { - throw new NotSupportedException(); - } + public override void Refresh(int id) => throw new NotSupportedException(); - public override void Refresh(Guid id) - { - throw new NotSupportedException(); - } + public override void Refresh(Guid id) => throw new NotSupportedException(); - public override void Remove(int id) - { - throw new NotSupportedException(); - } + public override void Remove(int id) => throw new NotSupportedException(); #endregion diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index 03102ec969..2877874e5a 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Web.PublishedCache; +using static Umbraco.Web.Cache.LanguageCacheRefresher.JsonPayload; namespace Umbraco.Web.Cache { @@ -12,11 +13,10 @@ namespace Umbraco.Web.Cache //CacheRefresherBase { - public LanguageCacheRefresher(AppCaches appCaches, IPublishedSnapshotService publishedSnapshotService, IDomainService domainService) + public LanguageCacheRefresher(AppCaches appCaches, IPublishedSnapshotService publishedSnapshotService) : base(appCaches) { _publishedSnapshotService = publishedSnapshotService; - _domainService = domainService; } #region Define @@ -45,20 +45,21 @@ namespace Umbraco.Web.Cache //clear all no matter what type of payload ClearAllIsolatedCacheByEntityType(); + //clear all no matter what type of payload + RefreshDomains(); + foreach (var payload in payloads) { - RefreshDomains(payload.Id); - switch (payload.ChangeType) { - case JsonPayload.LanguageChangeType.Update: + case LanguageChangeType.Update: clearDictionary = true; break; - case JsonPayload.LanguageChangeType.Remove: + case LanguageChangeType.Remove: clearDictionary = true; clearContent = true; break; - case JsonPayload.LanguageChangeType.ChangeCulture: + case LanguageChangeType.ChangeCulture: clearDictionary = true; clearContent = true; break; @@ -70,11 +71,13 @@ namespace Umbraco.Web.Cache ClearAllIsolatedCacheByEntityType(); } - //if this flag is set, we will tell the published snapshot service to refresh ALL content + //if this flag is set, we will tell the published snapshot service to refresh ALL content and evict ALL IContent items if (clearContent) { + ContentCacheRefresher.RefreshContentTypes(AppCaches); // we need to evict all IContent items + //now refresh all nucache var clearContentPayload = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }; - ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload); + ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload); } // then trigger event @@ -94,23 +97,19 @@ namespace Umbraco.Web.Cache #endregion - private void RefreshDomains(int langId) + /// + /// Clears all domain caches + /// + private void RefreshDomains() { - var assignedDomains = _domainService.GetAll(true).Where(x => x.LanguageId.HasValue && x.LanguageId.Value == langId).ToList(); + ClearAllIsolatedCacheByEntityType(); - if (assignedDomains.Count > 0) - { - // TODO: this is duplicating the logic in DomainCacheRefresher BUT we cannot inject that into this because it it not registered explicitly in the container, - // and we cannot inject the CacheRefresherCollection since that would be a circular reference, so what is the best way to call directly in to the - // DomainCacheRefresher? + // note: must do what's above FIRST else the repositories still have the old cached + // content and when the PublishedCachesService is notified of changes it does not see + // the new content... - ClearAllIsolatedCacheByEntityType(); - // note: must do what's above FIRST else the repositories still have the old cached - // content and when the PublishedCachesService is notified of changes it does not see - // the new content... - // notify - _publishedSnapshotService.Notify(assignedDomains.Select(x => new DomainCacheRefresher.JsonPayload(x.Id, DomainChangeTypes.Remove)).ToArray()); - } + var payloads = new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }; + _publishedSnapshotService.Notify(payloads); } #region Json diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 629f5694bc..0c531d8cf8 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -235,6 +235,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentTypeService.ScopedRefreshedEntity += OnContentTypeRefreshedEntity; MediaTypeService.ScopedRefreshedEntity += OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity += OnMemberTypeRefreshedEntity; + + LocalizationService.SavedLanguage += OnLanguageSaved; } private void TearDownRepositoryEvents() @@ -252,6 +254,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentTypeService.ScopedRefreshedEntity -= OnContentTypeRefreshedEntity; MediaTypeService.ScopedRefreshedEntity -= OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity -= OnMemberTypeRefreshedEntity; + + LocalizationService.SavedLanguage -= OnLanguageSaved; } public override void Dispose() @@ -1260,6 +1264,20 @@ namespace Umbraco.Web.PublishedCache.NuCache RebuildMemberDbCache(contentTypeIds: memberTypeIds); } + /// + /// If a is ever saved with a different culture, we need to rebuild all of the content nucache table + /// + /// + /// + private void OnLanguageSaved(ILocalizationService sender, Core.Events.SaveEventArgs e) + { + var cultureChanged = e.SavedEntities.Any(x => x.WasPropertyDirty(nameof(ILanguage.IsoCode))); + if(cultureChanged) + { + RebuildContentDbCache(); + } + } + private ContentNuDto GetDto(IContentBase content, bool published) { // should inject these in ctor From 00b5f59bce8a74feeab5efc1915908790c12ca34 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 22:09:47 +1100 Subject: [PATCH 049/109] simplifies lang refresher a bit --- src/Umbraco.Web/Cache/LanguageCacheRefresher.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index 2877874e5a..8463acd6e0 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -45,9 +45,6 @@ namespace Umbraco.Web.Cache //clear all no matter what type of payload ClearAllIsolatedCacheByEntityType(); - //clear all no matter what type of payload - RefreshDomains(); - foreach (var payload in payloads) { switch (payload.ChangeType) @@ -56,9 +53,6 @@ namespace Umbraco.Web.Cache clearDictionary = true; break; case LanguageChangeType.Remove: - clearDictionary = true; - clearContent = true; - break; case LanguageChangeType.ChangeCulture: clearDictionary = true; clearContent = true; @@ -74,6 +68,8 @@ namespace Umbraco.Web.Cache //if this flag is set, we will tell the published snapshot service to refresh ALL content and evict ALL IContent items if (clearContent) { + //clear all domain caches + RefreshDomains(); ContentCacheRefresher.RefreshContentTypes(AppCaches); // we need to evict all IContent items //now refresh all nucache var clearContentPayload = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }; From d0435f6625e6a3925547b31ec9a736ff35e5e1db Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Oct 2019 00:04:41 +1100 Subject: [PATCH 050/109] Moves read/write locking and isolation level to the responsibility of the sql provider based on the db type --- .../Persistence/DatabasenodeLockExtensions.cs | 43 ------------------- .../SqlSyntax/ISqlSyntaxProvider.cs | 9 ++++ .../SqlSyntax/SqlCeSyntaxProvider.cs | 37 ++++++++++++++++ .../SqlSyntax/SqlServerSyntaxProvider.cs | 38 ++++++++++++++++ .../SqlSyntax/SqlSyntaxProviderBase.cs | 7 ++- .../Persistence/UmbracoDatabase.cs | 7 +-- src/Umbraco.Core/Scoping/Scope.cs | 36 ++-------------- src/Umbraco.Core/Umbraco.Core.csproj | 1 - .../Models/UserExtensionsTests.cs | 3 ++ src/Umbraco.Tests/Persistence/LocksTests.cs | 13 +++--- 10 files changed, 104 insertions(+), 90 deletions(-) delete mode 100644 src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs diff --git a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs deleted file mode 100644 index 48edee3c94..0000000000 --- a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Runtime.CompilerServices; - -namespace Umbraco.Core.Persistence -{ - internal static class DatabaseNodeLockExtensions - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ValidateDatabase(IUmbracoDatabase database) - { - if (database == null) - throw new ArgumentNullException("database"); - if (database.GetCurrentTransactionIsolationLevel() < IsolationLevel.RepeatableRead) - throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); - } - - // updating a record within a repeatable-read transaction gets an exclusive lock on - // that record which will be kept until the transaction is ended, effectively locking - // out all other accesses to that record - thus obtaining an exclusive lock over the - // protected resources. - public static void AcquireLockNodeWriteLock(this IUmbracoDatabase database, int nodeId) - { - ValidateDatabase(database); - - database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", - new { @id = nodeId }); - } - - // reading a record within a repeatable-read transaction gets a shared lock on - // that record which will be kept until the transaction is ended, effectively preventing - // other write accesses to that record - thus obtaining a shared lock over the protected - // resources. - public static void AcquireLockNodeReadLock(this IUmbracoDatabase database, int nodeId) - { - ValidateDatabase(database); - - database.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", - new { @id = nodeId }); - } - } -} diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 55625ff04e..7ae001bf24 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Text.RegularExpressions; using NPoco; using Umbraco.Core.Persistence.DatabaseAnnotations; @@ -76,6 +77,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax string ConvertIntegerToOrderableString { get; } string ConvertDateToOrderableString { get; } string ConvertDecimalToOrderableString { get; } + + /// + /// Returns the default isolation level for the database + /// + IsolationLevel DefaultIsolationLevel { get; } IEnumerable GetTablesInSchema(IDatabase db); IEnumerable GetColumnsInSchema(IDatabase db); @@ -121,5 +127,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// unspecified. /// bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName); + + void ReadLock(IDatabase db, params int[] lockIds); + void WriteLock(IDatabase db, params int[] lockIds); } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index cb4b7a5176..92ee934e52 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Data; +using System.Data.SqlServerCe; using System.Linq; using NPoco; using Umbraco.Core.Persistence.DatabaseAnnotations; @@ -52,6 +54,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax return "(" + string.Join("+", args) + ")"; } + public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.RepeatableRead; + public override string FormatColumnRename(string tableName, string oldName, string newName) { //NOTE Sql CE doesn't support renaming a column, so a new column needs to be created, then copy data and finally remove old column @@ -152,6 +156,39 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() return result > 0; } + public override void WriteLock(IDatabase db, params int[] lockIds) + { + // soon as we get Database, a transaction is started + + if (db.Transaction.IsolationLevel < IsolationLevel.RepeatableRead) + throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); + + db.Execute(@"SET LOCK_TIMEOUT 1800;"); + // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks + foreach (var lockId in lockIds) + { + var i = db.Execute(@"UPDATE umbracoLock SET value = value*-1 WHERE id=@id", new { id = lockId }); + if (i == 0) // ensure we are actually locking! + throw new Exception($"LockObject with id={lockId} does not exist."); + } + } + + public override void ReadLock(IDatabase db, params int[] lockIds) + { + // soon as we get Database, a transaction is started + + if (db.Transaction.IsolationLevel < IsolationLevel.RepeatableRead) + throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); + + // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks + foreach (var lockId in lockIds) + { + var i = db.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId }); + if (i == null) // ensure we are actually locking! + throw new Exception($"LockObject with id={lockId} does not exist."); + } + } + protected override string FormatIdentity(ColumnDefinition column) { return column.IsIdentity ? GetIdentityString(column) : string.Empty; diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index fab7526a6b..7bf00522e3 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Data.SqlClient; using System.Linq; using NPoco; using Umbraco.Core.Logging; @@ -179,6 +180,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(x => x.TABLE_NAME).Cast().ToList(); } + public override IsolationLevel DefaultIsolationLevel => IsolationLevel.ReadCommitted; + public override IEnumerable GetColumnsInSchema(IDatabase db) { var items = db.Fetch("SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = (SELECT SCHEMA_NAME())"); @@ -246,6 +249,41 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName) return result > 0; } + public override void WriteLock(IDatabase db, params int[] lockIds) + { + // soon as we get Database, a transaction is started + + if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted) + throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required."); + + + // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks + foreach (var lockId in lockIds) + { + db.Execute(@"SET LOCK_TIMEOUT 1800;"); + var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = value*-1 WHERE id=@id", new { id = lockId }); + if (i == 0) // ensure we are actually locking! + throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); + } + } + + + public override void ReadLock(IDatabase db, params int[] lockIds) + { + // soon as we get Database, a transaction is started + + if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted) + throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required."); + + // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks + foreach (var lockId in lockIds) + { + var i = db.ExecuteScalar("SELECT value FROM umbracoLock WITH (REPEATABLEREAD) WHERE id=@id", new { id = lockId }); + if (i == null) // ensure we are actually locking! + throw new ArgumentException($"LockObject with id={lockId} does not exist.", nameof(lockIds)); + } + } + public override string FormatColumnRename(string tableName, string oldName, string newName) { return string.Format(RenameColumn, tableName, oldName, newName); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 0c27ac2d50..b2e03df96e 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -200,7 +200,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax return "NVARCHAR"; } - + + public abstract IsolationLevel DefaultIsolationLevel { get; } + public virtual IEnumerable GetTablesInSchema(IDatabase db) { return new List(); @@ -225,6 +227,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax public abstract bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName); + public abstract void ReadLock(IDatabase db, params int[] lockIds); + public abstract void WriteLock(IDatabase db, params int[] lockIds); + public virtual bool DoesTableExist(IDatabase db, string tableName) { return false; diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 51e0172f35..072813b4e6 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -20,9 +20,6 @@ namespace Umbraco.Core.Persistence /// public class UmbracoDatabase : Database, IUmbracoDatabase { - // Umbraco's default isolation level is RepeatableRead - private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead; - private readonly ILogger _logger; private readonly RetryPolicy _connectionRetryPolicy; private readonly RetryPolicy _commandRetryPolicy; @@ -38,7 +35,7 @@ namespace Umbraco.Core.Persistence /// Also used by DatabaseBuilder for creating databases and installing/upgrading. /// public UmbracoDatabase(string connectionString, ISqlContext sqlContext, DbProviderFactory provider, ILogger logger, RetryPolicy connectionRetryPolicy = null, RetryPolicy commandRetryPolicy = null) - : base(connectionString, sqlContext.DatabaseType, provider, DefaultIsolationLevel) + : base(connectionString, sqlContext.DatabaseType, provider, sqlContext.SqlSyntax.DefaultIsolationLevel) { SqlContext = sqlContext; @@ -54,7 +51,7 @@ namespace Umbraco.Core.Persistence /// /// Internal for unit tests only. internal UmbracoDatabase(DbConnection connection, ISqlContext sqlContext, ILogger logger) - : base(connection, sqlContext.DatabaseType, DefaultIsolationLevel) + : base(connection, sqlContext.DatabaseType, sqlContext.SqlSyntax.DefaultIsolationLevel) { SqlContext = sqlContext; _logger = logger; diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index e9dd04c5fa..84273e23da 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -33,8 +33,6 @@ namespace Umbraco.Core.Scoping private ICompletable _fscope; private IEventDispatcher _eventDispatcher; - private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead; - // initializes a new scope private Scope(ScopeProvider scopeProvider, ILogger logger, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable, @@ -205,7 +203,7 @@ namespace Umbraco.Core.Scoping { if (_isolationLevel != IsolationLevel.Unspecified) return _isolationLevel; if (ParentScope != null) return ParentScope.IsolationLevel; - return DefaultIsolationLevel; + return Database.SqlContext.SqlSyntax.DefaultIsolationLevel; } } @@ -488,37 +486,9 @@ namespace Umbraco.Core.Scoping ?? (_logUncompletedScopes = Current.Configs.CoreDebug().LogUncompletedScopes)).Value; /// - public void ReadLock(params int[] lockIds) - { - // soon as we get Database, a transaction is started - - if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead) - throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); - - // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks - foreach (var lockId in lockIds) - { - var i = Database.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId }); - if (i == null) // ensure we are actually locking! - throw new Exception($"LockObject with id={lockId} does not exist."); - } - } + public void ReadLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.ReadLock(Database, lockIds); /// - public void WriteLock(params int[] lockIds) - { - // soon as we get Database, a transaction is started - - if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead) - throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); - - // *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks - foreach (var lockId in lockIds) - { - var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId }); - if (i == 0) // ensure we are actually locking! - throw new Exception($"LockObject with id={lockId} does not exist."); - } - } + public void WriteLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.WriteLock(Database, lockIds); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a0d0fad6d9..62cd245265 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -982,7 +982,6 @@ - diff --git a/src/Umbraco.Tests/Models/UserExtensionsTests.cs b/src/Umbraco.Tests/Models/UserExtensionsTests.cs index d9c8057377..bf76031b59 100644 --- a/src/Umbraco.Tests/Models/UserExtensionsTests.cs +++ b/src/Umbraco.Tests/Models/UserExtensionsTests.cs @@ -57,6 +57,9 @@ namespace Umbraco.Tests.Models [TestCase("1,-1", "1", "1")] // was an issue [TestCase("-1,1", "1", "1")] // was an issue + [TestCase("-1", "", "-1")] + [TestCase("", "-1", "-1")] + public void CombineStartNodes(string groupSn, string userSn, string expected) { // 1 diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index 56779ace0f..51298035a7 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -5,7 +5,6 @@ using System.Threading; using NPoco; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -37,7 +36,7 @@ namespace Umbraco.Tests.Persistence { using (var scope = ScopeProvider.CreateScope()) { - scope.Database.AcquireLockNodeReadLock(Constants.Locks.Servers); + scope.WriteLock(Constants.Locks.Servers); scope.Complete(); } } @@ -62,7 +61,7 @@ namespace Umbraco.Tests.Persistence { try { - scope.Database.AcquireLockNodeReadLock(Constants.Locks.Servers); + scope.ReadLock(Constants.Locks.Servers); lock (locker) { acquired++; @@ -131,7 +130,7 @@ namespace Umbraco.Tests.Persistence if (entered == threadCount) m1.Set(); } ms[ic].WaitOne(); - scope.Database.AcquireLockNodeWriteLock(Constants.Locks.Servers); + scope.WriteLock(Constants.Locks.Servers); lock (locker) { acquired++; @@ -221,7 +220,7 @@ namespace Umbraco.Tests.Persistence { otherEv.WaitOne(); Console.WriteLine($"[{id1}] WAIT {id1}"); - scope.Database.AcquireLockNodeWriteLock(id1); + scope.WriteLock(id1); Console.WriteLine($"[{id1}] GRANT {id1}"); WriteLocks(scope.Database); myEv.Set(); @@ -232,7 +231,7 @@ namespace Umbraco.Tests.Persistence Thread.Sleep(200); // cannot wait due to deadlock... just give it a bit of time Console.WriteLine($"[{id1}] WAIT {id2}"); - scope.Database.AcquireLockNodeWriteLock(id2); + scope.WriteLock(id2); Console.WriteLine($"[{id1}] GRANT {id2}"); WriteLocks(scope.Database); } @@ -284,7 +283,7 @@ namespace Umbraco.Tests.Persistence { otherEv.WaitOne(); Console.WriteLine($"[{id}] WAIT {id}"); - scope.Database.AcquireLockNodeWriteLock(id); + scope.WriteLock(id); Console.WriteLine($"[{id}] GRANT {id}"); WriteLocks(scope.Database); myEv.Set(); From dcc46fa32d58d66d9d9015ce0a81c0ee8abf073e Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Oct 2019 11:14:28 +1100 Subject: [PATCH 051/109] no generic exception --- src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 92ee934e52..4e541c80d1 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -169,7 +169,7 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() { var i = db.Execute(@"UPDATE umbracoLock SET value = value*-1 WHERE id=@id", new { id = lockId }); if (i == 0) // ensure we are actually locking! - throw new Exception($"LockObject with id={lockId} does not exist."); + throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); } } @@ -185,7 +185,7 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() { var i = db.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId }); if (i == null) // ensure we are actually locking! - throw new Exception($"LockObject with id={lockId} does not exist."); + throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); } } From 37024664f5d39452effd10c635aed256a76551b7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Oct 2019 11:15:17 +1100 Subject: [PATCH 052/109] streamlines exception types --- src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs | 4 ++-- .../Persistence/SqlSyntax/SqlServerSyntaxProvider.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 4e541c80d1..141a1edadd 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -169,7 +169,7 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() { var i = db.Execute(@"UPDATE umbracoLock SET value = value*-1 WHERE id=@id", new { id = lockId }); if (i == 0) // ensure we are actually locking! - throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); + throw new ArgumentException($"LockObject with id={lockId} does not exist."); } } @@ -185,7 +185,7 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() { var i = db.ExecuteScalar("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId }); if (i == null) // ensure we are actually locking! - throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); + throw new ArgumentException($"LockObject with id={lockId} does not exist."); } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 7bf00522e3..f90b61968f 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -263,7 +263,7 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName) db.Execute(@"SET LOCK_TIMEOUT 1800;"); var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = value*-1 WHERE id=@id", new { id = lockId }); if (i == 0) // ensure we are actually locking! - throw new InvalidOperationException($"LockObject with id={lockId} does not exist."); + throw new ArgumentException($"LockObject with id={lockId} does not exist."); } } From 2b14408693d03f3f909d27188b03700a42977b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Tue, 15 Oct 2019 11:17:03 +0200 Subject: [PATCH 053/109] Revert "Moved property type variation auto correct." This reverts commit 3858bd40adf5fc6c8181c1c8518d41b3e01fd824. --- .../Repositories/Implement/ContentTypeRepository.cs | 10 ---------- .../Implement/ContentTypeRepositoryBase.cs | 10 +++++++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs index 7c7f39fa54..359b967dab 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -287,16 +287,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.SortOrder = maxSortOrder + 1; } - // Update property variations based on the content type variation - foreach (var propertyType in entity.PropertyTypes) - { - // Determine variation for the property type. - // The property is only considered culture variant when the base content type is also culture variant. - // The property is only considered segment variant when the base content type is also segment variant. - // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture - propertyType.Variations = entity.Variations & propertyType.Variations; - } - PersistUpdatedBaseContentType(entity); PersistTemplates(entity, true); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index dec5805cf2..2d70e89aa0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -411,7 +411,15 @@ AND umbracoNode.id <> @id", // note: this only deals with *local* property types, we're dealing w/compositions later below foreach (var propertyType in entity.PropertyTypes) { - // track each property individually + // Update property variations + + // Determine target variation of the property type. + // The property is only considered culture variant when the base content type is also culture variant. + // The property is only considered segment variant when the base content type is also segment variant. + // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture + propertyType.Variations = newContentTypeVariation & propertyType.Variations; + + // then, track each property individually if (propertyType.IsPropertyDirty("Variations")) { // allocate the list only when needed From c8a8d2e23508c3049c1fc7bd1594b0147856c47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Tue, 15 Oct 2019 11:21:12 +0200 Subject: [PATCH 054/109] Moved property type variation "auto correction" into separate method and perform before validation. --- .../Implement/ContentTypeRepositoryBase.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 2d70e89aa0..6385482686 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -219,6 +219,7 @@ AND umbracoNode.nodeObjectType = @objectType", protected void PersistUpdatedBaseContentType(IContentTypeComposition entity) { + CorrectPropertyTypeVariations(entity); ValidateVariations(entity); var dto = ContentTypeFactory.BuildContentTypeDto(entity); @@ -411,15 +412,7 @@ AND umbracoNode.id <> @id", // note: this only deals with *local* property types, we're dealing w/compositions later below foreach (var propertyType in entity.PropertyTypes) { - // Update property variations - - // Determine target variation of the property type. - // The property is only considered culture variant when the base content type is also culture variant. - // The property is only considered segment variant when the base content type is also segment variant. - // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture - propertyType.Variations = newContentTypeVariation & propertyType.Variations; - - // then, track each property individually + // track each property individually if (propertyType.IsPropertyDirty("Variations")) { // allocate the list only when needed @@ -510,6 +503,25 @@ AND umbracoNode.id <> @id", CommonRepository.ClearCache(); // always } + /// + /// Corrects the property type variations for the given entity + /// to make sure the property type variation is compatible with the + /// variation set on the entity itself. + /// + /// Entity to correct properties for + private void CorrectPropertyTypeVariations(IContentTypeComposition entity) + { + // Update property variations based on the content type variation + foreach (var propertyType in entity.PropertyTypes) + { + // Determine variation for the property type. + // The property is only considered culture variant when the base content type is also culture variant. + // The property is only considered segment variant when the base content type is also segment variant. + // Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture + propertyType.Variations = entity.Variations & propertyType.Variations; + } + } + /// /// Ensures that no property types are flagged for a variance that is not supported by the content type itself /// From 4495a4ab5a9a6f716c3a8c91bb526134de8173fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Tue, 15 Oct 2019 11:50:16 +0200 Subject: [PATCH 055/109] Updated various test cases to include the Segment flag. The original test method has been changed to a method with parameters for the ContentVariation "from" and "to" options and [TestCase] attributes are applied to run the different combinations. --- .../ContentTypeServiceVariantsTests.cs | 97 +++++++++++-------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 7affe2de3f..0fe05f385f 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -380,12 +380,16 @@ namespace Umbraco.Tests.Services Assert.AreEqual(properties.First().Variations, contentTypeVariation & propertyTypeVariation); } - [Test] - public void Change_Property_Type_From_Invariant_Variant() + [TestCase(ContentVariation.Nothing, ContentVariation.Culture)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment)] + public void Change_Property_Type_From_Invariant_Variant(ContentVariation invariant, ContentVariation variant) { var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; - var properties = CreatePropertyCollection(("title", ContentVariation.Nothing)); + // content type supports all variations + contentType.Variations = ContentVariation.Culture | ContentVariation.Segment; + var properties = CreatePropertyCollection(("title", invariant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); @@ -401,7 +405,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(doc.Edited); //change the property type to be variant - contentType.PropertyTypes.First().Variations = ContentVariation.Culture; + contentType.PropertyTypes.First().Variations = variant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -410,7 +414,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(doc.Edited); //change back property type to be invariant - contentType.PropertyTypes.First().Variations = ContentVariation.Nothing; + contentType.PropertyTypes.First().Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get @@ -419,13 +423,17 @@ namespace Umbraco.Tests.Services Assert.IsTrue(doc.Edited); } - [Test] - public void Change_Property_Type_From_Variant_Invariant() + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + public void Change_Property_Type_From_Variant_Invariant(ContentVariation variant, ContentVariation invariant) { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; - var properties = CreatePropertyCollection(("title", ContentVariation.Culture)); + // content type supports all variations + contentType.Variations = ContentVariation.Culture | ContentVariation.Segment; + var properties = CreatePropertyCollection(("title", variant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); @@ -438,33 +446,37 @@ namespace Umbraco.Tests.Services Assert.AreEqual("hello world", doc.GetValue("title", "en-US")); //change the property type to be invariant - contentType.PropertyTypes.First().Variations = ContentVariation.Nothing; + contentType.PropertyTypes.First().Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get Assert.AreEqual("hello world", doc.GetValue("title")); //change back property type to be variant - contentType.PropertyTypes.First().Variations = ContentVariation.Culture; + contentType.PropertyTypes.First().Variations = variant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get Assert.AreEqual("hello world", doc.GetValue("title", "en-US")); } - [Test] - public void Change_Property_Type_From_Variant_Invariant_On_A_Composition() + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + public void Change_Property_Type_From_Variant_Invariant_On_A_Composition(ContentVariation variant, ContentVariation invariant) { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; - var properties = CreatePropertyCollection(("title", ContentVariation.Culture)); + // content type supports all variations + contentType.Variations = ContentVariation.Culture | ContentVariation.Segment; + var properties = CreatePropertyCollection(("title", variant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); //compose this from the other one var contentType2 = MockedContentTypes.CreateBasicContentType("test"); - contentType2.Variations = ContentVariation.Culture; + contentType2.Variations = contentType.Variations; contentType2.AddContentType(contentType); ServiceContext.ContentTypeService.Save(contentType2); @@ -480,7 +492,7 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(doc2); //change the property type to be invariant - contentType.PropertyTypes.First().Variations = ContentVariation.Nothing; + contentType.PropertyTypes.First().Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get @@ -489,7 +501,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual("hello world", doc2.GetValue("title")); //change back property type to be variant - contentType.PropertyTypes.First().Variations = ContentVariation.Culture; + contentType.PropertyTypes.First().Variations = variant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get @@ -498,19 +510,22 @@ namespace Umbraco.Tests.Services Assert.AreEqual("hello world", doc2.GetValue("title", "en-US")); } - [Test] - public void Change_Content_Type_From_Variant_Invariant_On_A_Composition() + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + public void Change_Content_Type_From_Variant_Invariant_On_A_Composition(ContentVariation variant, ContentVariation invariant) { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = ContentVariation.Culture; + contentType.Variations = variant; var properties = CreatePropertyCollection(("title", ContentVariation.Culture)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); //compose this from the other one var contentType2 = MockedContentTypes.CreateBasicContentType("test"); - contentType2.Variations = ContentVariation.Culture; + contentType2.Variations = contentType.Variations; contentType2.AddContentType(contentType); ServiceContext.ContentTypeService.Save(contentType2); @@ -526,7 +541,7 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(doc2); //change the content type to be invariant - contentType.Variations = ContentVariation.Nothing; + contentType.Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get @@ -535,7 +550,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual("hello world", doc2.GetValue("title")); //change back content type to be variant - contentType.Variations = ContentVariation.Culture; + contentType.Variations = variant; ServiceContext.ContentTypeService.Save(contentType); doc = ServiceContext.ContentService.GetById(doc.Id); //re-get doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get @@ -811,22 +826,25 @@ namespace Umbraco.Tests.Services "{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'en','seg':'','val':'v2'}]},'cultureData':"); } - [Test] - public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edited_Values_Are_Renormalized() + [TestCase(ContentVariation.Culture, ContentVariation.Nothing)] + [TestCase(ContentVariation.Culture, ContentVariation.Segment)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Nothing)] + [TestCase(ContentVariation.CultureAndSegment, ContentVariation.Segment)] + public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edited_Values_Are_Renormalized(ContentVariation variant, ContentVariation invariant) { // one simple content type, variant, with both variant and invariant properties // can change an invariant property to variant and back CreateFrenchAndEnglishLangs(); - var contentType = CreateContentType(ContentVariation.Culture); + var contentType = CreateContentType(ContentVariation.Culture | ContentVariation.Segment); - var properties = CreatePropertyCollection(("value1", ContentVariation.Culture)); + var properties = CreatePropertyCollection(("value1", variant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); - var document = (IContent)new Content("document", -1, contentType); + IContent document = new Content("document", -1, contentType); document.SetCultureName("doc1en", "en"); document.SetCultureName("doc1fr", "fr"); document.SetValue("value1", "v1en-init", "en"); @@ -855,7 +873,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(document.Edited); // switch property type to Invariant - contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Nothing; + contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag document = ServiceContext.ContentService.GetById(document.Id); @@ -882,7 +900,7 @@ namespace Umbraco.Tests.Services Assert.IsFalse(document.Edited); // switch property back to Culture - contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture; + contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = variant; ServiceContext.ContentTypeService.Save(contentType); document = ServiceContext.ContentService.GetById(document.Id); @@ -911,17 +929,20 @@ namespace Umbraco.Tests.Services Assert.IsFalse(document.Edited); } - [Test] - public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edited_Values_Are_Renormalized() + [TestCase(ContentVariation.Nothing, ContentVariation.Culture)] + [TestCase(ContentVariation.Nothing, ContentVariation.CultureAndSegment)] + [TestCase(ContentVariation.Segment, ContentVariation.Culture)] + [TestCase(ContentVariation.Segment, ContentVariation.CultureAndSegment)] + public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edited_Values_Are_Renormalized(ContentVariation invariant, ContentVariation variant) { // one simple content type, variant, with both variant and invariant properties // can change an invariant property to variant and back CreateFrenchAndEnglishLangs(); - var contentType = CreateContentType(ContentVariation.Culture); + var contentType = CreateContentType(ContentVariation.Culture | ContentVariation.Segment); - var properties = CreatePropertyCollection(("value1", ContentVariation.Nothing)); + var properties = CreatePropertyCollection(("value1", invariant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); ServiceContext.ContentTypeService.Save(contentType); @@ -951,7 +972,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(document.Edited); // switch property type to Culture - contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture; + contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = variant; ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag document = ServiceContext.ContentService.GetById(document.Id); @@ -976,7 +997,7 @@ namespace Umbraco.Tests.Services Assert.IsFalse(document.Edited); // switch property back to Invariant - contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Nothing; + contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); document = ServiceContext.ContentService.GetById(document.Id); From 14d0bd056c6695f14b77e03ee6de818fa9004fc3 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 3 Oct 2019 20:58:01 +0200 Subject: [PATCH 056/109] Updated list view headers styling to have the link/hand cursor visible only if the header is sortable (and hence clicking has an effect) --- src/Umbraco.Web.UI.Client/src/less/components/umb-table.less | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 27b64f85fb..c2d019a64a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -50,18 +50,19 @@ input.umb-table__input { font-weight: bold; } -.umb-table-head__link { +a.umb-table-head__link { position: relative; cursor: default; text-decoration: none; color: @gray-3; &:hover { text-decoration: none; + cursor: default; color: @gray-3; } } -.umb-table-head__link .sortable { +a.umb-table-head__link.sortable { &:hover { text-decoration: none; cursor: pointer; From 28ec4da0faf5ddea645d95fd891da9f7eea23290 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 15 Oct 2019 13:18:45 +0200 Subject: [PATCH 057/109] Allows members to be ordered by additional system fields (#6575) --- .../Repositories/Implement/MemberRepository.cs | 11 ++++++++++- .../listview/listview.controller.js | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 1fc3568fc0..892122dff9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -133,7 +133,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // joining the type so we can do a query against the member type - not sure if this adds much overhead or not? // the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content - // types by default on the document and media repo's so we can query by content type there too. + // types by default on the document and media repos so we can query by content type there too. .InnerJoin().On(left => left.ContentTypeId, right => right.NodeId); sql.Where(x => x.NodeObjectType == NodeObjectTypeId); @@ -546,6 +546,15 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (ordering.OrderBy.InvariantEquals("userName")) return SqlSyntax.GetFieldName(x => x.LoginName); + if (ordering.OrderBy.InvariantEquals("updateDate")) + return SqlSyntax.GetFieldName(x => x.VersionDate); + + if (ordering.OrderBy.InvariantEquals("createDate")) + return SqlSyntax.GetFieldName(x => x.CreateDate); + + if (ordering.OrderBy.InvariantEquals("contentTypeAlias")) + return SqlSyntax.GetFieldName(x => x.Alias); + return base.ApplySystemOrdering(ref sql, ordering); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index fb01875733..0437522845 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -193,9 +193,14 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs _.each($scope.options.includeProperties, function (e, i) { e.allowSorting = true; - // Special case for members, only fields on the base table (cmsMember) can be used for sorting - if (e.isSystem && $scope.entityType == "member") { - e.allowSorting = e.alias == 'username' || e.alias == 'email'; + // Special case for members, only the configured system fields should be enabled sorting + // (see MemberRepository.ApplySystemOrdering) + if (e.isSystem && $scope.entityType === "member") { + e.allowSorting = e.alias === "username" || + e.alias === "email" || + e.alias === "updateDate" || + e.alias === "createDate" || + e.alias === "contentTypeAlias"; } if (e.isSystem) { @@ -779,8 +784,11 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs case "published": return "content_isPublished"; case "contentTypeAlias": - // TODO: Check for members - return $scope.entityType === "content" ? "content_documentType" : "content_mediatype"; + return $scope.entityType === "content" + ? "content_documentType" + : $scope.entityType === "media" + ? "content_mediatype" + : "content_membertype"; case "email": return "general_email"; case "username": From c8c5dfe594e9fe4a13c66de2887ff46bd9e99c88 Mon Sep 17 00:00:00 2001 From: rbottema Date: Tue, 15 Oct 2019 13:25:50 +0200 Subject: [PATCH 058/109] =?UTF-8?q?Fix=20ContentType.Alias=20matching=20in?= =?UTF-8?q?=20PublishedContentExtensions=20an=E2=80=A6=20(#6577)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Umbraco.Web/PublishedContentExtensions.cs | 24 +++++++++---------- src/Umbraco.Web/PublishedElementExtensions.cs | 7 +++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index d15d5817f6..75eb6adbcb 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web /// A value indicating whether the content is of a content type composed of a content type identified by the alias. public static bool IsComposedOf(this IPublishedContent content, string alias) { - return content.ContentType.CompositionAliases.Contains(alias); + return content.ContentType.CompositionAliases.InvariantContains(alias); } #endregion @@ -421,7 +421,7 @@ namespace Umbraco.Web /// Does not consider the content itself. Returns all ancestors, of the specified content type. public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias) { - return content.AncestorsOrSelf(false, n => n.ContentType.Alias == contentTypeAlias); + return content.AncestorsOrSelf(false, n => n.ContentType.Alias.InvariantEquals(contentTypeAlias)); } /// @@ -486,7 +486,7 @@ namespace Umbraco.Web /// May or may not begin with the content itself, depending on its content type. public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.AncestorsOrSelf(true, n => n.ContentType.Alias == contentTypeAlias); + return content.AncestorsOrSelf(true, n => n.ContentType.Alias.InvariantEquals(contentTypeAlias)); } /// @@ -549,7 +549,7 @@ namespace Umbraco.Web /// Does not consider the content itself. May return null. public static IPublishedContent Ancestor(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); + return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } /// @@ -612,7 +612,7 @@ namespace Umbraco.Web /// May or may not return the content itself depending on its content type. May return null. public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); + return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } /// @@ -727,7 +727,7 @@ namespace Umbraco.Web public static IEnumerable DescendantsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.DescendantsOrSelf(false, p => p.ContentType.Alias == contentTypeAlias, culture); + return content.DescendantsOrSelf(false, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); } public static IEnumerable Descendants(this IPublishedContent content, string culture = null) @@ -754,7 +754,7 @@ namespace Umbraco.Web public static IEnumerable DescendantsOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.DescendantsOrSelf(true, p => p.ContentType.Alias == contentTypeAlias, culture); + return content.DescendantsOrSelf(true, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); } public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string culture = null) @@ -781,7 +781,7 @@ namespace Umbraco.Web public static IPublishedContent DescendantOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.EnumerateDescendants(false, culture).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); + return content.EnumerateDescendants(false, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } public static T Descendant(this IPublishedContent content, string culture = null) @@ -808,7 +808,7 @@ namespace Umbraco.Web public static IPublishedContent DescendantOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.EnumerateDescendants(true, culture).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); + return content.EnumerateDescendants(true, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } public static T DescendantOrSelf(this IPublishedContent content, string culture = null) @@ -892,7 +892,7 @@ namespace Umbraco.Web /// The children of the content, of any of the specified types. public static IEnumerable ChildrenOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.Children(x => contentTypeAlias.InvariantContains(x.ContentType.Alias), culture); + return content.Children(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); } /// @@ -973,7 +973,7 @@ namespace Umbraco.Web ? content.Children(culture).Any() ? content.Children(culture).ElementAt(0) : null - : content.Children(culture).FirstOrDefault(x => x.ContentType.Alias == contentTypeAliasFilter); + : content.Children(culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAliasFilter)); if (firstNode == null) return new DataTable(); //no children found @@ -993,7 +993,7 @@ namespace Umbraco.Web { if (contentTypeAliasFilter.IsNullOrWhiteSpace() == false) { - if (n.ContentType.Alias != contentTypeAliasFilter) + if (n.ContentType.Alias.InvariantEquals(contentTypeAliasFilter) == false) continue; //skip this one, it doesn't match the filter } diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index c35c85c606..f28581eb42 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web /// A value indicating whether the content is of a content type composed of a content type identified by the alias. public static bool IsComposedOf(this IPublishedElement content, string alias) { - return content.ContentType.CompositionAliases.Contains(alias); + return content.ContentType.CompositionAliases.InvariantContains(alias); } #endregion @@ -165,8 +165,9 @@ namespace Umbraco.Web public static IEnumerable OfTypes(this IEnumerable contents, params string[] types) where T : IPublishedElement { - types = types.Select(x => x.ToLowerInvariant()).ToArray(); - return contents.Where(x => types.Contains(x.ContentType.Alias.ToLowerInvariant())); + if (types == null || types.Length == 0) return Enumerable.Empty(); + + return contents.Where(x => types.InvariantContains(x.ContentType.Alias)); } #endregion From 4e345a8f796370b78c7c01246dd2da74342ce6a0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 15 Oct 2019 15:04:51 +0200 Subject: [PATCH 059/109] Fixes an error in #6538 by moving the colon character to the sr-only span --- .../src/views/components/application/umb-navigation.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index 7fcabda57b..fa343fbcb4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -10,9 +10,9 @@ @@ -25,9 +25,9 @@ ng-repeat="language in languages" > - Switch language to + Switch language to: - : {{language.name}} + {{language.name}}
From 178185e4112973e4431ab3ca6edb56f40a0a0e3d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 15 Oct 2019 15:11:42 +0200 Subject: [PATCH 060/109] V8: Move misplaced colon in language selector (#6692) --- .../views/components/application/umb-navigation.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index fa343fbcb4..fdd9b44a1e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -10,7 +10,12 @@ From 8a85c3cbf9230a01cce5563ac9da89226fa79c60 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 15 Oct 2019 17:57:06 +0200 Subject: [PATCH 061/109] V8: It should be possible to disallow all types at content root (#6580) --- .../src/views/content/content.create.controller.js | 9 +++++---- .../src/views/content/create.html | 10 ++++++++-- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 + src/Umbraco.Web/Editors/ContentController.cs | 6 ++---- src/Umbraco.Web/Editors/ContentTypeController.cs | 6 +----- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index 9940a7a218..64f601f8b7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -22,7 +22,8 @@ function contentCreateController($scope, function initialize() { $scope.loading = true; $scope.allowedTypes = null; - + $scope.countTypes = contentTypeResource.getCount; + var getAllowedTypes = contentTypeResource.getAllowedTypes($scope.currentNode.id).then(function (data) { $scope.allowedTypes = iconHelper.formatContentTypeIcons(data); }); @@ -97,7 +98,7 @@ function contentCreateController($scope, $scope.close = function() { close(); - } + }; $scope.closeDialog = function (showMenu) { navigationService.hideDialog(showMenu); @@ -106,12 +107,12 @@ function contentCreateController($scope, $scope.createContentType = function () { $location.path("/settings/documenttypes/edit/-1").search("create", "true"); close(); - } + }; $scope.editContentType = function () { $location.path("/settings/documenttypes/edit/" + $scope.contentTypeId).search("view", "permissions"); close(); - } + }; $scope.createBlank = createBlank; $scope.createOrSelectBlueprintIfAny = createOrSelectBlueprintIfAny; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index 0b6f23c23a..cd883daf5f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -19,10 +19,16 @@
-

+ +

+ +

+ + +

-
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 8804ec3870..8a421c6e70 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -337,6 +337,7 @@ The selected page in the content tree doesn't allow for any pages to be created below it. Edit permissions for this document type Create a new document type + Document Types within the Settings section, by changing the Allow as root option under Permissions.]]> Media Types Types within the Settings section, by editing the Allowed child node types under Permissions.]]> The selected media in the tree doesn't allow for any other media to be created below it. Edit permissions for this media type diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 8a759fb6cc..2834e68a0f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -342,6 +342,7 @@ The selected page in the content tree doesn't allow for any pages to be created below it. Edit permissions for this document type Create a new document type + Document Types within the Settings section, by changing the Allow as root option under Permissions.]]> Media Types Types within the Settings section, by editing the Allowed child node types under Permissions.]]> The selected media in the tree doesn't allow for any other media to be created below it. Edit permissions for this media type Document Type without a template diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 9d5af028e3..d19c066861 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1926,10 +1926,8 @@ namespace Umbraco.Web.Editors } if (model.ParentId < 0) { - //cannot move if the content item is not allowed at the root unless there are - //none allowed at root (in which case all should be allowed at root) - var contentTypeService = Services.ContentTypeService; - if (toMove.ContentType.AllowedAsRoot == false && contentTypeService.GetAll().Any(ct => ct.AllowedAsRoot)) + //cannot move if the content item is not allowed at the root + if (toMove.ContentType.AllowedAsRoot == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse( diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 7c63061159..0f51c35a14 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -414,11 +414,7 @@ namespace Umbraco.Web.Editors IEnumerable types; if (contentId == Constants.System.Root) { - var allContentTypes = Services.ContentTypeService.GetAll().ToList(); - bool AllowedAsRoot(IContentType x) => x.AllowedAsRoot; - types = allContentTypes.Any(AllowedAsRoot) - ? allContentTypes.Where(AllowedAsRoot).ToList() - : allContentTypes; + types = Services.ContentTypeService.GetAll().Where(x => x.AllowedAsRoot).ToList(); } else { From 426aa981f8d8758fd567c332e95325a21dbcb0e6 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 15 Oct 2019 18:13:18 +0200 Subject: [PATCH 062/109] Pick macro parameters in an infinite editor like content type properties (#6586) --- .../src/common/resources/macro.resource.js | 14 ++ .../datatypepicker.controller.js | 28 ++-- .../datatypepicker/datatypepicker.html | 97 +++++++------ .../macroparameterpicker.controller.js | 127 ++++++++++++++++++ .../macroparameterpicker.html | 106 +++++++++++++++ .../macropicker/macropicker.controller.js | 2 - .../macropicker/macropicker.html | 4 +- .../infiniteeditors/parameter.controller.js | 32 ++++- .../macros/infiniteeditors/parameter.html | 61 ++++++--- .../views/macros/macros.edit.controller.js | 17 --- .../views/macro.parameters.controller.js | 57 +++++--- .../src/views/macros/views/parameters.html | 55 ++++---- .../src/views/macros/views/settings.html | 2 +- src/Umbraco.Web/Editors/MacrosController.cs | 33 +++++ 14 files changed, 500 insertions(+), 135 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js index 73a1651b5e..c9ce7866e5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js @@ -105,6 +105,20 @@ function macroResource($q, $http, umbRequestHelper) { ); }, + getGroupedParameterEditors: function () { + return umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetGroupedParameterEditors"), + "Failed to get parameter editors") + ); + }, + + getParameterEditorByAlias: function(alias) { + return umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetParameterEditorByAlias", { "alias": alias }), + "Failed to get parameter editor") + ); + }, + getById: function(id) { return umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetById", { "id": id }), "Failed to get macro") 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 c86f55b255..167e74c25d 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, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) { + function DataTypePicker($scope, $filter, dataTypeResource, contentTypeResource, localizationService, editorService) { var vm = this; @@ -122,10 +122,18 @@ vm.showTabs = false; var regex = new RegExp(vm.searchTerm, "i"); + + var userConfigured = filterCollection(vm.userConfigured, regex), + typesAndEditors = filterCollection(vm.typesAndEditors, regex); + + var totalResults = _.reduce(_.pluck(_.union(userConfigured, typesAndEditors), 'count'), (m, n) => m + n, 0); + vm.filterResult = { - userConfigured: filterCollection(vm.userConfigured, regex), - typesAndEditors: filterCollection(vm.typesAndEditors, regex) + userConfigured: userConfigured, + typesAndEditors: typesAndEditors, + totalResults: totalResults }; + } else { vm.filterResult = null; vm.showTabs = true; @@ -134,11 +142,15 @@ function filterCollection(collection, regex) { return _.map(_.keys(collection), function (key) { + + var filteredDataTypes = $filter('filter')(collection[key], function (dataType) { + return regex.test(dataType.name) || regex.test(dataType.alias); + }); + return { group: key, - dataTypes: $filter('filter')(collection[key], function (dataType) { - return regex.test(dataType.name) || regex.test(dataType.alias); - }) + count: filteredDataTypes.length, + dataTypes: filteredDataTypes } }); } @@ -150,7 +162,6 @@ propertyDetails.title = property.name; $scope.model.itemDetails = propertyDetails; - } function hideDetailsOverlay() { @@ -177,7 +188,6 @@ }; editorService.open(dataTypeSettings); - } function pickDataType(selectedDataType) { @@ -205,7 +215,7 @@ } function close() { - if($scope.model.close) { + if ($scope.model.close) { $scope.model.close(); } } 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 fd859b9e2e..b99cd1ceef 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 @@ -20,17 +20,19 @@
+ +
+
-
-
-
-
{{result.group}}
- +
+
+
+
+
{{result.group}}
+ +
-
-
-
-
{{result.group}}
- +
+
+
+
+
{{result.group}}
+ +
+ + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.controller.js new file mode 100644 index 0000000000..5b81cb947d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.controller.js @@ -0,0 +1,127 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.MacroParameterPickerController + * @function + * + * @description + * The controller for the content type editor macro parameter dialog + */ + +(function() { + "use strict"; + + function MacroParameterController($scope, $filter, macroResource, localizationService, editorService) { + + var vm = this; + + vm.searchTerm = ""; + vm.parameterEditors = []; + vm.loading = false; + vm.labels = {}; + + vm.filterItems = filterItems; + vm.showDetailsOverlay = showDetailsOverlay; + vm.hideDetailsOverlay = hideDetailsOverlay; + vm.pickParameterEditor = pickParameterEditor; + vm.close = close; + + function init() { + setTitle(); + getGroupedParameterEditors(); + } + + function setTitle() { + if (!$scope.model.title) { + localizationService.localize("defaultdialogs_selectEditor") + .then(function(data){ + $scope.model.title = data; + }); + } + } + + function getGroupedParameterEditors() { + + vm.loading = true; + + macroResource.getGroupedParameterEditors().then(function (data) { + vm.parameterEditors = data; + vm.loading = false; + }, function () { + vm.loading = false; + }); + + } + + function filterItems() { + // clear item details + $scope.model.itemDetails = null; + + if (vm.searchTerm) { + + var regex = new RegExp(vm.searchTerm, "i"); + + var parameterEditors = filterCollection(vm.parameterEditors, regex); + + var totalResults = _.reduce(_.pluck(parameterEditors, 'count'), (m, n) => m + n, 0); + + vm.filterResult = { + parameterEditors: parameterEditors, + totalResults: totalResults + }; + } else { + vm.filterResult = null; + } + } + + function filterCollection(collection, regex) { + return _.map(_.keys(collection), function (key) { + + var filteredEditors = $filter('filter')(collection[key], function (editor) { + return regex.test(editor.name) || regex.test(editor.alias); + }); + + return { + group: key, + count: filteredEditors.length, + parameterEditors: filteredEditors + } + }); + } + + function showDetailsOverlay(property) { + + var propertyDetails = {}; + propertyDetails.icon = property.icon; + propertyDetails.title = property.name; + + $scope.model.itemDetails = propertyDetails; + } + + function hideDetailsOverlay() { + $scope.model.itemDetails = null; + } + + function pickParameterEditor(selectedParameterEditor) { + + console.log("pickParameterEditor", selectedParameterEditor); + console.log("$scope.model", $scope.model); + + $scope.model.parameter.editor = selectedParameterEditor.alias; + $scope.model.parameter.dataTypeName = selectedParameterEditor.name; + $scope.model.parameter.dataTypeIcon = selectedParameterEditor.icon; + + $scope.model.submit($scope.model); + } + + function close() { + if ($scope.model.close) { + $scope.model.close(); + } + } + + init(); + } + + angular.module("umbraco").controller("Umbraco.Editors.MacroParameterPickerController", MacroParameterController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html new file mode 100644 index 0000000000..9f2b56401d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html @@ -0,0 +1,106 @@ +
+ + + +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ + +
+
+
+
+
{{result.group}}
+ +
+
+
+ + + + +
+ +
+
+
+ + + + + + + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js index aa63f2d6d6..dfc77f786c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js @@ -41,8 +41,6 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi macroResource.getMacroParameters($scope.model.selectedMacro.id) .then(function (data) { - - //go to next page if there are params otherwise we can just exit if (!angular.isArray(data) || data.length === 0) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html index 66c64657a9..fc1bec4ec1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html @@ -32,10 +32,10 @@
  • - + - {{ availableItem.name }} + {{availableItem.name}}
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.controller.js index 7dddbb42d1..7d4df17277 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.controller.js @@ -1,14 +1,43 @@ (function() { "use strict"; - function ParameterEditorController($scope, formHelper) { + function ParameterEditorController($scope, formHelper, editorService) { const vm = this; vm.submit = submit; vm.close = close; + vm.openMacroParameterPicker = openMacroParameterPicker; + + function openMacroParameterPicker(parameter) { + + vm.focusOnMandatoryField = false; + + var overlay = { + parameter: $scope.model.parameter, + view: "views/common/infiniteeditors/macroparameterpicker/macroparameterpicker.html", + size: "small", + submit: function (model) { + + vm.focusOnMandatoryField = true; + + // update property + parameter.editor = model.parameter.editor; + + editorService.close(); + }, + close: function (model) { + editorService.close(); + } + }; + + editorService.open(overlay); + } + function submit() { + console.log("model", $scope.model); + if ($scope.model && $scope.model.submit && formHelper.submitForm({scope: $scope})) { $scope.model.submit($scope.model); } @@ -19,7 +48,6 @@ $scope.model.close(); } } - } angular.module("umbraco").controller("Umbraco.Editors.ParameterEditorController", ParameterEditorController); diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html index ae46bf6057..c1f83cd0b7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/infiniteeditors/parameter.html @@ -1,34 +1,62 @@ 
    -
    + - + - + - - - +
    +
    + +
    + Required label +
    +
    +
    + +
    +
    - - - + + + - - - +
    @@ -40,6 +68,7 @@ { + openOverlay({}, vm.labels.addParameter, (newParameter) => { if (!$scope.model.macro.parameters) { $scope.model.macro.parameters = []; } @@ -40,23 +48,43 @@ function MacrosParametersController($scope, editorService, localizationService) }); } - $scope.edit = function (parameter, evt) { + function edit(parameter, evt) { evt.preventDefault(); - openOverlay(parameter, $scope.labels.editParameter, (newParameter) => { - parameter.key = newParameter.key; - parameter.label = newParameter.label; - parameter.editor = newParameter.editor; - setDirty(); + var promises = [ + getParameterEditorByAlias(parameter.editor) + ]; + + $q.all(promises).then(function (values) { + parameter.dataTypeName = values[0].name; + + openOverlay(parameter, vm.labels.editParameter, (newParameter) => { + + parameter.key = newParameter.key; + parameter.label = newParameter.label; + parameter.editor = newParameter.editor; + setDirty(); + }); }); } + function getParameterEditorByAlias(alias) { + var deferred = $q.defer(); + + macroResource.getParameterEditorByAlias(alias).then(function (data) { + deferred.resolve(data); + }, function () { + deferred.reject(); + }); + + return deferred.promise; + } + function openOverlay(parameter, title, onSubmit) { const ruleDialog = { title: title, parameter: _.clone(parameter), - editors : $scope.model.parameterEditors, view: "views/macros/infiniteeditors/parameter.html", size: "small", submit: function (model) { @@ -69,7 +97,6 @@ function MacrosParametersController($scope, editorService, localizationService) }; editorService.open(ruleDialog); - } function setDirty() { @@ -78,10 +105,8 @@ function MacrosParametersController($scope, editorService, localizationService) function init() { localizationService.localizeMany(["macro_addParameter", "macro_editParameter"]).then(function (data) { - $scope.labels = { - addParameter: data[0], - editParameter: data[1] - } + vm.labels.addParameter = data[0]; + vm.labels.editParameter = data[1]; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html index 0aa4a47fc0..6ea79fe16d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html @@ -1,33 +1,34 @@ - - -
    -
    -
    Parameters
    - Define the parameters that should be available when using this macro. -
    -
    -
    -
    -
    - -
    - {{parameter.label}} ({{parameter.key}}){{parameter.editor}} -
    -
    - Edit - Remove +
    + + +
    +
    +
    Parameters
    + Define the parameters that should be available when using this macro. +
    +
    +
    +
    +
    + +
    + {{parameter.label}} ({{parameter.key}}){{parameter.editor}} +
    +
    + Edit + Remove +
    + +
    - -
    -
    - - + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html index 60aeae0d92..d34cf1810d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html @@ -1,4 +1,4 @@ -
    +
    diff --git a/src/Umbraco.Web/Editors/MacrosController.cs b/src/Umbraco.Web/Editors/MacrosController.cs index 429e8b6190..3cb161e547 100644 --- a/src/Umbraco.Web/Editors/MacrosController.cs +++ b/src/Umbraco.Web/Editors/MacrosController.cs @@ -229,6 +229,39 @@ namespace Umbraco.Web.Editors return this.Request.CreateResponse(HttpStatusCode.OK, Current.ParameterEditors); } + /// + /// Gets the available parameter editors grouped by their group. + /// + /// + /// The . + /// + public HttpResponseMessage GetGroupedParameterEditors() + { + var parameterEditors = Current.ParameterEditors.ToArray(); + + var grouped = parameterEditors + .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group.ToLower()) + .OrderBy(x => x.Key) + .ToDictionary(group => group.Key, group => group.OrderBy(d => d.Name).AsEnumerable()); + + return this.Request.CreateResponse(HttpStatusCode.OK, grouped); + } + + /// + /// Get parameter editor by alias. + /// + /// + /// The . + /// + public HttpResponseMessage GetParameterEditorByAlias(string alias) + { + var parameterEditors = Current.ParameterEditors.ToArray(); + + var parameterEditor = parameterEditors.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); + + return this.Request.CreateResponse(HttpStatusCode.OK, parameterEditor); + } + /// /// Returns a error response and optionally logs it /// From 8fa3ebdd04526d0eb8b26c36c8dc1cbd6eac4c57 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 6 Oct 2019 09:32:18 +0200 Subject: [PATCH 063/109] Reload node children after publishing with descendants --- .../directives/components/content/edit.controller.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 2a2852c24f..49d455d9d6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -4,7 +4,7 @@ function ContentEditController($rootScope, $scope, $routeParams, $q, $window, appState, contentResource, entityResource, navigationService, notificationsService, serverValidationManager, contentEditingHelper, localizationService, formHelper, umbRequestHelper, - editorState, $http, eventsService, overlayService, $location, localStorageService) { + editorState, $http, eventsService, overlayService, $location, localStorageService, treeService) { var evts = []; var infiniteMode = $scope.infiniteModel && $scope.infiniteModel.infiniteMode; @@ -305,7 +305,7 @@ } /** Syncs the content item to it's tree node - this occurs on first load and after saving */ - function syncTreeNode(content, path, initialLoad) { + function syncTreeNode(content, path, initialLoad, reloadChildren) { if (infiniteMode || !path) { return; @@ -315,6 +315,9 @@ navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }) .then(function (syncArgs) { $scope.page.menu.currentNode = syncArgs.node; + if (reloadChildren && syncArgs.node.expanded) { + treeService.loadNodeChildren({node: syncArgs.node}); + } }, function () { //handle the rejection console.log("A problem occurred syncing the tree! A path is probably incorrect.") @@ -446,7 +449,7 @@ //needs to be manually set for infinite editing mode $scope.page.isNew = false; - syncTreeNode($scope.content, data.path); + syncTreeNode($scope.content, data.path, false, args.reloadChildren); eventsService.emit("content.saved", { content: $scope.content, action: args.action }); @@ -851,7 +854,8 @@ return contentResource.publishWithDescendants(content, create, model.includeUnpublished, files, showNotifications); }, action: "publishDescendants", - showNotifications: false + showNotifications: false, + reloadChildren: model.includeUnpublished }).then(function (data) { //show all notifications manually here since we disabled showing them automatically in the save method formHelper.showNotifications(data); From b8425fa662a688cba083ea5e3e3876af20426483 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Tue, 15 Oct 2019 17:30:12 +0100 Subject: [PATCH 064/109] Correct URL decoding of macro partial view names (#6592) --- .../src/views/macros/views/macro.settings.controller.js | 2 +- .../src/views/packages/edit.controller.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js index c8a08d57ca..5f633a6e4b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js @@ -35,7 +35,7 @@ function MacrosSettingsController($scope, editorService, localizationService) { }, filterCssClass: "not-allowed", select: function (node) { - const id = unescape(node.id); + const id = decodeURIComponent(node.id.replace(/\+/g, " ")); //vm.macro.view = id; $scope.model.macro.view = "~/Views/MacroPartials/" + id; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index d6bfb38b88..026b2260e2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -243,7 +243,7 @@ select: function(node) { node.selected = !node.selected; - const id = unescape(node.id); + const id = decodeURIComponent(node.id.replace(/\+/g, " ")); const index = selection.indexOf(id); if(node.selected) { @@ -284,7 +284,7 @@ }, filterCssClass: "not-allowed", select: function(node) { - const id = unescape(node.id); + const id = decodeURIComponent(node.id.replace(/\+/g, " ")); vm.package.packageView = id; editorService.close(); }, From 9c601ab631a4747a8b3dd55c050ef670de2aa9b5 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 15 Oct 2019 18:42:52 +0200 Subject: [PATCH 065/109] Fix semantics for list views (#6595) --- .../src/less/components/umb-table.less | 28 +++++++++++-------- .../views/components/umb-content-grid.html | 4 +-- .../src/views/components/umb-table.html | 28 +++++++++++-------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index c2d019a64a..ec2654cb7b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -27,14 +27,13 @@ } +.umb-table__action, .umb-table a { + background: transparent; + border: 0 none; text-decoration: none; - cursor: pointer; - - &:focus { - outline: none; - text-decoration: none; - } + padding: 0; + margin-left: 1px; } input.umb-table__input { @@ -50,11 +49,16 @@ input.umb-table__input { font-weight: bold; } -a.umb-table-head__link { +.umb-table-head__link { + background: transparent; + border: 0 none; position: relative; - cursor: default; text-decoration: none; color: @gray-3; + font-size: inherit; + font-weight: inherit; + padding: 0 1px; + &:hover { text-decoration: none; cursor: default; @@ -62,10 +66,10 @@ a.umb-table-head__link { } } -a.umb-table-head__link.sortable { +.umb-table-head__link.sortable { + cursor: pointer; &:hover { text-decoration: none; - cursor: pointer; color: @black; } } @@ -137,15 +141,15 @@ a.umb-table-head__link.sortable { } .umb-table-body__link { - color: @ui-option-type; font-size: 14px; font-weight: bold; text-decoration: none; - + &:hover, &:focus { color: @ui-option-type-hover; text-decoration: underline; + outline: none; } } 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 ce85537d7c..93fa590f68 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 @@ -6,15 +6,13 @@ ng-class="{'-selected': item.selected}" ng-click="clickItem(item, $event, $index)"> - -
    - + {{ item.name }} 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 79c9a8bbe9..df817c1657 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 @@ -5,29 +5,33 @@
    Status
    @@ -39,8 +43,8 @@ ng-click="vm.selectItem(item, $index, $event)">
    - - + +
    Date: Wed, 16 Oct 2019 12:38:42 +1100 Subject: [PATCH 066/109] fixes test --- src/Umbraco.Tests/Services/MemberServiceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index 340ada2519..57d6b67af8 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -96,8 +96,7 @@ namespace Umbraco.Tests.Services var properties = pmember.Properties.ToList(); - for (var i = 0; i < aliases.Length; i++) - Assert.AreEqual(properties[i].Alias, aliases[i]); + Assert.IsTrue(properties.Select(x => x.Alias).ContainsAll(aliases)); var email = properties[aliases.IndexOf("Email")]; Assert.AreEqual("xemail", email.GetSourceValue()); From 0c5b8326271f88b1e9c40734b58ec7eb6aff7e65 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 14:21:03 +1100 Subject: [PATCH 067/109] fixing tests --- src/Umbraco.Tests/Composing/CompositionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index f4478e2add..33855a8bfb 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Tests.Composing @@ -35,7 +36,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeLoader = new TypeLoader(Mock.Of(), "", logger); + var typeLoader = new TypeLoader(Mock.Of(), IOHelper.MapPath("~/App_Data/TEMP"), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of()); // create the factory, ensure it is the mocked factory From 6d38f946a47574f35d32cd115dd643a9077fe825 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 15:00:13 +1100 Subject: [PATCH 068/109] ensures nucache table isn't rebuilt when adding a new lang --- .../PublishedCache/NuCache/PublishedSnapshotService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 0c531d8cf8..e76b526492 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1271,7 +1271,8 @@ namespace Umbraco.Web.PublishedCache.NuCache /// private void OnLanguageSaved(ILocalizationService sender, Core.Events.SaveEventArgs e) { - var cultureChanged = e.SavedEntities.Any(x => x.WasPropertyDirty(nameof(ILanguage.IsoCode))); + //culture changed on an existing language + var cultureChanged = e.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode))); if(cultureChanged) { RebuildContentDbCache(); From 4e51a2d18428bff885b887ec73eeebe1d356b18b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 15:49:19 +1100 Subject: [PATCH 069/109] Fixes tests - Nucache tests were not disposing the snapshot service so was binding to all events!! renames the test snapshot service to avoid confusion --- ...dSnapshotService.cs => XmlPublishedSnapshotService.cs} | 6 +++--- src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs | 2 +- .../PublishedContent/NuCacheChildrenTests.cs | 6 ++++++ src/Umbraco.Tests/PublishedContent/NuCacheTests.cs | 7 +++++++ src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs | 8 ++++---- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs | 4 ++-- 8 files changed, 25 insertions(+), 12 deletions(-) rename src/Umbraco.Tests/LegacyXmlPublishedCache/{PublishedSnapshotService.cs => XmlPublishedSnapshotService.cs} (97%) diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs similarity index 97% rename from src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs rename to src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs index 394a33d777..ec6b854a46 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// /// Implements a published snapshot service. /// - internal class PublishedSnapshotService : PublishedSnapshotServiceBase + internal class XmlPublishedSnapshotService : PublishedSnapshotServiceBase { private readonly XmlStore _xmlStore; private readonly RoutesCache _routesCache; @@ -41,7 +41,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Constructors // used in WebBootManager + tests - public PublishedSnapshotService(ServiceContext serviceContext, + public XmlPublishedSnapshotService(ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, IAppCache requestCache, @@ -65,7 +65,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } // used in some tests - internal PublishedSnapshotService(ServiceContext serviceContext, + internal XmlPublishedSnapshotService(ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, IAppCache requestCache, diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index 3b675c2f07..447104b7cd 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// Represents the Xml storage for the Xml published cache. /// /// - /// One instance of is instantiated by the and + /// One instance of is instantiated by the and /// then passed to all instances that are created (one per request). /// This class should *not* be public. /// diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index ac3ac7d455..b0864ffc5c 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -41,6 +41,12 @@ namespace Umbraco.Tests.PublishedContent private ContentType _contentTypeVariant; private TestDataSource _source; + [TearDown] + public void Teardown() + { + _snapshotService?.Dispose(); + } + private void Init(IEnumerable kits) { Current.Reset(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 44df5c20cb..0e05e6baad 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -36,6 +36,12 @@ namespace Umbraco.Tests.PublishedContent private ContentType _contentType; private PropertyType _propertyType; + [TearDown] + public void Teardown() + { + _snapshotService?.Dispose(); + } + private void Init() { Current.Reset(); @@ -303,5 +309,6 @@ namespace Umbraco.Tests.PublishedContent Assert.IsFalse(c2.IsPublished("dk-DA")); Assert.IsTrue(c2.IsPublished("de-DE")); } + } } diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 34482a79fa..91a6934e18 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Scoping // xmlStore.Xml - the actual main xml document // publishedContentCache.GetXml() - the captured xml - private static XmlStore XmlStore => (Current.Factory.GetInstance() as PublishedSnapshotService).XmlStore; + private static XmlStore XmlStore => (Current.Factory.GetInstance() as XmlPublishedSnapshotService).XmlStore; private static XmlDocument XmlMaster => XmlStore.Xml; private static XmlDocument XmlInContext => ((PublishedContentCache) Umbraco.Web.Composing.Current.UmbracoContext.Content).GetXml(false); diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 9cc2b97445..050de4fcb9 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -259,7 +259,7 @@ namespace Umbraco.Tests.TestHelpers var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); var variationContextAccessor = new TestVariationContextAccessor(); - var service = new PublishedSnapshotService( + var service = new XmlPublishedSnapshotService( ServiceContext, Factory.GetInstance(), ScopeProvider, @@ -357,14 +357,14 @@ namespace Umbraco.Tests.TestHelpers protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IEnumerable mediaUrlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) { // ensure we have a PublishedCachesService - var service = snapshotService ?? PublishedSnapshotService as PublishedSnapshotService; + var service = snapshotService ?? PublishedSnapshotService as XmlPublishedSnapshotService; if (service == null) throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService."); - if (service is PublishedSnapshotService) + if (service is XmlPublishedSnapshotService) { // re-initialize PublishedCacheService content with an Xml source with proper template id - ((PublishedSnapshotService)service).XmlStore.GetXmlDocument = () => + ((XmlPublishedSnapshotService)service).XmlStore.GetXmlDocument = () => { var doc = new XmlDocument(); doc.LoadXml(GetXmlContent(templateId)); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index fcf73fbffa..39826fcc38 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -511,7 +511,7 @@ - + diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 5c291c9601..9ca17675af 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -29,7 +29,7 @@ namespace Umbraco.Tests.Web.Mvc [UmbracoTest(WithApplication = true)] public class UmbracoViewPageTests : UmbracoTestBase { - private PublishedSnapshotService _service; + private XmlPublishedSnapshotService _service; [TearDown] public override void TearDown() @@ -421,7 +421,7 @@ namespace Umbraco.Tests.Web.Mvc var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var factory = Mock.Of(); var umbracoContextAccessor = Mock.Of(); - _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, + _service = new XmlPublishedSnapshotService(svcCtx, factory, scopeProvider, cache, null, null, umbracoContextAccessor, null, null, null, new TestDefaultCultureAccessor(), From 5ca32ba1e9376002d14c735d9825a687acab3720 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 17:29:45 +1100 Subject: [PATCH 070/109] starts writing tests --- .../PublishedContent/NuCacheChildrenTests.cs | 108 ++++++++++++++++-- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index ac3ac7d455..d67d69d46e 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -213,22 +213,24 @@ namespace Umbraco.Tests.PublishedContent var level = path.Count(x => x == ','); var now = DateTime.Now; + var contentData = new ContentData + { + Name = "N" + id, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + }; + return new ContentNodeKit { ContentTypeId = _contentTypeInvariant.Id, Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), DraftData = null, - PublishedData = new ContentData - { - Name = "N" + id, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } + PublishedData = contentData }; } @@ -333,7 +335,7 @@ namespace Umbraco.Tests.PublishedContent CultureInfos = GetCultureInfos(id, now) }; - var withDraft = id%2==0; + var withDraft = id % 2 == 0; var withPublished = !withDraft; return new ContentNodeKit @@ -1063,6 +1065,88 @@ namespace Umbraco.Tests.PublishedContent AssertLinkedNode(child3.contentNode, 1, 2, -1, -1, -1); } + /// + /// This addresses issue: https://github.com/umbraco/Umbraco-CMS/issues/6698 + /// + /// + /// This test mimics if someone were to: + /// 1) Unpublish a "middle child" + /// 2) Save and publish it + /// 3) Publish it with descendants + /// 4) Repeat steps 2 and 3 + /// + /// Which has caused an exception. To replicate this test: + /// 1) RefreshBranch with kits for a branch where the top most node is unpublished + /// 2) RefreshBranch with kits for the branch where the top most node is published + /// 3) RefreshBranch with kits for the branch where the top most node is published + /// 4) RefreshBranch with kits for the branch where the top most node is published + /// 5) RefreshBranch with kits for the branch where the top most node is published + /// + [Test] + public void Refresh_Branch_With_Alternating_Publish_Flags() + { + // NOTE: these tests are not using real scopes, in which case a Scope does not control + // how the snapshots generations work. We are forcing new snapshot generations manually. + + IEnumerable GetKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + //root + yield return CreateInvariantKit(1, -1, 1, paths); + + //children + yield return CreateInvariantKit(2, 1, 1, paths); + yield return CreateInvariantKit(3, 1, 2, paths); //middle child + yield return CreateInvariantKit(4, 1, 3, paths); + } + + //init with all published + Init(GetKits()); + + var snapshotService = (PublishedSnapshotService)_snapshotService; + var contentStore = snapshotService.GetContentStore(); + + var rootKit = _source.Kits[1].Clone(); + + void ChangePublishFlagOfRoot(bool published, int assertGen) + { + //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) + contentStore.CreateSnapshot(); + + Assert.IsFalse(contentStore.Test.NextGen); + + //Change the root publish flag + var kit = rootKit.Clone(); + kit.DraftData = published ? null : kit.PublishedData; + kit.PublishedData = published? kit.PublishedData : null; + _source.Kits[1] = kit; + + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) + }, out _, out _); + + Assert.AreEqual(assertGen, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + } + + //unpublish the root + ChangePublishFlagOfRoot(false, 2); + + //publish the root + ChangePublishFlagOfRoot(true, 3); + + //publish root + descendants + ChangePublishFlagOfRoot(true, 4); + + //publish the root + ChangePublishFlagOfRoot(true, 5); + + //publish root + descendants + ChangePublishFlagOfRoot(true, 6); //TODO: This should fail, need to figure out what the diff is between this and a website + } + [Test] public void Refresh_Branch_Ensures_Linked_List() { From 8e73198979148c73bc8a05d8a97dbb26f6ea1d3d Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Wed, 16 Oct 2019 12:09:19 +0200 Subject: [PATCH 071/109] =?UTF-8?q?Hide=20icons=20and=20use=20buttons=20in?= =?UTF-8?q?stead=20of=20empty=20anchors=20in=20Examine=E2=80=A6=20(#6596)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/less/tables.less | 12 ++++++++++ .../dashboard/settings/examinemanagement.html | 24 +++++++------------ src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/tables.less b/src/Umbraco.Web.UI.Client/src/less/tables.less index cd6304ef49..65b3eacdcd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tables.less +++ b/src/Umbraco.Web.UI.Client/src/less/tables.less @@ -263,3 +263,15 @@ table th[class*="span"], .table-sortable tbody tr { cursor: move; } + +.table__action-overlay{ + background: transparent; + border: 0 none; + padding: 0; + font-style: italic; + + &:focus, + &:hover{ + text-decoration: underline; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index bb11addf08..31a37796f0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -31,15 +31,15 @@
    -
    @@ -67,14 +67,14 @@ @@ -159,10 +159,7 @@ {{result.values['nodeName']}} {{result.values['nodeName']}} -   - - ({{result.fieldCount}} fields) - + @@ -227,7 +224,7 @@
    -
    @@ -302,10 +299,7 @@ {{result.values['nodeName']}} {{result.values['nodeName']}} -   - - ({{result.fieldCount}} fields) - + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index e2a9b02086..9968af4d26 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -512,6 +512,7 @@ Søg i indekset og se resultaterne Værktøjer Værktøjer til at administrere indekset + felter Indtast dit brugernavn diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 8a421c6e70..0e87eb7898 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -514,6 +514,7 @@ Search the index and view the results Tools Tools to manage the index + fields Enter your username diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 2834e68a0f..a867aff69b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -517,6 +517,7 @@ Search the index and view the results Tools Tools to manage the index + fields Enter your username From 82b8302f256f8d63a233d69a874cda4590275f2d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 7 Oct 2019 22:07:29 +0200 Subject: [PATCH 072/109] Turn the "empty recycle bin" warning into an actual warning --- .../src/views/content/emptyrecyclebin.html | 8 ++++---- .../src/views/media/emptyrecyclebin.html | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html b/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html index 9ac7ef10fc..093d2627cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html @@ -4,12 +4,12 @@ -

    - When items are deleted from the recycle bin, they will be gone forever. +

    +

    When items are deleted from the recycle bin, they will be gone forever.

    Are you sure? -

    +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html b/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html index 33681f9269..471880cfe8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html @@ -5,12 +5,12 @@ -

    - When items are deleted from the recycle bin, they will be gone forever. +

    +

    When items are deleted from the recycle bin, they will be gone forever.

    Are you sure? -

    +
    - + From 6c6b23c465c2f95eb4cfd489dbfe280612b1f63d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 16 Oct 2019 15:28:03 +0200 Subject: [PATCH 073/109] V8: Add irreversible warning when deleting items from the recycle bin (#6600) --- src/Umbraco.Web.UI.Client/src/views/content/delete.html | 6 +++++- src/Umbraco.Web.UI.Client/src/views/media/delete.html | 6 +++++- src/Umbraco.Web/Trees/TreeControllerBase.cs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/delete.html b/src/Umbraco.Web.UI.Client/src/views/content/delete.html index 721139c6e9..9a88428173 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/delete.html @@ -14,10 +14,14 @@ Are you sure you want to delete {{currentNode.name}}?

    -
    +
    This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead.
    +
    + When items are deleted from the recycle bin, they will be gone forever. +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/media/delete.html b/src/Umbraco.Web.UI.Client/src/views/media/delete.html index d4f16ce71c..7231ccf2c4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/delete.html @@ -14,7 +14,11 @@ Are you sure you want to delete {{currentNode.name}}?

    - +
    + When items are deleted from the recycle bin, they will be gone forever. +
    + +
    diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 0f9b61469e..77e5ebef62 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -265,6 +265,7 @@ namespace Umbraco.Web.Trees treeNode.Path = entity.Path; treeNode.Udi = Udi.Create(ObjectTypes.GetUdiType(entityObjectType), entity.Key); treeNode.HasChildren = hasChildren; + treeNode.Trashed = entity.Trashed; return treeNode; } From 0b395482f1770d7946868f475a6187ec001eea2a Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Wed, 16 Oct 2019 16:00:59 +0200 Subject: [PATCH 074/109] Use umb-radiobutton in public access overlay (#6612) --- .../forms/umbradiobutton.directive.js | 2 + src/Umbraco.Web.UI.Client/src/less/hacks.less | 3 +- .../components/forms/umb-radiobutton.html | 15 ++++--- .../src/views/content/protect.html | 44 +++++++++---------- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js index df45181991..bb36e3b027 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js @@ -22,6 +22,7 @@ @param {boolean} model Set to true or false to set the radiobutton to checked or unchecked. +@param {string} inputId Set the id of the radiobutton. @param {string} value Set the value of the radiobutton. @param {string} name Set the name of the radiobutton. @param {string} text Set the text for the radiobutton label. @@ -55,6 +56,7 @@ controllerAs: 'vm', bindings: { model: "=", + inputId: "@", value: "@", name: "@", text: "@", diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index 0b6d1b7a60..3ead4d6905 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -111,11 +111,10 @@ iframe, .content-column-body { } .pa-select-type label { - padding: 0 20px; + padding: 0 15px; } .pa-access-header { - font-weight: bold; margin: 0 0 3px 0; padding-bottom: 0; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html index f3daa652d6..9ee50bcae1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html @@ -1,12 +1,13 @@

    Select the members that should have access to this page

    - +
    Add - +
    @@ -61,7 +59,7 @@

    Select the groups that should have access to this page

    - +
    Add - +

    Select the pages that contain login form and error messages

    - +
    - +
    @@ -119,8 +117,6 @@

    Are you sure you want to remove the protection from this page?

    - - From 008505354b9a04ab6ea51c5cb9b232f159148644 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 16 Oct 2019 16:15:29 +0200 Subject: [PATCH 075/109] =?UTF-8?q?V8:=20Do=20not=20warn=20about=20unpubli?= =?UTF-8?q?shing=20multiple=20languages=20for=20invar=E2=80=A6=20(#6613)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/content/content.delete.controller.js | 14 -------------- .../src/views/content/delete.html | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 60a04732f0..a0eb80b2dd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -80,20 +80,6 @@ function ContentDeleteController($scope, $timeout, contentResource, treeService, $scope.close = function () { navigationService.hideDialog(); }; - - - languageResource.getAll().then(function (data) { - - $scope.hasMoreThanOneLanguage = data.length > 1; - }, function (err) { - toggleDeleting(false); - - //check if response is ysod - if (err.status && err.status >= 500) { - // TODO: All YSOD handling should be done with an interceptor - overlayService.ysod(err); - } - }); } angular.module("umbraco").controller("Umbraco.Editors.Content.DeleteController", ContentDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/delete.html b/src/Umbraco.Web.UI.Client/src/views/content/delete.html index 9a88428173..316d0669c4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/delete.html @@ -14,7 +14,7 @@ Are you sure you want to delete {{currentNode.name}}?

    -
    +
    This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead.
    diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 0e87eb7898..11ce7a716a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -477,7 +477,7 @@ account Select editor Select snippet - This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead. + This will delete the node and all its languages. If you only want to delete one language, you should unpublish the node in that language instead. There are no dictionary items. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index a867aff69b..f5c3127d8e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -480,7 +480,7 @@ account Select editor Select snippet - This will delete the node and all its languages. If you only want to delete one language go and unpublish it instead. + This will delete the node and all its languages. If you only want to delete one language, you should unpublish the node in that language instead. There are no dictionary items. From 77f2b28aed884818206fb4963b98d6cca9f23df2 Mon Sep 17 00:00:00 2001 From: Paul Seal Date: Wed, 16 Oct 2019 15:18:06 +0100 Subject: [PATCH 076/109] - set the border for the today class on the date picker (#6619) * - set the border for the today class on the date picker * - added the actual styling for the today border --- .../src/less/components/umb-date-time-picker.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less index 2df0cc5fd8..5bcfdd1c71 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less @@ -6,6 +6,10 @@ span.flatpickr-day { border-radius: @baseBorderRadius; border: none; + + &.today:not(.active) { + border: 1px solid; + } } span.flatpickr-day:hover { From 9c7d3d8c2a642f20c4f48a070d24bd9461e8e0e5 Mon Sep 17 00:00:00 2001 From: Krnlsoft Date: Wed, 9 Oct 2019 22:40:30 +0200 Subject: [PATCH 077/109] Missing Italian translations Added missing Italian translations Removed TODO --- src/Umbraco.Web.UI/Umbraco/config/lang/it.xml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml index 5c82e061b0..a7735fba79 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml @@ -323,8 +323,7 @@ Mostra la pagina inviata Dimensione Ordina - Submit - + Invia Tipo Su @@ -339,8 +338,8 @@ Benvenuto... Larghezza Si - Reorder - I am done reordering + Riordina + Ho finito di ordinare/key> Colore di sfondo @@ -618,8 +617,8 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Tabs - Sort order - Creation date + Ordinamento + Data creazione @@ -853,6 +852,6 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i - Writer + Autore From 86c6b3d7f8386648e5530606f5ff9959048228c7 Mon Sep 17 00:00:00 2001 From: Kewbish Date: Thu, 10 Oct 2019 09:34:18 -0700 Subject: [PATCH 078/109] Grammar changes There were a few grammatical mistakes and one spelling error in the various .md files, so I fixed them :) Signed-off-by: Kewbish --- .github/BUILD.md | 4 ++-- .github/CODE_OF_CONDUCT.md | 2 +- .github/CONTRIBUTING.md | 2 +- .github/CONTRIBUTION_GUIDELINES.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/BUILD.md b/.github/BUILD.md index c89a1be460..a9c26f3a9b 100644 --- a/.github/BUILD.md +++ b/.github/BUILD.md @@ -1,4 +1,4 @@ -# Umbraco Cms Build +# Umbraco CMS Build ## Are you sure? @@ -66,7 +66,7 @@ The Visual Studio object is `null` when Visual Studio has not been detected (eg * `Path`: Visual Studio installation path (eg some place under `Program Files`) * `Major`: Visual Studio major version (eg `15` for VS 2017) * `Minor`: Visual Studio minor version -* `MsBUild`: the absolute path to the MsBuild executable +* `MsBuild`: the absolute path to the MsBuild executable #### GetUmbracoVersion diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 0e79851c0b..1526c54656 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -29,4 +29,4 @@ Don't rest on your laurels and never accept the status quo. Contribute and give ## Friendly -Don’t judge upon mistakes made but rather upon the speed and quality with which mistakes are corrected. Friendly posts and contributions generate smiles and builds long lasting relationships. \ No newline at end of file +Don’t judge upon mistakes made but rather upon the speed and quality with which mistakes are corrected. Friendly posts and contributions generate smiles and build long lasting relationships. \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 84115b946a..4f5402c512 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -38,7 +38,7 @@ This document gives you a quick overview on how to get started. ### Guidelines for contributions we welcome -Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time. +Not all changes are wanted, so on occasion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time. We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes. diff --git a/.github/CONTRIBUTION_GUIDELINES.md b/.github/CONTRIBUTION_GUIDELINES.md index 7d2afb46bf..0ac35e6897 100644 --- a/.github/CONTRIBUTION_GUIDELINES.md +++ b/.github/CONTRIBUTION_GUIDELINES.md @@ -13,7 +13,7 @@ We’re usually able to handle small PRs pretty quickly. A community volunteer w Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. We encourage anyone to pick them up and help out. -If you do start working on something, make sure leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has. +If you do start working on something, make sure to leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has. ## Large PRs New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGet’s packages.config, NPM’s packages.json, etc.). @@ -30,6 +30,6 @@ It is highly recommended that you speak to the HQ before making large, complex c ### Pull request or package? -If it doesn’t fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and to fix bugs. +If it doesn’t fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and fix bugs. Eventually, a package could "graduate" to be included in the CMS. From fe5996aaf76a2326fffe8567765d9f11951b5157 Mon Sep 17 00:00:00 2001 From: Volken Date: Thu, 10 Oct 2019 00:15:17 +0200 Subject: [PATCH 079/109] Update ModelStateExtensions.cs Line 76 There is a duplicated word "errors" change this line /// Returns a list of cultures that have property validation errors errors To /// Returns a list of cultures that have property validation errors Or if i missunderstood what you mean change with that for a better understanding. /// Returns a list of cultures that have property validation errors "errors" --- src/Umbraco.Web/ModelStateExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/ModelStateExtensions.cs b/src/Umbraco.Web/ModelStateExtensions.cs index 706ebf2825..00b17781e0 100644 --- a/src/Umbraco.Web/ModelStateExtensions.cs +++ b/src/Umbraco.Web/ModelStateExtensions.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web } /// - /// Returns a list of cultures that have property validation errors errors + /// Returns a list of cultures that have property validation errors /// /// /// From 1d0def72f3717acd08f60f7907f35a810cbc9e80 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Wed, 16 Oct 2019 16:51:06 +0100 Subject: [PATCH 080/109] Handle duplicate keys in DataTypeMigration (#6651) --- .../Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs | 11 ++++++++++- .../V_8_0_0/DataTypes/DefaultPreValueMigrator.cs | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs index 7b2daa99ef..95b272dcb4 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs @@ -74,9 +74,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 .From() .Where(x => x.NodeId == group.Key)).First(); + // check for duplicate aliases + var aliases = group.Select(x => x.Alias).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray(); + if (aliases.Distinct().Count() != aliases.Length) + throw new InvalidOperationException($"Cannot migrate prevalues for datatype id={dataType.NodeId}, editor={dataType.EditorAlias}: duplicate alias."); + + // handle null/empty aliases + int index = 0; + var dictionary = group.ToDictionary(x => string.IsNullOrWhiteSpace(x.Alias) ? index++.ToString() : x.Alias); + // migrate the preValues to configuration var migrator = _preValueMigrators.GetMigrator(dataType.EditorAlias) ?? new DefaultPreValueMigrator(); - var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, group.ToDictionary(x => x.Alias, x => x)); + var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, dictionary); var json = JsonConvert.SerializeObject(config); // validate - and kill the migration if it fails diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs index 7112679de2..0c8161c9ef 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs @@ -24,8 +24,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes } // assuming we don't want to fall back to array - if (aliases.Length != preValuesA.Count || aliases.Any(string.IsNullOrWhiteSpace)) - throw new InvalidOperationException($"Cannot migrate datatype w/ id={dataTypeId} preValues: duplicate or null/empty alias."); + if (aliases.Any(string.IsNullOrWhiteSpace)) + throw new InvalidOperationException($"Cannot migrate prevalues for datatype id={dataTypeId}, editor={editorAlias}: null/empty alias."); // dictionary-base prevalues return GetPreValues(preValuesA).ToDictionary(x => x.Alias, GetPreValueValue); From f384b31e981b634cf8b2c79f2c714ad36c8b3e3a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 15:47:15 +1100 Subject: [PATCH 081/109] Gets a test to confirm the issue, now to fix --- .../PublishedContent/NuCacheChildrenTests.cs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d67d69d46e..7904eed0ec 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1079,7 +1079,7 @@ namespace Umbraco.Tests.PublishedContent /// 1) RefreshBranch with kits for a branch where the top most node is unpublished /// 2) RefreshBranch with kits for the branch where the top most node is published /// 3) RefreshBranch with kits for the branch where the top most node is published - /// 4) RefreshBranch with kits for the branch where the top most node is published + /// 4) RefreshNode /// 5) RefreshBranch with kits for the branch where the top most node is published /// [Test] @@ -1093,12 +1093,17 @@ namespace Umbraco.Tests.PublishedContent var paths = new Dictionary { { -1, "-1" } }; //root - yield return CreateInvariantKit(1, -1, 1, paths); + yield return CreateInvariantKit(100, -1, 1, paths); - //children - yield return CreateInvariantKit(2, 1, 1, paths); - yield return CreateInvariantKit(3, 1, 2, paths); //middle child - yield return CreateInvariantKit(4, 1, 3, paths); + //site + yield return CreateInvariantKit(2, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 1, paths); //middle child + yield return CreateInvariantKit(3, 100, 1, paths); + + //children of 1 + yield return CreateInvariantKit(20, 1, 1, paths); + yield return CreateInvariantKit(30, 1, 2, paths); + yield return CreateInvariantKit(40, 1, 3, paths); } //init with all published @@ -1109,7 +1114,7 @@ namespace Umbraco.Tests.PublishedContent var rootKit = _source.Kits[1].Clone(); - void ChangePublishFlagOfRoot(bool published, int assertGen) + void ChangePublishFlagOfRoot(bool published, int assertGen, TreeChangeTypes changeType) { //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) contentStore.CreateSnapshot(); @@ -1124,7 +1129,7 @@ namespace Umbraco.Tests.PublishedContent _snapshotService.Notify(new[] { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) }, out _, out _); Assert.AreEqual(assertGen, contentStore.Test.LiveGen); @@ -1132,19 +1137,19 @@ namespace Umbraco.Tests.PublishedContent } //unpublish the root - ChangePublishFlagOfRoot(false, 2); + ChangePublishFlagOfRoot(false, 2, TreeChangeTypes.RefreshBranch); - //publish the root - ChangePublishFlagOfRoot(true, 3); + //publish the root (since it's not published, it will cause a RefreshBranch) + ChangePublishFlagOfRoot(true, 3, TreeChangeTypes.RefreshBranch); //publish root + descendants - ChangePublishFlagOfRoot(true, 4); + ChangePublishFlagOfRoot(true, 4, TreeChangeTypes.RefreshBranch); - //publish the root - ChangePublishFlagOfRoot(true, 5); + //save/publish the root (since it's already published, it will just cause a RefreshNode + ChangePublishFlagOfRoot(true, 5, TreeChangeTypes.RefreshNode); //publish root + descendants - ChangePublishFlagOfRoot(true, 6); //TODO: This should fail, need to figure out what the diff is between this and a website + ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); //TODO: This should fail, need to figure out what the diff is between this and a website } [Test] From 439bacff0708bed566d397fec26deddf290b53ab Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:41:05 +1100 Subject: [PATCH 082/109] Fixes issue - when calling Set, we were not setting all node graph elements. --- .../PublishedContent/NuCacheChildrenTests.cs | 14 +++++++++---- .../PublishedCache/NuCache/ContentNode.cs | 2 ++ .../PublishedCache/NuCache/ContentStore.cs | 21 ++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 7904eed0ec..d0dcd66193 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1097,8 +1097,8 @@ namespace Umbraco.Tests.PublishedContent //site yield return CreateInvariantKit(2, 100, 1, paths); - yield return CreateInvariantKit(1, 100, 1, paths); //middle child - yield return CreateInvariantKit(3, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 2, paths); //middle child + yield return CreateInvariantKit(3, 100, 3, paths); //children of 1 yield return CreateInvariantKit(20, 1, 1, paths); @@ -1134,11 +1134,17 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(assertGen, contentStore.Test.LiveGen); Assert.IsTrue(contentStore.Test.NextGen); + + //get the latest gen for content Id 1 + var (gen, contentNode) = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(assertGen, gen); + //even when unpublishing/re-publishing/etc... the linked list is always maintained + AssertLinkedNode(contentNode, 100, 2, 3, 20, 40); } //unpublish the root ChangePublishFlagOfRoot(false, 2, TreeChangeTypes.RefreshBranch); - + //publish the root (since it's not published, it will cause a RefreshBranch) ChangePublishFlagOfRoot(true, 3, TreeChangeTypes.RefreshBranch); @@ -1149,7 +1155,7 @@ namespace Umbraco.Tests.PublishedContent ChangePublishFlagOfRoot(true, 5, TreeChangeTypes.RefreshNode); //publish root + descendants - ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); //TODO: This should fail, need to figure out what the diff is between this and a website + ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); } [Test] diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index 5f8e81fd31..3b4859432d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -6,6 +7,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { // represents a content "node" ie a pair of draft + published versions // internal, never exposed, to be accessed from ContentStore (only!) + [DebuggerDisplay("Id: {Id}, Path: {Path}")] internal class ContentNode { // special ctor for root pseudo node diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index f0d695f090..e90e67c050 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -549,7 +549,10 @@ namespace Umbraco.Web.PublishedCache.NuCache // manage children if (existing != null) + { kit.Node.FirstChildContentId = existing.FirstChildContentId; + kit.Node.LastChildContentId = existing.LastChildContentId; + } // set SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); @@ -571,6 +574,8 @@ namespace Umbraco.Web.PublishedCache.NuCache { // replacing existing, handle siblings kit.Node.NextSiblingContentId = existing.NextSiblingContentId; + //TODO: What about previous sibling?? + kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } _xmap[kit.Node.Uid] = kit.Node.Id; @@ -729,7 +734,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // clear if (existing != null) { + //this zero's out the branch (recursively), if we're in a new gen this will add a NULL placeholder for the gen ClearBranchLocked(existing); + //TODO: This removes the current GEN from the tree - do we really want to do that? RemoveTreeNodeLocked(existing); } @@ -1002,11 +1009,19 @@ namespace Umbraco.Web.PublishedCache.NuCache } // else it's going somewhere in the middle, - // and this is bad, perfs-wise - we only do it when moving - // inserting in linked list is slow, optimizing would require trees - // but... that should not happen very often - and not on large amount of data + // TODO: There was a note about performance when this occurs and that this only happens when moving and not very often, but that is not true, + // this also happens anytime a middle node is unpublished or republished (which causes a branch update), i'm unsure if this has perf impacts, + // i think this used to but it doesn't seem bad anymore that I can see... while (child.NextSiblingContentId > 0) { + if (child.NextSiblingContentId == content.Id) + { + content.PreviousSiblingContentId = child.Id; + child = content; + continue; + } + + // get next child var nextChildLink = GetRequiredLinkedNode(child.NextSiblingContentId, "next child", null); var nextChild = nextChildLink.Value; From ea642b3c51b5158b13e6b96df2d6f4b9b98c9460 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:43:09 +1100 Subject: [PATCH 083/109] removes TODO --- src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index e90e67c050..8ac7eec2b6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -574,7 +574,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { // replacing existing, handle siblings kit.Node.NextSiblingContentId = existing.NextSiblingContentId; - //TODO: What about previous sibling?? kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } From c91680e4c71cdef4f550f7879760b4aad7f42106 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:43:54 +1100 Subject: [PATCH 084/109] removes test code --- src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 8ac7eec2b6..f71abd6aa7 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -1013,14 +1013,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // i think this used to but it doesn't seem bad anymore that I can see... while (child.NextSiblingContentId > 0) { - if (child.NextSiblingContentId == content.Id) - { - content.PreviousSiblingContentId = child.Id; - child = content; - continue; - } - - // get next child var nextChildLink = GetRequiredLinkedNode(child.NextSiblingContentId, "next child", null); var nextChild = nextChildLink.Value; From b95404f5854df8732e0a278e66651b6c37ba28b9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:51:15 +1100 Subject: [PATCH 085/109] Adds simpler test to validate RefreshNode --- .../PublishedContent/NuCacheChildrenTests.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d0dcd66193..e615933c44 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1065,6 +1065,60 @@ namespace Umbraco.Tests.PublishedContent AssertLinkedNode(child3.contentNode, 1, 2, -1, -1, -1); } + [Test] + public void Refresh_Node_Ensures_Linked_list() + { + // NOTE: these tests are not using real scopes, in which case a Scope does not control + // how the snapshots generations work. We are forcing new snapshot generations manually. + + IEnumerable GetKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + //root + yield return CreateInvariantKit(100, -1, 1, paths); + + //site + yield return CreateInvariantKit(2, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 2, paths); //middle child + yield return CreateInvariantKit(3, 100, 3, paths); + + //children of 1 + yield return CreateInvariantKit(20, 1, 1, paths); + yield return CreateInvariantKit(30, 1, 2, paths); + yield return CreateInvariantKit(40, 1, 3, paths); + } + + Init(GetKits()); + + var snapshotService = (PublishedSnapshotService)_snapshotService; + var contentStore = snapshotService.GetContentStore(); + + Assert.AreEqual(1, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + + var middleNode = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(1, middleNode.gen); + AssertLinkedNode(middleNode.contentNode, 100, 2, 3, 20, 40); + + //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) + contentStore.CreateSnapshot(); + + Assert.IsFalse(contentStore.Test.NextGen); + + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) + }, out _, out _); + + Assert.AreEqual(2, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + + middleNode = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(2, middleNode.gen); + AssertLinkedNode(middleNode.contentNode, 100, 2, 3, 20, 40); + } + /// /// This addresses issue: https://github.com/umbraco/Umbraco-CMS/issues/6698 /// From 4a7c73993b9bec48bfe27158475a150d072d92b8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 21:05:27 +1100 Subject: [PATCH 086/109] formatting --- src/Umbraco.Core/Mapping/UmbracoMapper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Mapping/UmbracoMapper.cs b/src/Umbraco.Core/Mapping/UmbracoMapper.cs index 21164e8de9..e62825101c 100644 --- a/src/Umbraco.Core/Mapping/UmbracoMapper.cs +++ b/src/Umbraco.Core/Mapping/UmbracoMapper.cs @@ -343,7 +343,8 @@ namespace Umbraco.Core.Mapping if (ctor == null) return null; - _ctors.AddOrUpdate(sourceType, sourceCtor, (k, v) => { + _ctors.AddOrUpdate(sourceType, sourceCtor, (k, v) => + { // Add missing constructors foreach (var c in sourceCtor) { From a3779b5be9d226c3a821ba644bd6750c1607141e Mon Sep 17 00:00:00 2001 From: James George Date: Thu, 17 Oct 2019 16:36:21 +0530 Subject: [PATCH 087/109] [Docs]: minor tweak (#6743) --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index bdf9ef9f67..23142186df 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,4 +1,4 @@ -# [Umbraco CMS](https://umbraco.com) · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/dev)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge) +# [Umbraco CMS](https://umbraco.com) · [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/dev)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![Twitter](https://img.shields.io/twitter/follow/umbraco.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=umbraco) Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social. From 0355f7111e594e7770f9dd030aa03df8faa00f16 Mon Sep 17 00:00:00 2001 From: nfJakobsen <54019528+nfJakobsen@users.noreply.github.com> Date: Thu, 17 Oct 2019 08:51:02 +0200 Subject: [PATCH 088/109] Documenttypes correctly spelled in danish is one word. --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 9968af4d26..08d444ee54 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -332,7 +332,7 @@ Vælg den dokumenttype, du vil oprette en indholdsskabelon til Angiv et navn for mappen Vælg en type og skriv en titel - "dokument typer".]]> + "dokumenttyper".]]> Document Types within the Settings section.]]> Den valgte side i træet tillader ikke at sider oprettes under den. Rediger tilladelser for denne dokumenttype. From fef6730c1fce628bb9019aff20f9ac949cb4ee01 Mon Sep 17 00:00:00 2001 From: nfJakobsen <54019528+nfJakobsen@users.noreply.github.com> Date: Thu, 17 Oct 2019 09:10:27 +0200 Subject: [PATCH 089/109] Fixed title to proper danish --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 08d444ee54..a16de5a706 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -935,7 +935,7 @@ Mange hilsner fra Umbraco robotten Sti til fil Absolut sti til fil (f.eks.: /bin/umbraco.bin) Installeret - Installeret pakker + Installerede pakker Installér lokal Afslut Denne pakke har ingen konfigurationsvisning From 6b4f7f9ec85398a37a31704cc12704f85167a66c Mon Sep 17 00:00:00 2001 From: nfJakobsen <54019528+nfJakobsen@users.noreply.github.com> Date: Thu, 17 Oct 2019 09:29:37 +0200 Subject: [PATCH 090/109] Added apostrophe to Filtrer... in danish --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index a16de5a706..0a4a009c3c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -525,7 +525,7 @@ Label... Indtast beskrivelse Søg... - Filtrer... + Filtrér... Indtast nøgleord (tryk på Enter efter hvert nøgleord)... Indtast din e-mail Indtast en besked... From 2aede644f4dc4d9d39792b1f00f25a540b8aec1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kongsh=C3=B8j?= Date: Thu, 17 Oct 2019 16:27:34 +0200 Subject: [PATCH 091/109] Only show time in datepicker if format is HH/HH:mm/HH:mm:ss (#6525) --- .../propertyeditors/datepicker/datepicker.controller.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 869ca71eee..9100f2e1f6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -53,6 +53,12 @@ function dateTimePickerController($scope, notificationsService, assetsService, a dateFormat: dateFormat, time_24hr: true }; + + // Don't show calendar if date format has been set to only time + if ($scope.model.config.format === "HH:mm:ss" || $scope.model.config.format === "HH:mm" || $scope.model.config.format === "HH") { + $scope.datePickerConfig.enableTime = true; + $scope.datePickerConfig.noCalendar = true; + } setDatePickerVal(); From 04a201f5f05ba5281fdf28892510eb712dd62b67 Mon Sep 17 00:00:00 2001 From: Matthew-Wise <6782865+Matthew-Wise@users.noreply.github.com> Date: Thu, 17 Oct 2019 15:41:25 +0100 Subject: [PATCH 092/109] install ngAria (#6628) --- .../gulp/tasks/dependencies.js | 7 +- src/Umbraco.Web.UI.Client/package-lock.json | 64 ++++++++----------- src/Umbraco.Web.UI.Client/package.json | 1 + src/Umbraco.Web.UI.Client/src/app.js | 1 + .../src/install.loader.js | 1 + .../test/config/karma.conf.js | 1 + src/Umbraco.Web/JavaScript/JsInitialize.js | 1 + 7 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js index 819c804a4f..acf0e6a8f0 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js @@ -42,6 +42,11 @@ gulp.task('dependencies', function () { "src": ["./node_modules/angular/angular.js"], "base": "./node_modules/angular" }, + { + "name": "angular-aria", + "src": ["./node_modules/angular-aria/angular-aria.min.js"], + "base": "./node_modules/angular-aria" + }, { "name": "angular-cookies", "src": ["./node_modules/angular-cookies/angular-cookies.js"], @@ -100,7 +105,7 @@ gulp.task('dependencies', function () { "name": "angular-messages", "src": ["./node_modules/angular-messages/angular-messages.js"], "base": "./node_modules/angular-messages" - }, + }, { "name": "angular-mocks", "src": ["./node_modules/angular-mocks/angular-mocks.js"], diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 9c0011d2f5..6c05976c1d 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -916,6 +916,11 @@ "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.5.tgz", "integrity": "sha512-kU/fHIGf2a4a3bH7E1tzALTHk+QfoUSCK9fEcMFisd6ZWvNDwPzXWAilItqOC3EDiAXPmGHaNc9/aXiD9xrAxQ==" }, + "angular-aria": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.5.tgz", + "integrity": "sha512-X2dGRw+PK7hrV7/X1Ns4e5P3KC/OBFi1l7z//D/v7zbZObsAx48qBoX7unsck+s4+mnO+ikNNkHG5N49VfAyRw==" + }, "angular-chart.js": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/angular-chart.js/-/angular-chart.js-1.1.1.tgz", @@ -5302,8 +5307,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5324,14 +5328,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" @@ -5346,20 +5348,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", @@ -5476,8 +5475,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5489,7 +5487,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5504,7 +5501,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5512,14 +5508,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5538,7 +5532,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5619,8 +5612,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5632,7 +5624,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5718,8 +5709,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5755,7 +5745,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", @@ -5775,7 +5764,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5819,14 +5807,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -8616,9 +8602,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==", "dev": true }, "lodash._basecopy": { @@ -8956,9 +8942,9 @@ } }, "marked": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.2.tgz", - "integrity": "sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.3.tgz", + "integrity": "sha512-Fqa7eq+UaxfMriqzYLayfqAE40WN03jf+zHjT18/uXNuzjq3TY0XTbrAoPeqSJrAmPz11VuUA+kBPYOhHt9oOQ==", "dev": true }, "matchdep": { @@ -9356,9 +9342,9 @@ "dev": true }, "nouislider": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.0.1.tgz", - "integrity": "sha512-YNLKuABWYxmC5WXJ9TUj3N7+iyL/xT3+jm1mgOMXoqBhAL0Pj9BMgyKmLgwRnrxNN+C/fe7sFmpQDDPsxbMT2w==" + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.0.2.tgz", + "integrity": "sha512-N4AQStV4frh+XcLUwMI/hZpBP6tRboDE/4LZ7gzfxMVXFi/2J9URphnm40Ff4KEyrAVGSGaWApvljoMzTNWBlA==" }, "npm": { "version": "6.11.3", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c8acd0f88d..ae1dbcd828 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -10,6 +10,7 @@ "ace-builds": "1.4.2", "angular": "1.7.5", "angular-animate": "1.7.5", + "angular-aria": "1.7.5", "angular-chart.js": "^1.1.1", "angular-cookies": "1.7.5", "angular-dynamic-locale": "0.1.37", diff --git a/src/Umbraco.Web.UI.Client/src/app.js b/src/Umbraco.Web.UI.Client/src/app.js index 8e0eaa4943..74a7008901 100644 --- a/src/Umbraco.Web.UI.Client/src/app.js +++ b/src/Umbraco.Web.UI.Client/src/app.js @@ -12,6 +12,7 @@ var app = angular.module('umbraco', [ 'ngSanitize', 'ngTouch', 'ngMessages', + 'ngAria', 'tmh.dynamicLocale', 'ngFileUpload', 'LocalStorageModule', diff --git a/src/Umbraco.Web.UI.Client/src/install.loader.js b/src/Umbraco.Web.UI.Client/src/install.loader.js index 99be6db9f1..997c8cbe84 100644 --- a/src/Umbraco.Web.UI.Client/src/install.loader.js +++ b/src/Umbraco.Web.UI.Client/src/install.loader.js @@ -6,6 +6,7 @@ LazyLoad.js([ 'lib/angular-touch/angular-touch.js', 'lib/angular-sanitize/angular-sanitize.js', 'lib/angular-messages/angular-messages.js', + 'lib/angular-aria/angular-aria.min.js', 'lib/underscore/underscore-min.js', 'lib/angular-ui-sortable/sortable.js', 'js/installer.app.js', diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 08223de092..39c4844466 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -15,6 +15,7 @@ module.exports = function (config) { 'node_modules/angular/angular.js', 'node_modules/angular-animate/angular-animate.js', 'node_modules/angular-cookies/angular-cookies.js', + 'node_modules/angular-aria/angular-aria.min.js', 'node_modules/angular-local-storage/dist/angular-local-storage.min.js', 'node_modules/angular-route/angular-route.js', 'node_modules/angular-sanitize/angular-sanitize.js', diff --git a/src/Umbraco.Web/JavaScript/JsInitialize.js b/src/Umbraco.Web/JavaScript/JsInitialize.js index 1ef6e74dfe..4b0a457b64 100644 --- a/src/Umbraco.Web/JavaScript/JsInitialize.js +++ b/src/Umbraco.Web/JavaScript/JsInitialize.js @@ -13,6 +13,7 @@ 'lib/angular-route/angular-route.js', 'lib/angular-cookies/angular-cookies.js', + 'lib/angular-aria/angular-aria.min.js', 'lib/angular-touch/angular-touch.js', 'lib/angular-sanitize/angular-sanitize.js', 'lib/angular-animate/angular-animate.js', From 4c8675664018770a0476487ee13682b25d7fdf54 Mon Sep 17 00:00:00 2001 From: Tom Pipe Date: Thu, 17 Oct 2019 15:06:43 +0100 Subject: [PATCH 093/109] Fix for #3510 - health check for http headers was case sensitive, but HTTP spec states http headers are case insensitive --- .../Checks/Security/BaseHttpHeaderCheck.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs index 7d644f8cf1..baf490ce97 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs @@ -115,7 +115,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security private bool DoHttpHeadersContainHeader(WebResponse response) { - return response.Headers.AllKeys.Contains(_header); + return response.Headers.AllKeys.Contains(_header, StringComparer.InvariantCultureIgnoreCase); } private bool DoMetaTagsContainKeyForHeader(WebResponse response) @@ -186,24 +186,22 @@ namespace Umbraco.Web.HealthCheck.Checks.Security } var removeHeaderElement = customHeadersElement.Elements("remove") - .SingleOrDefault(x => x.Attribute("name") != null && - x.Attribute("name")?.Value == _value); + .SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_value, StringComparison.InvariantCultureIgnoreCase) == true); if (removeHeaderElement == null) { - removeHeaderElement = new XElement("remove"); - removeHeaderElement.Add(new XAttribute("name", _header)); - customHeadersElement.Add(removeHeaderElement); + customHeadersElement.Add( + new XElement("remove", + new XAttribute("name", _header))); } var addHeaderElement = customHeadersElement.Elements("add") - .SingleOrDefault(x => x.Attribute("name") != null && - x.Attribute("name").Value == _header); + .SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_header, StringComparison.InvariantCultureIgnoreCase) == true); if (addHeaderElement == null) { - addHeaderElement = new XElement("add"); - addHeaderElement.Add(new XAttribute("name", _header)); - addHeaderElement.Add(new XAttribute("value", _value)); - customHeadersElement.Add(addHeaderElement); + customHeadersElement.Add( + new XElement("add", + new XAttribute("name", _header), + new XAttribute("value", _value))); } doc.Save(configFile); From 20fd5acae68684a92d035d249cb1cd7c025cc931 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Thu, 17 Oct 2019 17:32:19 +0200 Subject: [PATCH 094/109] v8: Extend the umb-checkbox component to handle localization (#5635) --- .../components/forms/umbcheckbox.directive.js | 22 +++++++++--- .../src/less/components/umb-list.less | 4 +++ .../src/less/properties.less | 4 +++ .../views/components/forms/umb-checkbox.html | 1 + .../src/views/content/overlays/publish.html | 14 ++++---- .../content/overlays/publishdescendants.html | 34 ++++++++----------- .../src/views/content/overlays/save.html | 14 ++++---- .../src/views/content/overlays/schedule.html | 14 ++++---- .../views/content/overlays/sendtopublish.html | 26 +++++++------- .../src/views/content/overlays/unpublish.html | 18 +++++----- .../src/views/packages/edit.html | 16 --------- .../listview/overlays/listviewpublish.html | 23 ++++++------- .../listview/overlays/listviewunpublish.html | 17 ++++------ 13 files changed, 97 insertions(+), 110 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js index 8560b5a3ea..0f3d251e82 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js @@ -26,6 +26,7 @@ @param {string} value Set the value of the checkbox. @param {string} name Set the name of the checkbox. @param {string} text Set the text for the checkbox label. +@param {string} labelKey Set a dictinary/localization string for the checkbox label @param {string} serverValidationField Set the val-server-field of the checkbox. @param {boolean} disabled Set the checkbox to be disabled. @param {boolean} required Set the checkbox to be required. @@ -35,13 +36,25 @@ (function () { 'use strict'; - - function UmbCheckboxController($timeout) { - + + function UmbCheckboxController($timeout, localizationService) { + var vm = this; + vm.$onInit = onInit; vm.change = change; + function onInit() { + // If a labelKey is passed let's update the returned text if it's does not contain an opening square bracket [ + if (vm.labelKey) { + localizationService.localize(vm.labelKey).then(function (data) { + if(data.indexOf('[') === -1){ + vm.text = data; + } + }); + } + } + function change() { if (vm.onChange) { $timeout(function () { @@ -50,7 +63,7 @@ } } } - + var component = { templateUrl: 'views/components/forms/umb-checkbox.html', controller: UmbCheckboxController, @@ -61,6 +74,7 @@ value: "@", name: "@", text: "@", + labelKey: "@?", serverValidationField: "@", disabled: "<", required: "<", diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less index 12d7085b0a..94cfa6f62c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less @@ -36,6 +36,10 @@ a.umb-list-item:focus { color: @gray-4; } +.umb-list-item__description--checkbox{ + margin: 0 0 0 26px; +} + .umb-list-checkbox { position: absolute; opacity: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less index e14bb5c0d6..152ea49bbd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/properties.less +++ b/src/Umbraco.Web.UI.Client/src/less/properties.less @@ -48,6 +48,10 @@ flex-direction: row; } +.date-wrapper-mini--checkbox{ + margin: 0 0 0 26px; +} + .date-wrapper-mini__date { display: flex; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html index fd3b451e90..c35c600ce1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html @@ -15,5 +15,6 @@ + {{vm.text}} diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html index d4a3bdd2a0..9e3499db6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html @@ -13,32 +13,30 @@
    -
    +
    + server-validation-field="{{variant.htmlId}}" + text="{{ variant.language.name }}" + />
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html index 5002007e20..7256fefbf7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html @@ -6,12 +6,11 @@
    - - - - +
    @@ -23,12 +22,11 @@
    - - - - +
    @@ -39,17 +37,17 @@
    -
    +
    + server-validation-field="{{variant.htmlId}}" + text="{{variant.language.name}}"/>
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html index 4ca52ffc85..ef135b7b48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html @@ -17,31 +17,29 @@
    -
    +
    + server-validation-field="{{variant.htmlId}}" + text="{{variant.language.name}}" + />
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/schedule.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/schedule.html index 4b0bd55e49..4f4e3c2874 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/schedule.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/schedule.html @@ -86,27 +86,25 @@
    -
    +
    + on-change="vm.changeSelection(variant)" + text="{{variant.language.name}}" + />
    - -
    +
    Publish:  {{variant.releaseDateFormatted}}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/sendtopublish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/sendtopublish.html index 55e2121d28..ee20680089 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/sendtopublish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/sendtopublish.html @@ -12,31 +12,29 @@
    -
    +
    + server-validation-field="{{variant.htmlId}}" + text="{{ variant.language.name }}" + />
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/unpublish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/unpublish.html index 32b1d38841..e384823cc3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/unpublish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/unpublish.html @@ -15,7 +15,7 @@
    -
    +
    + server-validation-field="{{variant.htmlId}}" + text="{{ variant.language.name }}" + />
    - + + + - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 53a8e58b58..9f91e1d0f7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -131,80 +131,64 @@ Add -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewpublish.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewpublish.html index abe1db73d2..88dfb45d4a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewpublish.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewpublish.html @@ -4,11 +4,11 @@

    - +
    - +
    @@ -26,20 +26,17 @@
    - + on-change="vm.changeSelection(language)" + text="{{language.name}}" + />
    - -
    + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewunpublish.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewunpublish.html index f59b351181..d7f33db58b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewunpublish.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/listviewunpublish.html @@ -26,19 +26,16 @@
    - + on-change="vm.changeSelection(language)" + text="{{language.name}}" + />
    - + + +
    From 7af15b3a28a01b3141280b8a9dde545bdf188edf Mon Sep 17 00:00:00 2001 From: Jeavon Date: Thu, 17 Oct 2019 17:26:32 +0100 Subject: [PATCH 095/109] Add additional null conditionals as this method is sometimes called for front end rendering where this is no current user (DTGE) --- src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 3eed40c8bf..5743e9c1d5 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -114,7 +114,7 @@ namespace Umbraco.Web.PropertyEditors if (editorValue.Value == null) return null; - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; + var userId = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser?.Id ?? Constants.Security.SuperUserId; var config = editorValue.DataTypeConfiguration as RichTextConfiguration; var mediaParent = config?.MediaParentId; From b87a952c6a614bcf48e4d651433e8b01837e420a Mon Sep 17 00:00:00 2001 From: Claus Date: Fri, 18 Oct 2019 09:39:36 +0200 Subject: [PATCH 096/109] fixes #6754 - skip custom errors when using StatusCodeFilterAttribute. --- src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs | 3 +++ src/Umbraco.Web/UmbracoModule.cs | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs index 24a41519f7..4a78d201ca 100644 --- a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs +++ b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs @@ -1,5 +1,7 @@ using System.Net; using System.Web.Mvc; +using Umbraco.Core; +using Umbraco.Web.Composing; namespace Umbraco.Web.Mvc { @@ -20,6 +22,7 @@ namespace Umbraco.Web.Mvc base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.StatusCode = (int)_statusCode; + filterContext.HttpContext.Response.TrySkipIisCustomErrors = Current.Configs.Settings().WebRouting.TrySkipIisCustomErrors; } } } diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 9065341ab9..2ec4141f5a 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -1,7 +1,6 @@ using System; using System.Web; using Umbraco.Core; -using Umbraco.Core.Collections; using Umbraco.Core.Logging; using Umbraco.Web.Composing; using Umbraco.Web.Routing; @@ -102,6 +101,5 @@ namespace Umbraco.Web return end; } - } } From d49ad81448446253a47cfc4a2356feabe62b614e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 18 Oct 2019 13:17:16 +0200 Subject: [PATCH 097/109] V8: Handle save keyboard shortcut from within the RTE (#6756) --- .../directives/components/content/edit.controller.js | 6 ++++++ .../src/common/services/tinymce.service.js | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 49d455d9d6..5398038113 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -201,6 +201,12 @@ $scope.page.buttonGroupState = "success"; })); + evts.push(eventsService.on("rte.shortcut.save", function(){ + if ($scope.page.showSaveButton) { + $scope.save(); + } + })); + evts.push(eventsService.on("content.saved", function(){ // Clear out localstorage keys that start with tinymce__ // When we save/perist a content node diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 4ff5a05522..33a15d74d3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1143,6 +1143,14 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s prependToContext: true }); + // the editor frame catches Ctrl+S and handles it with the system save dialog + // - we want to handle it in the content controller, so we'll emit an event instead + editor.addShortcut('Ctrl+S', '', function () { + angularHelper.safeApply($rootScope, function() { + eventsService.emit("rte.shortcut.save"); + }); + }); + }, insertLinkInEditor: function (editor, target, anchorElm) { From 9af135e7e7adf70c7b00fea33c09dc5ac70aa3f4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 17 Oct 2019 21:19:17 +0200 Subject: [PATCH 098/109] Make the color class prefix configurable for the umb-color-swatches directive --- .../directives/components/umbcolorswatches.directive.js | 9 ++++++++- .../src/views/components/umb-color-swatches.html | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js index d998504d1b..9d7927f59a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js @@ -18,6 +18,7 @@ Use this directive to generate color swatches to pick from. @param {string} size (attribute): The size (s, m). @param {string} useLabel (attribute): Specify if labels should be used. @param {string} useColorClass (attribute): Specify if color values are css classes. +@param {string} colorClassNamePrefix (attribute): Specify the prefix used for the class for each color (defaults to "btn"). @param {function} onSelect (expression): Callback function when the item is selected. **/ @@ -32,6 +33,11 @@ Use this directive to generate color swatches to pick from. if (angular.isUndefined(scope.useColorClass)) { scope.useColorClass = false; } + + // Set default to "btn" if not defined + if (angular.isUndefined(scope.colorClassNamePrefix)) { + scope.colorClassNamePrefix = "btn"; + } scope.setColor = function (color, $index, $event) { if (scope.onSelect) { @@ -66,7 +72,8 @@ Use this directive to generate color swatches to pick from. selectedColor: '=', onSelect: '&', useLabel: '=', - useColorClass: '=?' + useColorClass: '=?', + colorClassNamePrefix: '@?' }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html index 91d461668f..7e6bf443b2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html @@ -1,6 +1,6 @@ 
    - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 0437522845..7febda9508 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,4 +1,4 @@ -function listViewController($scope, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $location, listViewHelper, navigationService, editorService, overlayService, languageResource, mediaHelper) { +function listViewController($scope, $interpolate, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $location, listViewHelper, navigationService, editorService, overlayService, languageResource, mediaHelper) { //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content // that isn't created yet, if we continue this will use the parent id in the route params which isn't what @@ -168,6 +168,11 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete, cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture }; + _.each($scope.options.includeProperties, function (property) { + property.nameExp = !!property.nameTemplate + ? $interpolate(property.nameTemplate) + : undefined; + }); //watch for culture changes in the query strings and update accordingly $scope.$watch(function () { @@ -699,6 +704,13 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs value = value.substring(0, value.length - 3); } + if (e.nameExp) { + var newValue = e.nameExp({ value }); + if (newValue && (newValue = $.trim(newValue))) { + value = newValue; + } + } + // set what we've got on the result result[alias] = value; }); diff --git a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs index ae0b32fde1..ce41628ec1 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewConfiguration.cs @@ -77,6 +77,9 @@ namespace Umbraco.Web.PropertyEditors [JsonProperty("header")] public string Header { get; set; } + [JsonProperty("nameTemplate")] + public string Template { get; set; } + [JsonProperty("isSystem")] public int IsSystem { get; set; } // TODO: bool }