From a90f0c407027e7a93aa3923cdbfc45fbad368c5a Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 26 Jun 2019 14:37:02 +0200 Subject: [PATCH 001/173] Added custom validation messages to property types: model, dto and migration, update via property type editing, display when content editing. --- .../Migrations/Upgrade/UmbracoPlan.cs | 1 + ...AddPropertyTypeValidationMessageColumns.cs | 20 +++++++ src/Umbraco.Core/Models/PropertyType.cs | 24 ++++++++- .../Packaging/PackageDataInstallation.cs | 2 + .../Persistence/Dtos/PropertyTypeDto.cs | 10 ++++ .../Dtos/PropertyTypeReadOnlyDto.cs | 6 +++ .../Factories/PropertyGroupFactory.cs | 4 ++ .../Persistence/Mappers/PropertyTypeMapper.cs | 2 + .../Implement/ContentTypeCommonRepository.cs | 2 + .../PropertyEditors/DataValueEditor.cs | 6 +-- .../PropertyEditors/IDataValueEditor.cs | 6 ++- .../PropertyEditors/IValueFormatValidator.cs | 5 +- .../IValueRequiredValidator.cs | 5 +- .../Validators/RegexValidator.cs | 19 +++++-- .../Validators/RequiredValidator.cs | 22 ++++++-- .../Services/Implement/EntityXmlSerializer.cs | 4 ++ .../Services/PropertyValidationService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Mapping/ContentTypeModelMappingTests.cs | 32 ++++++----- .../components/umbgroupsbuilder.directive.js | 6 ++- .../less/components/umb-group-builder.less | 16 ++++-- src/Umbraco.Web.UI.Client/src/less/forms.less | 3 +- .../propertysettings/propertysettings.html | 53 ++++++++++++------- .../textbox/textbox.controller.js | 11 +++- .../propertyeditors/textbox/textbox.html | 4 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 + .../Umbraco/config/lang/en_us.xml | 2 + .../Editors/Filters/ContentModelValidator.cs | 2 +- .../ContentEditing/ContentPropertyDto.cs | 7 +++ .../ContentEditing/PropertyTypeValidation.cs | 6 +++ .../Mapping/ContentPropertyDisplayMapper.cs | 2 + .../Mapping/ContentPropertyDtoMapper.cs | 2 + .../Mapping/ContentTypeMapDefinition.cs | 2 + .../Models/Mapping/PropertyTypeGroupMapper.cs | 8 ++- .../NestedContentPropertyEditor.cs | 19 +++++-- .../PropertyEditors/TagsPropertyEditor.cs | 2 +- 36 files changed, 254 insertions(+), 66 deletions(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 834eade986..3a3c9cffc5 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -176,6 +176,7 @@ namespace Umbraco.Core.Migrations.Upgrade To("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}"); To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); + To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); //FINAL } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs new file mode 100644 index 0000000000..07514faf75 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs @@ -0,0 +1,20 @@ +using System.Linq; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 +{ + public class AddPropertyTypeValidationMessageColumns : MigrationBase + { + public AddPropertyTypeValidationMessageColumns(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); + + AddColumnIfNotExists(columns, "mandatoryMessage"); + AddColumnIfNotExists(columns, "validationRegExpMessage"); + } + } +} diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 1e950a876c..1ae5133f8d 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -25,8 +25,10 @@ namespace Umbraco.Core.Models private string _propertyEditorAlias; private ValueStorageType _valueStorageType; private bool _mandatory; + private string _mandatoryMessage; private int _sortOrder; private string _validationRegExp; + private string _validationRegExpMessage; private ContentVariation _variations; /// @@ -175,7 +177,7 @@ namespace Umbraco.Core.Models } /// - /// Gets of sets a value indicating whether a value for this property type is required. + /// Gets or sets a value indicating whether a value for this property type is required. /// [DataMember] public bool Mandatory @@ -184,6 +186,16 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _mandatory, nameof(Mandatory)); } + /// + /// Gets or sets the custom validation message used when a value for this PropertyType is required + /// + [DataMember] + public string MandatoryMessage + { + get => _mandatoryMessage; + set => SetPropertyValueAndDetectChanges(value, ref _mandatoryMessage, nameof(MandatoryMessage)); + } + /// /// Gets of sets the sort order of the property type. /// @@ -204,6 +216,16 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _validationRegExp, nameof(ValidationRegExp)); } + /// + /// Gets or sets the custom validation message used when a pattern for this PropertyType must be matched + /// + [DataMember] + public string ValidationRegExpMessage + { + get => _validationRegExpMessage; + set => SetPropertyValueAndDetectChanges(value, ref _validationRegExpMessage, nameof(ValidationRegExpMessage)); + } + /// /// Gets or sets the content variation of the property type. /// diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index c811f484bc..d5738b5e3f 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -749,7 +749,9 @@ namespace Umbraco.Core.Packaging Name = property.Element("Name").Value, Description = (string)property.Element("Description"), Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, + MandatoryMessage = property.Element("MandatoryMessage") != null ? (string)property.Element("MandatoryMessage") : string.Empty, ValidationRegExp = (string)property.Element("Validation"), + ValidationRegExpMessage = property.Element("ValidationRegExpMessage") != null ? (string)property.Element("ValidationRegExpMessage") : string.Empty, SortOrder = sortOrder }; diff --git a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs index 8c52aa1e15..3e8d6e7496 100644 --- a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs @@ -43,10 +43,20 @@ namespace Umbraco.Core.Persistence.Dtos [Constraint(Default = "0")] public bool Mandatory { get; set; } + [Column("mandatoryMessage")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(500)] + public string MandatoryMessage { get; set; } + [Column("validationRegExp")] [NullSetting(NullSetting = NullSettings.Null)] public string ValidationRegExp { get; set; } + [Column("validationRegExpMessage")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(500)] + public string ValidationRegExpMessage { get; set; } + [Column("Description")] [NullSetting(NullSetting = NullSettings.Null)] [Length(2000)] diff --git a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeReadOnlyDto.cs b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeReadOnlyDto.cs index c68dee42b5..4c352a0134 100644 --- a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeReadOnlyDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeReadOnlyDto.cs @@ -32,9 +32,15 @@ namespace Umbraco.Core.Persistence.Dtos [Column("mandatory")] public bool Mandatory { get; set; } + [Column("mandatoryMessage")] + public string MandatoryMessage { get; set; } + [Column("validationRegExp")] public string ValidationRegExp { get; set; } + [Column("validationRegExpMessage")] + public string ValidationRegExpMessage { get; set; } + [Column("Description")] public string Description { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index c134748047..0846d6d1f6 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -59,8 +59,10 @@ namespace Umbraco.Core.Persistence.Factories propertyType.Key = typeDto.UniqueId; propertyType.Name = typeDto.Name; propertyType.Mandatory = typeDto.Mandatory; + propertyType.MandatoryMessage = typeDto.MandatoryMessage; propertyType.SortOrder = typeDto.SortOrder; propertyType.ValidationRegExp = typeDto.ValidationRegExp; + propertyType.ValidationRegExpMessage = typeDto.ValidationRegExpMessage; propertyType.PropertyGroupId = new Lazy(() => tempGroupDto.Id); propertyType.CreateDate = createDate; propertyType.UpdateDate = updateDate; @@ -123,9 +125,11 @@ namespace Umbraco.Core.Persistence.Factories DataTypeId = propertyType.DataTypeId, Description = propertyType.Description, Mandatory = propertyType.Mandatory, + MandatoryMessage = propertyType.MandatoryMessage, Name = propertyType.Name, SortOrder = propertyType.SortOrder, ValidationRegExp = propertyType.ValidationRegExp, + ValidationRegExpMessage = propertyType.ValidationRegExpMessage, UniqueId = propertyType.Key, Variations = (byte)propertyType.Variations }; diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs index ab1869a7f5..6f22b61f9a 100644 --- a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs @@ -24,9 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers DefineMap(nameof(PropertyType.DataTypeId), nameof(PropertyTypeDto.DataTypeId)); DefineMap(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description)); DefineMap(nameof(PropertyType.Mandatory), nameof(PropertyTypeDto.Mandatory)); + DefineMap(nameof(PropertyType.MandatoryMessage), nameof(PropertyTypeDto.MandatoryMessage)); DefineMap(nameof(PropertyType.Name), nameof(PropertyTypeDto.Name)); DefineMap(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder)); DefineMap(nameof(PropertyType.ValidationRegExp), nameof(PropertyTypeDto.ValidationRegExp)); + DefineMap(nameof(PropertyType.ValidationRegExpMessage), nameof(PropertyTypeDto.ValidationRegExpMessage)); DefineMap(nameof(PropertyType.PropertyEditorAlias), nameof(DataTypeDto.EditorAlias)); DefineMap(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType)); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs index ccafb9f771..8cbcf4a523 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs @@ -293,10 +293,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Id = dto.Id, Key = dto.UniqueId, Mandatory = dto.Mandatory, + MandatoryMessage = dto.MandatoryMessage, Name = dto.Name, PropertyGroupId = groupId.HasValue ? new Lazy(() => groupId.Value) : null, SortOrder = dto.SortOrder, ValidationRegExp = dto.ValidationRegExp, + ValidationRegExpMessage = dto.ValidationRegExpMessage, Variations = (ContentVariation)dto.Variations }; } diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index c4380f032c..b88c49583f 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -85,7 +85,7 @@ namespace Umbraco.Core.PropertyEditors public string ValueType { get; set; } /// - public IEnumerable Validate(object value, bool required, string format) + public IEnumerable Validate(object value, bool required, string requiredMessage, string format, string formatMessage) { List results = null; var r = Validators.SelectMany(v => v.Validate(value, ValueType, Configuration)).ToList(); @@ -97,14 +97,14 @@ namespace Umbraco.Core.PropertyEditors if (required) { - r = RequiredValidator.ValidateRequired(value, ValueType).ToList(); + r = RequiredValidator.ValidateRequired(value, ValueType, requiredMessage).ToList(); if (r.Any()) { if (results == null) results = r; else results.AddRange(r); } } var stringValue = value?.ToString(); if (!string.IsNullOrWhiteSpace(format) && !string.IsNullOrWhiteSpace(stringValue)) { - r = FormatValidator.ValidateFormat(value, ValueType, format).ToList(); + r = FormatValidator.ValidateFormat(value, ValueType, format, formatMessage).ToList(); if (r.Any()) { if (results == null) results = r; else results.AddRange(r); } } diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index cb68531cc7..0db4881da4 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -35,12 +35,14 @@ namespace Umbraco.Core.PropertyEditors bool HideLabel { get; } /// - /// Validates a property value. + /// Validates a property value using custom messages. /// /// The property value. /// A value indicating whether the property value is required. + /// A custom validation message to use when the property value is required. /// A specific format (regex) that the property value must respect. - IEnumerable Validate(object value, bool required, string format); + /// A custom validation message to use when the property value is does not match the specific format (regex). + IEnumerable Validate(object value, bool required, string requiredMessage, string format, string formatMessage); /// /// Gets the validators to use to validate the edited value. diff --git a/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs index 3e4aea4385..a361f387e3 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs @@ -14,11 +14,12 @@ namespace Umbraco.Core.PropertyEditors /// The value to validate. /// The value type. /// A format definition. + /// A custom validation message to use when the property value is does not match the specific format (regex). /// Validation results. /// /// The is expected to be a valid regular expression. /// This is used to validate values against the property type validation regular expression. /// - IEnumerable ValidateFormat(object value, string valueType, string format); + IEnumerable ValidateFormat(object value, string valueType, string format, string formatMessage); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs index f8e62788c8..7055e1287b 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs @@ -13,10 +13,11 @@ namespace Umbraco.Core.PropertyEditors /// /// The value to validate. /// The value type. + /// A custom validation message to use when the property value is required. /// Validation results. /// /// This is used to validate values when the property type specifies that a value is required. /// - IEnumerable ValidateRequired(object value, string valueType); + IEnumerable ValidateRequired(object value, string valueType, string requiredMessage); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs index e405fa3a3e..53686d745c 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs @@ -58,17 +58,28 @@ namespace Umbraco.Core.PropertyEditors.Validators public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { if (_regex == null) + { throw new InvalidOperationException("The validator has not been configured."); + } - return ValidateFormat(value, valueType, _regex); + return ValidateFormat(value, valueType, _regex, string.Empty); } /// - public IEnumerable ValidateFormat(object value, string valueType, string format) + public IEnumerable ValidateFormat(object value, string valueType, string format, string formatMessage) { - if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullOrEmptyException(nameof(format)); + if (string.IsNullOrWhiteSpace(format)) + { + throw new ArgumentNullOrEmptyException(nameof(format)); + } + if (value == null || !new Regex(format).IsMatch(value.ToString())) - yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" }); + { + var message = string.IsNullOrWhiteSpace(formatMessage) + ? _textService.Localize("validation", "invalidPattern") + : formatMessage; + yield return new ValidationResult(message, new[] { "value" }); + } } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs index c51f572817..d752c4ca47 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs @@ -27,28 +27,40 @@ namespace Umbraco.Core.PropertyEditors.Validators /// public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { - return ValidateRequired(value, valueType); + return ValidateRequired(value, valueType, string.Empty); } /// - public IEnumerable ValidateRequired(object value, string valueType) + public IEnumerable ValidateRequired(object value, string valueType, string requiredMessage) { if (value == null) { - yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] {"value"}); + var message = string.IsNullOrWhiteSpace(requiredMessage) + ? _textService.Localize("validation", "invalidNull") + : requiredMessage; + yield return new ValidationResult(message, new[] {"value"}); yield break; } if (valueType.InvariantEquals(ValueTypes.Json)) { + var message = string.IsNullOrWhiteSpace(requiredMessage) + ? _textService.Localize("validation", "invalidEmpty") + : requiredMessage; if (value.ToString().DetectIsEmptyJson()) - yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); + { + yield return new ValidationResult(message, new[] { "value" }); + } + yield break; } if (value.ToString().IsNullOrWhiteSpace()) { - yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); + var message = string.IsNullOrWhiteSpace(requiredMessage) + ? _textService.Localize("validation", "invalidEmpty") + : requiredMessage; + yield return new ValidationResult(message, new[] { "value" }); } } } diff --git a/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs index 863ecc0b1b..d9be4b01b7 100644 --- a/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs @@ -360,7 +360,9 @@ namespace Umbraco.Core.Services.Implement new XElement("Definition", definition.Key), new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name), new XElement("Mandatory", propertyType.Mandatory.ToString()), + new XElement("MandatoryMessage", propertyType.MandatoryMessage), new XElement("Validation", propertyType.ValidationRegExp), + new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage), new XElement("Description", new XCData(propertyType.Description))); genericProperties.Add(genericProperty); } @@ -486,7 +488,9 @@ namespace Umbraco.Core.Services.Implement new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name), new XElement("SortOrder", propertyType.SortOrder), new XElement("Mandatory", propertyType.Mandatory.ToString()), + propertyType.MandatoryMessage != null ? new XElement("MandatoryMessage", propertyType.MandatoryMessage) : null, propertyType.ValidationRegExp != null ? new XElement("Validation", propertyType.ValidationRegExp) : null, + propertyType.ValidationRegExpMessage != null ? new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage) : null, propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null); genericProperties.Add(genericProperty); diff --git a/src/Umbraco.Core/Services/PropertyValidationService.cs b/src/Umbraco.Core/Services/PropertyValidationService.cs index b846095bd1..2e1ba0b8eb 100644 --- a/src/Umbraco.Core/Services/PropertyValidationService.cs +++ b/src/Umbraco.Core/Services/PropertyValidationService.cs @@ -133,7 +133,7 @@ namespace Umbraco.Core.Services var editor = _propertyEditors[propertyType.PropertyEditorAlias]; var configuration = _dataTypeService.GetDataType(propertyType.DataTypeId).Configuration; var valueEditor = editor.GetValueEditor(configuration); - return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.ValidationRegExp).Any(); + return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.MandatoryMessage, propertyType.ValidationRegExp, propertyType.ValidationRegExpMessage).Any(); } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7c0e41348b..cbd372cfb6 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -240,6 +240,7 @@ + diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 21180ce51b..eee7446823 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -621,7 +621,9 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation() { Mandatory = true, - Pattern = "xyz" + MandatoryMessage = "Please enter a value", + Pattern = "xyz", + PatternMessage = "Please match the pattern", } }; @@ -634,7 +636,9 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(basic.DataTypeId, result.DataTypeId); Assert.AreEqual(basic.Label, result.Name); Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory); + Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage); Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp); + Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage); } [Test] @@ -655,7 +659,9 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation() { Mandatory = true, - Pattern = "xyz" + MandatoryMessage = "Please enter a value", + Pattern = "xyz", + PatternMessage = "Please match the pattern", } }; @@ -668,7 +674,9 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(basic.DataTypeId, result.DataTypeId); Assert.AreEqual(basic.Label, result.Name); Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory); + Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage); Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp); + Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage); } [Test] @@ -951,7 +959,7 @@ namespace Umbraco.Tests.Models.Mapping Name = "Tab 1", SortOrder = 0, Inherited = false, - Properties = new [] + Properties = new[] { new MemberPropertyTypeBasic { @@ -965,7 +973,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1000,7 +1008,7 @@ namespace Umbraco.Tests.Models.Mapping Name = "Tab 1", SortOrder = 0, Inherited = false, - Properties = new [] + Properties = new[] { new PropertyTypeBasic { @@ -1011,7 +1019,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1052,7 +1060,7 @@ namespace Umbraco.Tests.Models.Mapping Name = "Tab 1", SortOrder = 0, Inherited = false, - Properties = new [] + Properties = new[] { new PropertyTypeBasic { @@ -1063,7 +1071,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1109,7 +1117,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1133,7 +1141,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1187,7 +1195,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 @@ -1211,7 +1219,7 @@ namespace Umbraco.Tests.Models.Mapping Validation = new PropertyTypeValidation { Mandatory = false, - Pattern = "" + Pattern = string.Empty }, SortOrder = 0, DataTypeId = 555 diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 396699866c..1d04418060 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -501,7 +501,9 @@ property.dataTypeIcon = propertyModel.dataTypeIcon; property.dataTypeName = propertyModel.dataTypeName; property.validation.mandatory = propertyModel.validation.mandatory; + property.validation.mandatoryMessage = propertyModel.validation.mandatoryMessage; property.validation.pattern = propertyModel.validation.pattern; + property.validation.patternMessage = propertyModel.validation.patternMessage; property.showOnMemberProfile = propertyModel.showOnMemberProfile; property.memberCanEdit = propertyModel.memberCanEdit; property.isSensitiveValue = propertyModel.isSensitiveValue; @@ -592,7 +594,9 @@ propertyState: "init", validation: { mandatory: false, - pattern: null + mandatoryMessage: null, + pattern: null, + patternMessage: null } }; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less index 4c23aef5f0..36bd00c8cb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-group-builder.less @@ -494,11 +494,11 @@ input.umb-group-builder__group-sort-value { font-weight: bold; font-size: 14px; color: @ui-action-type; - - &:hover{ + + &:hover { text-decoration: none; - color:@ui-action-type-hover; - border-color:@ui-action-border-hover; + color: @ui-action-type-hover; + border-color: @ui-action-border-hover; } } @@ -555,7 +555,13 @@ input.umb-group-builder__group-sort-value { overflow: hidden; } - .editor-validation-pattern{ + .editor-validation-message { + min-width: 100%; + min-height: 25px; + margin-top: 4px; + } + + .editor-validation-pattern { border: 1px solid @gray-7; margin: 10px 0 0; padding: 6px; diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index cfbb8b78ab..72abb3ba00 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -528,7 +528,8 @@ input[type="checkbox"][readonly] { .help-inline { display: inline-block; vertical-align: middle; - padding-left: 5px; + padding-top: 4px; + padding-left: 2px; } div.help { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index 93d7936326..56c2b94bd7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -81,38 +81,55 @@
- +
- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js index 32e9891eb4..2bbd7404f7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js @@ -1,4 +1,4 @@ -function textboxController($scope) { +function textboxController($scope, localizationService) { // macro parameter editor doesn't contains a config object, // so we create a new one to hold any properties if (!$scope.model.config) { @@ -18,6 +18,15 @@ function textboxController($scope) { } } $scope.model.change(); + + $scope.mandatoryMessage = ""; + if ($scope.model.validation.mandatoryMessage) { + $scope.mandatoryMessage = $scope.model.validation.mandatoryMessage; + } else { + localizationService.localize("general_required").then(function (value) { + $scope.mandatoryMessage = value; + }); + } } angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html index 92f02b9f5b..238d70e5e3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html @@ -7,9 +7,9 @@ ng-trim="false" ng-keyup="model.change()" /> - + {{textboxFieldForm.textbox.errorMsg}} - Required + {{mandatoryMessage}}
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 175146aafd..7541297815 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1951,7 +1951,9 @@ To manage your website, simply open the Umbraco back office and start adding con Validate as a URL ...or enter a custom validation Field is mandatory + Enter a custom validation error message (optional) Enter a regular expression + Enter a custom validation error message (optional) You need to add at least You can only have 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 6aa6054992..9357d4720a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1961,7 +1961,9 @@ To manage your website, simply open the Umbraco back office and start adding con Validate as a URL ...or enter a custom validation Field is mandatory + Enter a custom validation error message (optional) Enter a regular expression + Enter a custom validation error message (optional) You need to add at least You can only have items diff --git a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs index 4acf0c948e..67f2e2d090 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs @@ -167,7 +167,7 @@ namespace Umbraco.Web.Editors.Filters { // validate var valueEditor = editor.GetValueEditor(property.DataType.Configuration); - foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp)) + foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.IsRequiredMessage, property.ValidationRegExp, property.ValidationRegExpMessage)) { modelState.AddPropertyError(r, property.Alias, property.Culture); } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs index 1f9d0e9b77..83e5e2a9b2 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs @@ -11,10 +11,17 @@ namespace Umbraco.Web.Models.ContentEditing internal class ContentPropertyDto : ContentPropertyBasic { public IDataType DataType { get; set; } + public string Label { get; set; } + public string Description { get; set; } + public bool IsRequired { get; set; } + + public string IsRequiredMessage { get; set; } + public string ValidationRegExp { get; set; } + public string ValidationRegExpMessage { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs index 929c7ce324..a45af12341 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeValidation.cs @@ -11,7 +11,13 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "mandatory")] public bool Mandatory { get; set; } + [DataMember(Name = "mandatoryMessage")] + public string MandatoryMessage { get; set; } + [DataMember(Name = "pattern")] public string Pattern { get; set; } + + [DataMember(Name = "patternMessage")] + public string PatternMessage { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs index 8a45548e9c..535c67bee1 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayMapper.cs @@ -42,7 +42,9 @@ namespace Umbraco.Web.Models.Mapping //add the validation information dest.Validation.Mandatory = originalProp.PropertyType.Mandatory; + dest.Validation.MandatoryMessage = originalProp.PropertyType.MandatoryMessage; dest.Validation.Pattern = originalProp.PropertyType.ValidationRegExp; + dest.Validation.PatternMessage = originalProp.PropertyType.ValidationRegExpMessage; if (dest.PropertyEditor == null) { diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs index f192cd32ce..472a32f0b1 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoMapper.cs @@ -21,7 +21,9 @@ namespace Umbraco.Web.Models.Mapping base.Map(property, dest, context); dest.IsRequired = property.PropertyType.Mandatory; + dest.IsRequiredMessage = property.PropertyType.MandatoryMessage; dest.ValidationRegExp = property.PropertyType.ValidationRegExp; + dest.ValidationRegExpMessage = property.PropertyType.ValidationRegExpMessage; dest.Description = property.PropertyType.Description; dest.Label = property.PropertyType.Name; dest.DataType = DataTypeService.GetDataType(property.PropertyType.DataTypeId); diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index a438f04781..f89e0604b4 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -220,7 +220,9 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Label; target.DataTypeId = source.DataTypeId; target.Mandatory = source.Validation.Mandatory; + target.MandatoryMessage = source.Validation.MandatoryMessage; target.ValidationRegExp = source.Validation.Pattern; + target.ValidationRegExpMessage = source.Validation.PatternMessage; target.Variations = source.AllowCultureVariant ? ContentVariation.Culture : ContentVariation.Nothing; if (source.Id > 0) diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs index 8c5f347799..70074d22a9 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs @@ -223,7 +223,13 @@ namespace Umbraco.Web.Models.Mapping Alias = p.Alias, Description = p.Description, Editor = p.PropertyEditorAlias, - Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp}, + Validation = new PropertyTypeValidation + { + Mandatory = p.Mandatory, + MandatoryMessage = p.MandatoryMessage, + Pattern = p.ValidationRegExp, + PatternMessage = p.ValidationRegExpMessage, + }, Label = p.Name, View = propertyEditor.GetValueEditor().View, Config = config, diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 6dee2f78b5..f85171304b 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -313,9 +313,19 @@ namespace Umbraco.Web.PropertyEditors if (propType.Mandatory) { if (propValues[propKey] == null) - yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be null", new[] { propKey }); + { + var message = string.IsNullOrWhiteSpace(propType.MandatoryMessage) + ? $"'{propType.Name}' cannot be null" + : propType.MandatoryMessage; + yield return new ValidationResult($"Item {(i + 1)}: {message}", new[] { propKey }); + } else if (propValues[propKey].ToString().IsNullOrWhiteSpace() || (propValues[propKey].Type == JTokenType.Array && !propValues[propKey].HasValues)) - yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' cannot be empty", new[] { propKey }); + { + var message = string.IsNullOrWhiteSpace(propType.MandatoryMessage) + ? $"'{propType.Name}' cannot be empty" + : propType.MandatoryMessage; + yield return new ValidationResult($"Item{(i + 1)}: {message}", new[] { propKey }); + } } // Check regex @@ -325,7 +335,10 @@ namespace Umbraco.Web.PropertyEditors var regex = new Regex(propType.ValidationRegExp); if (!regex.IsMatch(propValues[propKey].ToString())) { - yield return new ValidationResult("Item " + (i + 1) + " '" + propType.Name + "' is invalid, it does not match the correct pattern", new[] { propKey }); + var message = string.IsNullOrWhiteSpace(propType.ValidationRegExpMessage) + ? $"'{propType.Name}' is invalid, it does not match the correct pattern" + : propType.ValidationRegExpMessage; + yield return new ValidationResult($"Item {(i + 1)}: {message}", new[] { propKey }); } } } diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs index 90527a8b8d..508f4d03e8 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.PropertyEditors private class RequiredJsonValueValidator : IValueRequiredValidator { /// - public IEnumerable ValidateRequired(object value, string valueType) + public IEnumerable ValidateRequired(object value, string valueType, string requiredMessage) { if (value == null) { From bb3d2cd948703196f2df883049a1dffba107d938 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 27 Jun 2019 15:10:27 +0200 Subject: [PATCH 002/173] Applied custom mandatory validation message to existing core property editors that support the mandatory setting. --- .../services/validationhelper.service.js | 30 +++++++++++++ .../colorpicker/colorpicker.controller.js | 2 +- .../datepicker/datepicker.controller.js | 7 +++- .../datepicker/datepicker.html | 2 +- .../dropdownFlexible.controller.js | 8 +++- .../dropdownFlexible/dropdownFlexible.html | 42 +++++++++++-------- .../propertyeditors/email/email.controller.js | 10 +++++ .../views/propertyeditors/email/email.html | 4 +- .../radiobuttons/radiobuttons.controller.js | 8 +++- .../radiobuttons/radiobuttons.html | 18 +++++--- .../textbox/textbox.controller.js | 16 +++---- 11 files changed, 108 insertions(+), 39 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.controller.js diff --git a/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js new file mode 100644 index 0000000000..0ab8e9fdb9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js @@ -0,0 +1,30 @@ +(function () { + 'use strict'; + + function validationHelperService($q, localizationService) { + + // Gets the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type's validation object + // or a localised default. + function getMandatoryMessage(validation) { + if (validation.mandatoryMessage) { + return $q.when(validation.mandatoryMessage); + } else { + return localizationService.localize("general_required").then(function (value) { + return $q.when(value); + }); + } + } + + var service = { + getMandatoryMessage: getMandatoryMessage + }; + + return service; + + } + + angular.module('umbraco.services').factory('validationHelper', validationHelperService); + + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js index d6fe709962..937e4e787b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js @@ -110,7 +110,7 @@ function ColorPickerController($scope) { ); return { isValid: isValid, - errorMsg: "Value cannot be empty", + errorMsg: $scope.model.validation.mandatoryMessage || "Value cannot be empty", errorKey: "required" }; } 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 62099734fb..092f6411e8 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 @@ -1,4 +1,4 @@ -function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) { +function dateTimePickerController($scope, angularHelper, dateHelper, validationHelper) { let flatPickr = null; @@ -56,6 +56,11 @@ function dateTimePickerController($scope, notificationsService, assetsService, a setDatePickerVal(); + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationHelper.getMandatoryMessage($scope.model.validation).then(function (value) { + $scope.mandatoryMessage = value; + }); } $scope.clearDate = function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 0d3fad580e..77719e4421 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -32,7 +32,7 @@
- Required + {{mandatoryMessage}} {{datePickerForm.datepicker.errorMsg}} Invalid date diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js index 3b341f7ac0..d842b3b006 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js @@ -1,5 +1,5 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleController", - function($scope) { + function ($scope, validationHelper) { //setup the default config var config = { @@ -89,4 +89,10 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo $scope.model.value = null; } } + + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationHelper.getMandatoryMessage($scope.model.validation).then(function (value) { + $scope.mandatoryMessage = value; + }); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html index 5f873e9e43..617d841139 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html @@ -1,21 +1,29 @@
- + + + - - + + + + + {{mandatoryMessage}} + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.controller.js new file mode 100644 index 0000000000..9c8a581dd4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.controller.js @@ -0,0 +1,10 @@ +function emailController($scope, validationHelper) { + + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationHelper.getMandatoryMessage($scope.model.validation).then(function(value) { + $scope.mandatoryMessage = value; + }); + +} +angular.module('umbraco').controller("Umbraco.PropertyEditors.EmailController", emailController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html index 881ad37d7c..8576d179f2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html @@ -1,4 +1,4 @@ -
+
- Required + {{mandatoryMessage}} Invalid email {{emailFieldForm.textbox.errorMsg}} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js index 3a96b6573a..13baf27f8a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.controller.js @@ -1,5 +1,5 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController", - function($scope) { + function ($scope, validationHelper) { function init() { @@ -19,6 +19,12 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsContro $scope.configItems = configItems; } + + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationHelper.getMandatoryMessage($scope.model.validation).then(function (value) { + $scope.mandatoryMessage = value; + }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html index 8c5f2a22d3..b5e55adebd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html @@ -1,7 +1,15 @@ 
-
    -
  • - -
  • -
+ + +
    +
  • + +
  • +
+ + + {{mandatoryMessage}} + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js index 2bbd7404f7..feb0148849 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js @@ -1,4 +1,4 @@ -function textboxController($scope, localizationService) { +function textboxController($scope, validationHelper) { // macro parameter editor doesn't contains a config object, // so we create a new one to hold any properties if (!$scope.model.config) { @@ -19,14 +19,10 @@ function textboxController($scope, localizationService) { } $scope.model.change(); - $scope.mandatoryMessage = ""; - if ($scope.model.validation.mandatoryMessage) { - $scope.mandatoryMessage = $scope.model.validation.mandatoryMessage; - } else { - localizationService.localize("general_required").then(function (value) { - $scope.mandatoryMessage = value; - }); - } - + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationHelper.getMandatoryMessage($scope.model.validation).then(function(value) { + $scope.mandatoryMessage = value; + }); } angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController); From 011ad47ac0709227aa3e7313966fb7c022b6d3ce Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 27 Jun 2019 20:00:52 +0200 Subject: [PATCH 003/173] Updated new ng-messsages elements to use tags introduced in main branch --- .../views/propertyeditors/datepicker/datepicker.html | 10 +++++----- .../dropdownFlexible/dropdownFlexible.html | 6 +++--- .../src/views/propertyeditors/email/email.html | 10 +++++----- .../propertyeditors/radiobuttons/radiobuttons.html | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 3142f078ec..0f3ae8f510 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -32,11 +32,11 @@
- - {{mandatoryMessage}} - {{datePickerForm.datepicker.errorMsg}} - Invalid date - +
+

{{mandatoryMessage}}

+

{{datePickerForm.datepicker.errorMsg}}

+

Invalid date

+

This translates to the following time on the server: {{serverTime}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html index 617d841139..1059df2994 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html @@ -21,9 +21,9 @@ ng-options="item.value as item.value for item in model.config.items" ng-required="model.validation.mandatory"> - - {{mandatoryMessage}} - +

+

{{mandatoryMessage}}

+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html index 8576d179f2..26ec22df8d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html @@ -8,10 +8,10 @@ ng-required="model.config.IsRequired || model.validation.mandatory" val-server="value" /> - - {{mandatoryMessage}} - Invalid email - {{emailFieldForm.textbox.errorMsg}} - +
+

{{mandatoryMessage}}

+

Invalid email

+

{{emailFieldForm.textbox.errorMsg}}

+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html index b5e55adebd..c4c76e0b40 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html @@ -7,9 +7,9 @@ - - {{mandatoryMessage}} - +
+

{{mandatoryMessage}}

+
From 1168197e6652eea6cbbd11a488740d29a27e2a6f Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 27 Jun 2019 23:20:40 +0200 Subject: [PATCH 004/173] Fixed formatting of nested content validation --- src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index f85171304b..c4b0333c2d 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -324,7 +324,7 @@ namespace Umbraco.Web.PropertyEditors var message = string.IsNullOrWhiteSpace(propType.MandatoryMessage) ? $"'{propType.Name}' cannot be empty" : propType.MandatoryMessage; - yield return new ValidationResult($"Item{(i + 1)}: {message}", new[] { propKey }); + yield return new ValidationResult($"Item {(i + 1)}: {message}", new[] { propKey }); } } From 8fe25ff781e2b0301f29054a4f67fde741698ce5 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Fri, 28 Jun 2019 14:46:28 +0200 Subject: [PATCH 005/173] Added null check to resolve failing front-end unit test --- .../src/common/services/validationhelper.service.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js index 0ab8e9fdb9..16ea5a60a5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js @@ -7,6 +7,10 @@ // Will either use the one provided on the property type's validation object // or a localised default. function getMandatoryMessage(validation) { + if (!validation) { + return $q.when(""); + } + if (validation.mandatoryMessage) { return $q.when(validation.mandatoryMessage); } else { From 973117df581aea9c6725ea658f3c1612b863affb Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 30 Sep 2019 20:16:29 +0200 Subject: [PATCH 006/173] Updated migration plan to move validation messages into migration to 8.3 --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 1 + .../AddPropertyTypeValidationMessageColumns.cs | 0 2 files changed, 1 insertion(+) rename src/Umbraco.Core/Migrations/Upgrade/{V_8_1_0 => V_8_3_0}/AddPropertyTypeValidationMessageColumns.cs (100%) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index cf9e0f7b67..a328a13bed 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common; using Umbraco.Core.Migrations.Upgrade.V_8_0_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_1; using Umbraco.Core.Migrations.Upgrade.V_8_1_0; +using Umbraco.Core.Migrations.Upgrade.V_8_3_0; namespace Umbraco.Core.Migrations.Upgrade { diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_3_0/AddPropertyTypeValidationMessageColumns.cs similarity index 100% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/AddPropertyTypeValidationMessageColumns.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_3_0/AddPropertyTypeValidationMessageColumns.cs From 133704db7bfd626b0c39cdca3542bc77def566ba Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 30 Sep 2019 20:22:13 +0200 Subject: [PATCH 007/173] Update validation message fields to single-line inputs --- .../propertysettings/propertysettings.html | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index ed183e3a03..4474390199 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -94,14 +94,14 @@ focus-when="{{vm.focusOnMandatoryField}}" - + +
/// + [Obsolete("Instead of throwing an exception, return null or an HTTP 404 status code instead.")] [Serializable] public class EntityNotFoundException : Exception { diff --git a/src/Umbraco.Web/Install/InstallException.cs b/src/Umbraco.Web/Install/InstallException.cs index 7efcbdc769..dfa2bc461b 100644 --- a/src/Umbraco.Web/Install/InstallException.cs +++ b/src/Umbraco.Web/Install/InstallException.cs @@ -7,6 +7,7 @@ namespace Umbraco.Web.Install /// Used for steps to be able to return a JSON structure back to the UI. /// /// + [Obsolete("Refactor the view model into a concrete serializable type.")] [Serializable] internal class InstallException : Exception { From af1615bbb738647d5004364163cc75bfa8e99cb2 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Sun, 6 Oct 2019 11:32:37 +0200 Subject: [PATCH 019/173] Removed usage of obsoleted FileSecurityException --- src/Umbraco.Core/IO/PhysicalFileSystem.cs | 2 +- src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs | 2 +- src/Umbraco.Tests/IO/ShadowFileSystemTests.cs | 2 +- .../Repositories/PartialViewRepositoryTests.cs | 7 ++++--- .../Persistence/Repositories/ScriptRepositoryTest.cs | 9 +++++---- .../Persistence/Repositories/StylesheetRepositoryTest.cs | 9 +++++---- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index e4edb2b86b..1cff75a3b2 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -314,7 +314,7 @@ namespace Umbraco.Core.IO // nothing prevents us to reach the file, security-wise, yet it is outside // this filesystem's root - throw - throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root."); + throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root."); } /// diff --git a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs index ab9b2cf73d..7c3c106724 100644 --- a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs @@ -101,7 +101,7 @@ namespace Umbraco.Tests.IO Assert.AreEqual(Path.Combine(basePath, @"foo\bar.tmp"), path); // that path is invalid as it would be outside the root directory - Assert.Throws(() => _fileSystem.GetFullPath("../../foo.tmp")); + Assert.Throws(() => _fileSystem.GetFullPath("../../foo.tmp")); // a very long path, which ends up being very long, works path = Repeat("bah/bah/", 50); diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 31b00e5cf8..343994b03a 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -238,7 +238,7 @@ namespace Umbraco.Tests.IO var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); - Assert.Throws(() => + Assert.Throws(() => { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) ss.AddFile("../../f1.txt", ms); diff --git a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs index 9c326b3ddc..dad2a0ea17 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -7,6 +7,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using System; namespace Umbraco.Tests.Persistence.Repositories { @@ -77,7 +78,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual("/Views/Partials/path-2/test-path-3.cshtml", partialView.VirtualPath); partialView = new PartialView(PartialViewType.PartialView, "\\test-path-4.cshtml") { Content = "// partialView" }; - Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ + Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ { repository.Save(partialView); }); @@ -86,11 +87,11 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsNull(partialView); // fixed in 7.3 - 7.2.8 used to... - Assert.Throws(() => + Assert.Throws(() => { partialView = (PartialView) repository.Get("\\test-path-4.cshtml"); // outside the filesystem, does not exist }); - Assert.Throws(() => + Assert.Throws(() => { partialView = (PartialView) repository.Get("../../packages.config"); // outside the filesystem, exists }); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs index 36c1bbdfb4..e3c316cad0 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using System.Text; using Moq; @@ -301,7 +302,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual("/scripts/path-2/test-path-3.js", script.VirtualPath); script = new Script("\\test-path-4.js") { Content = "// script" }; - Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ + Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ { repository.Save(script); }); @@ -310,11 +311,11 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsNull(script); // fixed in 7.3 - 7.2.8 used to... - Assert.Throws(() => + Assert.Throws(() => { script = repository.Get("\\test-path-4.js"); // outside the filesystem, does not exist }); - Assert.Throws(() => + Assert.Throws(() => { script = repository.Get("../packages.config"); // outside the filesystem, exists }); diff --git a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs index 6fae1d4749..f427f22796 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -1,4 +1,5 @@ -using System.Data; +using System; +using System.Data; using System.IO; using System.Linq; using System.Text; @@ -284,7 +285,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual("/css/path-2/test-path-3.css", stylesheet.VirtualPath); stylesheet = new Stylesheet("\\test-path-4.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; - Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ + Assert.Throws(() => // fixed in 7.3 - 7.2.8 used to strip the \ { repository.Save(stylesheet); }); @@ -294,11 +295,11 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.IsNull(stylesheet); // fixed in 7.3 - 7.2.8 used to... - Assert.Throws(() => + Assert.Throws(() => { stylesheet = repository.Get("\\test-path-4.css"); // outside the filesystem, does not exist }); - Assert.Throws(() => + Assert.Throws(() => { stylesheet = repository.Get("../packages.config"); // outside the filesystem, exists }); From 6350c8c05b8c90f7bae36281a405fe3307f599b0 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Sun, 6 Oct 2019 11:34:25 +0200 Subject: [PATCH 020/173] Removed usage of obsoleted WontImplementException --- .../Models/Entities/EntitySlim.cs | 22 +++++++++---------- .../Implement/DocumentRepository.cs | 12 +++++----- .../Repositories/Implement/MediaRepository.cs | 12 +++++----- .../Implement/PermissionRepository.cs | 14 ++++++------ .../Implement/SimpleGetRepository.cs | 8 +++---- .../Implement/UserGroupRepository.cs | 16 +++++++------- .../Published/NestedContentTests.cs | 2 +- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Umbraco.Core/Models/Entities/EntitySlim.cs b/src/Umbraco.Core/Models/Entities/EntitySlim.cs index b095965056..b4b09b58c2 100644 --- a/src/Umbraco.Core/Models/Entities/EntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/EntitySlim.cs @@ -66,7 +66,7 @@ namespace Umbraco.Core.Models.Entities public int ParentId { get; set; } /// - public void SetParent(ITreeEntity parent) => throw new WontImplementException(); + public void SetParent(ITreeEntity parent) => throw new InvalidOperationException("This property won't be implemented."); /// [DataMember] @@ -116,7 +116,7 @@ namespace Umbraco.Core.Models.Entities /// public object DeepClone() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } #endregion @@ -128,47 +128,47 @@ namespace Umbraco.Core.Models.Entities public bool IsDirty() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public bool IsPropertyDirty(string propName) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public IEnumerable GetDirtyProperties() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public void ResetDirtyProperties() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public bool WasDirty() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public bool WasPropertyDirty(string propertyName) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public void ResetWereDirtyProperties() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public void ResetDirtyProperties(bool rememberDirty) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } public IEnumerable GetWereDirtyProperties() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 30a2927cc8..1505f394ed 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -875,32 +875,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable GetDeleteClauses() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override void PersistNewItem(IContent entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override void PersistUpdatedItem(IContent entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override Sql GetBaseQuery(bool isCount) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override string GetBaseWhereClause() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index 25828b8126..3947773a76 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -425,32 +425,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable GetDeleteClauses() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override void PersistNewItem(IMedia entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override void PersistUpdatedItem(IMedia entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override Sql GetBaseQuery(bool isCount) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override string GetBaseWhereClause() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs index b4fd86c567..259f0b89c0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/PermissionRepository.cs @@ -252,27 +252,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override ContentPermissionSet PerformGet(int id) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable PerformGetAll(params int[] ids) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override Sql GetBaseQuery(bool isCount) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override string GetBaseWhereClause() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable GetDeleteClauses() @@ -280,11 +280,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return new List(); } - protected override Guid NodeObjectTypeId => throw new WontImplementException(); + protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented."); protected override void PersistDeletedItem(ContentPermissionSet entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/SimpleGetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/SimpleGetRepository.cs index 43233d0f31..f7e59820c3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/SimpleGetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/SimpleGetRepository.cs @@ -75,19 +75,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected sealed override IEnumerable GetDeleteClauses() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } - protected sealed override Guid NodeObjectTypeId => throw new WontImplementException(); + protected sealed override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented."); protected sealed override void PersistNewItem(TEntity entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected sealed override void PersistUpdatedItem(TEntity entity) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/UserGroupRepository.cs index 0701a0996e..0a66e29147 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/UserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/UserGroupRepository.cs @@ -286,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return list; } - protected override Guid NodeObjectTypeId => throw new WontImplementException(); + protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented."); protected override void PersistNewItem(IUserGroup entity) { @@ -370,35 +370,35 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override UserGroupWithUsers PerformGet(int id) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable PerformGetAll(params int[] ids) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override Sql GetBaseQuery(bool isCount) { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override string GetBaseWhereClause() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } protected override IEnumerable GetDeleteClauses() { - throw new WontImplementException(); + throw new InvalidOperationException("This method won't be implemented."); } - protected override Guid NodeObjectTypeId => throw new WontImplementException(); + protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented."); #endregion diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 9385b8955a..eea65568d4 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -264,7 +264,7 @@ namespace Umbraco.Tests.Published public override bool HasValue(string culture = null, string segment = null) => _hasValue; public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); - public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException(); + public override object GetXPathValue(string culture = null, string segment = null) => throw new InvalidOperationException("This method won't be implemented."); } } } From 604919495dc2323049bf2e0d59fed1ef209e21b3 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Sun, 6 Oct 2019 11:35:05 +0200 Subject: [PATCH 021/173] Removed usage of obsoleted EntityNotFoundException --- src/Umbraco.Web/Editors/MediaController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index a673f06e1d..81d5704b5a 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -815,7 +815,7 @@ namespace Umbraco.Web.Editors } else { - throw new EntityNotFoundException(parentId, "The passed id doesn't exist"); + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "The passed id doesn't exist")); } } else From 8920e10a94607ce0c155ac13f502732d2e7bdf42 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Sun, 6 Oct 2019 11:43:49 +0200 Subject: [PATCH 022/173] Removed usage of obsoleted DuplicateKeyException --- src/Umbraco.Core/Collections/ObservableDictionary.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Collections/ObservableDictionary.cs b/src/Umbraco.Core/Collections/ObservableDictionary.cs index 8e49ccf1b5..968a4a21cd 100644 --- a/src/Umbraco.Core/Collections/ObservableDictionary.cs +++ b/src/Umbraco.Core/Collections/ObservableDictionary.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core.Collections /// The equality comparer to use when comparing keys, or null to use the default comparer. public ObservableDictionary(Func keySelector, IEqualityComparer equalityComparer = null) { - KeySelector = keySelector ?? throw new ArgumentException("keySelector"); + KeySelector = keySelector ?? throw new ArgumentException(nameof(keySelector)); Indecies = new Dictionary(equalityComparer); } @@ -37,7 +37,7 @@ namespace Umbraco.Core.Collections { var key = KeySelector(item); if (Indecies.ContainsKey(key)) - throw new DuplicateKeyException(key.ToString()); + throw new ArgumentException($"An element with the same key '{key}' already exists in the dictionary.", nameof(item)); if (index != Count) { @@ -92,7 +92,7 @@ namespace Umbraco.Core.Collections { //confirm key matches if (!KeySelector(value).Equals(key)) - throw new InvalidOperationException("Key of new value does not match"); + throw new InvalidOperationException("Key of new value does not match."); if (!Indecies.ContainsKey(key)) { @@ -119,7 +119,7 @@ namespace Umbraco.Core.Collections //confirm key matches if (!KeySelector(value).Equals(key)) - throw new InvalidOperationException("Key of new value does not match"); + throw new InvalidOperationException("Key of new value does not match."); this[Indecies[key]] = value; return true; @@ -156,12 +156,12 @@ namespace Umbraco.Core.Collections { if (!Indecies.ContainsKey(currentKey)) { - throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection"); + throw new InvalidOperationException($"No item with the key '{currentKey}' was found in the dictionary."); } if (ContainsKey(newKey)) { - throw new DuplicateKeyException(newKey.ToString()); + throw new ArgumentException($"An element with the same key '{newKey}' already exists in the dictionary.", nameof(newKey)); } var currentIndex = Indecies[currentKey]; From ed90e71f768f0dcde19a43988f5cc654a6a2251e Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 7 Oct 2019 22:10:21 +0200 Subject: [PATCH 023/173] Removed usage of obsoleted ArgumentNullOrEmptyException --- src/Umbraco.Core/IO/MediaFileSystem.cs | 11 +- src/Umbraco.Core/IO/PhysicalFileSystem.cs | 9 +- src/Umbraco.Core/Manifest/ManifestParser.cs | 8 +- .../Expressions/Delete/DeleteBuilder.cs | 7 +- .../Migrations/Install/DatabaseBuilder.cs | 7 +- src/Umbraco.Core/Migrations/MigrationPlan.cs | 30 ++-- src/Umbraco.Core/Models/ContentBase.cs | 6 +- .../Models/ContentCultureInfos.cs | 5 +- .../Models/ContentCultureInfosCollection.cs | 6 +- .../Models/ContentRepositoryExtensions.cs | 21 ++- src/Umbraco.Core/Models/Member.cs | 13 +- .../Models/PublishedContent/ModelType.cs | 5 +- .../PublishedContent/PublishedCultureInfos.cs | 4 +- .../PublishedModelAttribute.cs | 5 +- src/Umbraco.Core/Models/RelationType.cs | 9 +- .../Implement/EntityContainerRepository.cs | 12 +- .../Persistence/UmbracoDatabaseFactory.cs | 5 +- .../ConfigurationFieldAttribute.cs | 21 +-- .../PropertyEditors/DataEditorAttribute.cs | 23 +-- .../Validators/RegexValidator.cs | 10 +- src/Umbraco.Core/ReflectionUtilities.cs | 146 ++++++++++++------ .../Security/BackOfficeUserStore.cs | 16 +- .../Services/Implement/ContentService.cs | 10 +- ...peServiceBaseOfTRepositoryTItemTService.cs | 7 +- .../Services/Implement/MediaService.cs | 6 +- .../Services/Implement/MemberService.cs | 6 +- .../Services/Implement/UserService.cs | 8 +- src/Umbraco.Core/StringExtensions.cs | 8 +- .../Xml/UmbracoXPathPathSyntaxParser.cs | 6 +- src/Umbraco.Core/Xml/XmlHelper.cs | 41 ++--- .../Services/UserServiceTests.cs | 2 +- src/Umbraco.Web/GridTemplateExtensions.cs | 28 ++-- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 37 +++-- src/Umbraco.Web/HttpUrlHelperExtensions.cs | 10 +- src/Umbraco.Web/Models/Trees/TreeNode.cs | 10 +- .../Mvc/AreaRegistrationExtensions.cs | 4 +- .../Search/SearchableTreeAttribute.cs | 74 ++++----- .../AuthenticationOptionsExtensions.cs | 4 +- src/Umbraco.Web/UrlHelperExtensions.cs | 12 +- src/Umbraco.Web/UrlHelperRenderExtensions.cs | 17 +- ...EnsureUserPermissionForContentAttribute.cs | 13 +- .../EnsureUserPermissionForMediaAttribute.cs | 9 +- .../OutgoingDateTimeFormatAttribute.cs | 10 +- 43 files changed, 394 insertions(+), 307 deletions(-) diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index 2ce1230bcc..05c02171ba 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -1,15 +1,10 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO; using System.Linq; using System.Threading.Tasks; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; -using Umbraco.Core.Media; using Umbraco.Core.Models; namespace Umbraco.Core.IO @@ -92,7 +87,8 @@ namespace Umbraco.Core.IO { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentNullOrEmptyException(nameof(filename)); + if (filename == null) throw new ArgumentNullException(nameof(filename)); + if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); if (filestream == null) throw new ArgumentNullException(nameof(filestream)); // clear the old file, if any @@ -111,7 +107,8 @@ namespace Umbraco.Core.IO { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentNullOrEmptyException(nameof(sourcepath)); + if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath)); + if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); // ensure we have a file to copy if (FileExists(sourcepath) == false) return null; diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 1cff75a3b2..96aaf7ca27 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; using System.Threading; using Umbraco.Core.Logging; @@ -39,9 +38,11 @@ namespace Umbraco.Core.IO public PhysicalFileSystem(string rootPath, string rootUrl) { - if (string.IsNullOrEmpty(rootPath)) throw new ArgumentNullOrEmptyException(nameof(rootPath)); - if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentNullOrEmptyException(nameof(rootUrl)); - if (rootPath.StartsWith("~/")) throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'"); + if (rootPath == null) throw new ArgumentNullException(nameof(rootPath)); + if (string.IsNullOrEmpty(rootPath)) throw new ArgumentException("Value can't be empty.", nameof(rootPath)); + if (rootUrl == null) throw new ArgumentNullException(nameof(rootUrl)); + if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl)); + if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath)); // rootPath should be... rooted, as in, it's a root path! if (Path.IsPathRooted(rootPath) == false) diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs index efd9e92b1f..1ecc738b95 100644 --- a/src/Umbraco.Core/Manifest/ManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ManifestParser.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text; using Newtonsoft.Json; using Umbraco.Core.Cache; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; @@ -42,7 +41,8 @@ namespace Umbraco.Core.Manifest _cache = appCaches.RuntimeCache; _validators = validators ?? throw new ArgumentNullException(nameof(validators)); _filters = filters ?? throw new ArgumentNullException(nameof(filters)); - if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullOrEmptyException(nameof(path)); + if (path == null) throw new ArgumentNullException(nameof(path)); + if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path)); Path = path; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -155,8 +155,8 @@ namespace Umbraco.Core.Manifest /// internal PackageManifest ParseManifest(string text) { - if (string.IsNullOrWhiteSpace(text)) - throw new ArgumentNullOrEmptyException(nameof(text)); + if (text == null) throw new ArgumentNullException(nameof(text)); + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); var manifest = JsonConvert.DeserializeObject(text, new DataEditorConverter(_logger), diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs index 9a4f437f62..65c15456a5 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs @@ -1,4 +1,4 @@ -using Umbraco.Core.Exceptions; +using System; using Umbraco.Core.Migrations.Expressions.Common; using Umbraco.Core.Migrations.Expressions.Delete.Column; using Umbraco.Core.Migrations.Expressions.Delete.Constraint; @@ -39,8 +39,9 @@ namespace Umbraco.Core.Migrations.Expressions.Delete /// public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true) { - if (tableName.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(tableName)); + if (tableName == null) throw new ArgumentNullException(nameof(tableName)); + if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(tableName)); + return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeleteLocal = local, DeleteForeign = foreign }; } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs index d86c682bd5..d5d8bbab6f 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Xml.Linq; using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Upgrade; @@ -278,8 +277,10 @@ namespace Umbraco.Core.Migrations.Install /// A logger. private static void SaveConnectionString(string connectionString, string providerName, ILogger logger) { - if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString)); - if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName)); + if (connectionString == null) throw new ArgumentNullException(nameof(connectionString)); + if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionString)); + if (providerName == null) throw new ArgumentNullException(nameof(providerName)); + if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(providerName)); var fileSource = "web.config"; var fileName = IOHelper.MapPath(SystemDirectories.Root +"/" + fileSource); diff --git a/src/Umbraco.Core/Migrations/MigrationPlan.cs b/src/Umbraco.Core/Migrations/MigrationPlan.cs index 37d1a03a5a..89c3c809e8 100644 --- a/src/Umbraco.Core/Migrations/MigrationPlan.cs +++ b/src/Umbraco.Core/Migrations/MigrationPlan.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Scoping; using Type = System.Type; @@ -25,7 +24,9 @@ namespace Umbraco.Core.Migrations /// The name of the plan. public MigrationPlan(string name) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + Name = name; } @@ -43,7 +44,8 @@ namespace Umbraco.Core.Migrations private MigrationPlan Add(string sourceState, string targetState, Type migration) { if (sourceState == null) throw new ArgumentNullException(nameof(sourceState)); - if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentNullOrEmptyException(nameof(targetState)); + if (targetState == null) throw new ArgumentNullException(nameof(targetState)); + if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(targetState)); if (sourceState == targetState) throw new ArgumentException("Source and target state cannot be identical."); if (migration == null) throw new ArgumentNullException(nameof(migration)); if (!migration.Implements()) throw new ArgumentException($"Type {migration.Name} does not implement IMigration.", nameof(migration)); @@ -135,10 +137,12 @@ namespace Umbraco.Core.Migrations /// public MigrationPlan ToWithClone(string startState, string endState, string targetState) { - if (string.IsNullOrWhiteSpace(startState)) throw new ArgumentNullOrEmptyException(nameof(startState)); - if (string.IsNullOrWhiteSpace(endState)) throw new ArgumentNullOrEmptyException(nameof(endState)); - if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentNullOrEmptyException(nameof(targetState)); - + if (startState == null) throw new ArgumentNullException(nameof(startState)); + if (string.IsNullOrWhiteSpace(startState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(startState)); + if (endState == null) throw new ArgumentNullException(nameof(endState)); + if (string.IsNullOrWhiteSpace(endState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(endState)); + if (targetState == null) throw new ArgumentNullException(nameof(targetState)); + if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(targetState)); if (startState == endState) throw new ArgumentException("Start and end states cannot be identical."); startState = startState.Trim(); @@ -317,7 +321,7 @@ namespace Umbraco.Core.Migrations // throw a raw exception here: this should never happen as the plan has // been validated - this is just a paranoid safety test if (!_transitions.TryGetValue(origState, out transition)) - throw new Exception($"Unknown state \"{origState}\"."); + throw new InvalidOperationException($"Unknown state \"{origState}\"."); } // prepare and de-duplicate post-migrations, only keeping the 1st occurence @@ -339,7 +343,7 @@ namespace Umbraco.Core.Migrations // safety check - again, this should never happen as the plan has been validated, // and this is just a paranoid safety test if (origState != _finalState) - throw new Exception($"Internal error, reached state {origState} which is not final state {_finalState}"); + throw new InvalidOperationException($"Internal error, reached state {origState} which is not final state {_finalState}"); return origState; } @@ -358,7 +362,7 @@ namespace Umbraco.Core.Migrations var states = new List { origState }; if (!_transitions.TryGetValue(origState, out var transition)) - throw new Exception($"Unknown state \"{origState}\"."); + throw new InvalidOperationException($"Unknown state \"{origState}\"."); while (transition != null) { @@ -373,12 +377,12 @@ namespace Umbraco.Core.Migrations } if (!_transitions.TryGetValue(origState, out transition)) - throw new Exception($"Unknown state \"{origState}\"."); + throw new InvalidOperationException($"Unknown state \"{origState}\"."); } // safety check if (origState != (toState ?? _finalState)) - throw new Exception($"Internal error, reached state {origState} which is not state {toState ?? _finalState}"); + throw new InvalidOperationException($"Internal error, reached state {origState} which is not state {toState ?? _finalState}"); return states; } @@ -417,7 +421,7 @@ namespace Umbraco.Core.Migrations public override string ToString() { return MigrationType == typeof(NoopMigration) - ? $"{(SourceState == "" ? "" : SourceState)} --> {TargetState}" + ? $"{(SourceState == string.Empty ? "" : SourceState)} --> {TargetState}" : $"{SourceState} -- ({MigrationType.FullName}) --> {TargetState}"; } } diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index fbb68194b7..d02ea82012 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -4,8 +4,6 @@ using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using System.Runtime.Serialization; -using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -229,8 +227,8 @@ namespace Umbraco.Core.Models private void ClearCultureInfo(string culture) { - if (culture.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); if (_cultureInfos == null) return; _cultureInfos.Remove(culture); diff --git a/src/Umbraco.Core/Models/ContentCultureInfos.cs b/src/Umbraco.Core/Models/ContentCultureInfos.cs index c8c4bea56a..2f9c08b985 100644 --- a/src/Umbraco.Core/Models/ContentCultureInfos.cs +++ b/src/Umbraco.Core/Models/ContentCultureInfos.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Umbraco.Core.Exceptions; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -18,7 +17,9 @@ namespace Umbraco.Core.Models /// public ContentCultureInfos(string culture) { - if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); + Culture = culture; } diff --git a/src/Umbraco.Core/Models/ContentCultureInfosCollection.cs b/src/Umbraco.Core/Models/ContentCultureInfosCollection.cs index 52f6f9adb6..9bc78fc56d 100644 --- a/src/Umbraco.Core/Models/ContentCultureInfosCollection.cs +++ b/src/Umbraco.Core/Models/ContentCultureInfosCollection.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using Umbraco.Core.Collections; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models { @@ -23,7 +21,9 @@ namespace Umbraco.Core.Models /// public void AddOrUpdate(string culture, string name, DateTime date) { - if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); + culture = culture.ToLowerInvariant(); if (TryGetValue(culture, out var item)) diff --git a/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs b/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs index bf7228ca47..644e60a961 100644 --- a/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs +++ b/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models { @@ -103,11 +102,11 @@ namespace Umbraco.Core.Models public static void SetPublishInfo(this IContent content, string culture, string name, DateTime date) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); - if (culture.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); content.PublishCultureInfos.AddOrUpdate(culture, name, date); } @@ -153,11 +152,11 @@ namespace Umbraco.Core.Models public static void SetCultureInfo(this IContentBase content, string culture, string name, DateTime date) { - if (name.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); - if (culture.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); content.CultureInfos.AddOrUpdate(culture, name, date); } @@ -249,8 +248,8 @@ namespace Umbraco.Core.Models public static void ClearPublishInfo(this IContent content, string culture) { - if (culture.IsNullOrWhiteSpace()) - throw new ArgumentNullOrEmptyException(nameof(culture)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture)); content.PublishCultureInfos.Remove(culture); diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index b473a154f1..49e07a486d 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; namespace Umbraco.Core.Models @@ -43,7 +42,8 @@ namespace Umbraco.Core.Models public Member(string name, IMemberType contentType) : base(name, -1, contentType, new PropertyCollection()) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); IsApproved = true; @@ -63,9 +63,12 @@ namespace Umbraco.Core.Models public Member(string name, string email, string username, IMemberType contentType, bool isApproved = true) : base(name, -1, contentType, new PropertyCollection()) { - if (string.IsNullOrWhiteSpace(email)) throw new ArgumentNullOrEmptyException(nameof(email)); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullOrEmptyException(nameof(username)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + if (email == null) throw new ArgumentNullException(nameof(email)); + if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(email)); + if (username == null) throw new ArgumentNullException(nameof(username)); + if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(username)); _email = email; _username = username; diff --git a/src/Umbraco.Core/Models/PublishedContent/ModelType.cs b/src/Umbraco.Core/Models/PublishedContent/ModelType.cs index 540abda2c5..6f42715f16 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ModelType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ModelType.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models.PublishedContent { @@ -20,7 +19,9 @@ namespace Umbraco.Core.Models.PublishedContent { private ModelType(string contentTypeAlias) { - if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias)); + if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias)); + if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias)); + ContentTypeAlias = contentTypeAlias; Name = "{" + ContentTypeAlias + "}"; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index d5096158a7..908b97fc36 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models.PublishedContent { @@ -13,7 +12,8 @@ namespace Umbraco.Core.Models.PublishedContent /// public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); Culture = culture ?? throw new ArgumentNullException(nameof(culture)); Name = name; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs index 16eb614ee7..3f2c63a78f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedModelAttribute.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models.PublishedContent { @@ -19,7 +18,9 @@ namespace Umbraco.Core.Models.PublishedContent /// The content type alias. public PublishedModelAttribute(string contentTypeAlias) { - if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias)); + if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias)); + if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias)); + ContentTypeAlias = contentTypeAlias; } diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs index 259b7bc4ef..725628bf90 100644 --- a/src/Umbraco.Core/Models/RelationType.cs +++ b/src/Umbraco.Core/Models/RelationType.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.Serialization; -using Umbraco.Core.Exceptions; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -20,7 +19,9 @@ namespace Umbraco.Core.Models public RelationType(Guid childObjectType, Guid parentObjectType, string alias) { - if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias)); + if (alias == null) throw new ArgumentNullException(nameof(alias)); + if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias)); + _childObjectType = childObjectType; _parentObjectType = parentObjectType; _alias = alias; @@ -30,7 +31,9 @@ namespace Umbraco.Core.Models public RelationType(Guid childObjectType, Guid parentObjectType, string alias, string name) : this(childObjectType, parentObjectType, alias) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + Name = name; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs index 09fe949df1..89a62d997e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using NPoco; using Umbraco.Core.Cache; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; @@ -129,6 +128,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistDeletedItem(EntityContainer entity) { + if (entity == null) throw new ArgumentNullException(nameof(entity)); EnsureContainerType(entity); var nodeDto = Database.FirstOrDefault(Sql().SelectAll() @@ -162,9 +162,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistNewItem(EntityContainer entity) { + // TODO Ensure correct exceptions are thrown (entity.Name is not an argument and NullReferenceException shouldn't be thrown by user code) + if (entity == null) throw new ArgumentNullException(nameof(entity)); EnsureContainerType(entity); - if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentNullOrEmptyException("entity.Name"); + if (entity.Name == null) throw new ArgumentNullException(nameof(entity.Name)); + if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(entity.Name)); entity.Name = entity.Name.Trim(); // guard against duplicates @@ -223,10 +226,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // protected override void PersistUpdatedItem(EntityContainer entity) { + // TODO Ensure correct exceptions are thrown (entity.Name is not an argument and NullReferenceException shouldn't be thrown by user code) + if (entity == null) throw new ArgumentNullException(nameof(entity)); EnsureContainerType(entity); + if (entity.Name == null) throw new ArgumentNullException(nameof(entity.Name)); + if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(entity.Name)); entity.Name = entity.Name.Trim(); - if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentNullOrEmptyException("entity.Name"); // find container to update var nodeDto = Database.FirstOrDefault(Sql().SelectAll() diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 13422f43b1..c502abc87c 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -4,7 +4,6 @@ using System.Data.Common; using System.Threading; using NPoco; using NPoco.FluentMappings; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.FaultHandling; using Umbraco.Core.Persistence.Mappers; @@ -61,8 +60,8 @@ namespace Umbraco.Core.Persistence /// Used by the other ctor and in tests. public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy mappers) { - if (string.IsNullOrWhiteSpace(connectionStringName)) - throw new ArgumentNullOrEmptyException(nameof(connectionStringName)); + if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName)); + if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionStringName)); _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs index d90aeef2e6..1852c742f0 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationFieldAttribute.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.PropertyEditors { @@ -28,13 +27,15 @@ namespace Umbraco.Core.PropertyEditors /// The view to use to render the field editor. public ConfigurationFieldAttribute(string key, string name, string view) { - if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullOrEmptyException(nameof(key)); + if (key == null) throw new ArgumentNullException(nameof(key)); + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(key)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + if (view == null) throw new ArgumentNullException(nameof(view)); + if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view)); + Key = key; - - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); Name = name; - - if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view)); View = view; } @@ -47,10 +48,12 @@ namespace Umbraco.Core.PropertyEditors /// from the name of the property marked with this attribute. public ConfigurationFieldAttribute(string name, string view) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - Name = name; + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + if (view == null) throw new ArgumentNullException(nameof(view)); + if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view)); - if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view)); + Name = name; View = view; } diff --git a/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs index ca08127d51..f780cdd3d7 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.PropertyEditors { @@ -53,17 +52,17 @@ namespace Umbraco.Core.PropertyEditors /// public DataEditorAttribute(string alias, EditorType type, string name, string view) { - if ((type & ~(EditorType.PropertyValue | EditorType.MacroParameter)) > 0) - throw new ArgumentOutOfRangeException(nameof(type), $"Not a valid {typeof(EditorType)} value."); + if (alias == null) throw new ArgumentNullException(nameof(alias)); + if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias)); + if ((type & ~(EditorType.PropertyValue | EditorType.MacroParameter)) > 0) throw new ArgumentOutOfRangeException(nameof(type), type, $"Not a valid {typeof(EditorType)} value."); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); + if (view == null) throw new ArgumentNullException(nameof(view)); + if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view)); + Type = type; - - if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias)); Alias = alias; - - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); Name = name; - - if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view)); View = view == NullView ? null : view; } @@ -100,8 +99,10 @@ namespace Umbraco.Core.PropertyEditors get => _valueType; set { - if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullOrEmptyException(nameof(value)); - if (!ValueTypes.IsValue(value)) throw new ArgumentOutOfRangeException(nameof(value), $"Not a valid {typeof(ValueTypes)} value."); + if (value == null) throw new ArgumentNullException(nameof(value)); + if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(value)); + if (!ValueTypes.IsValue(value)) throw new ArgumentOutOfRangeException(nameof(value), value, $"Not a valid {typeof(ValueTypes)} value."); + _valueType = value; } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs index e405fa3a3e..bc1572445e 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; using Umbraco.Core.Services; namespace Umbraco.Core.PropertyEditors.Validators @@ -48,8 +47,9 @@ namespace Umbraco.Core.PropertyEditors.Validators get => _regex; set { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentNullOrEmptyException(nameof(value)); + if (value == null) throw new ArgumentNullException(nameof(value)); + if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(value)); + _regex = value; } } @@ -66,7 +66,9 @@ namespace Umbraco.Core.PropertyEditors.Validators /// public IEnumerable ValidateFormat(object value, string valueType, string format) { - if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullOrEmptyException(nameof(format)); + if (format == null) throw new ArgumentNullException(nameof(format)); + if (string.IsNullOrWhiteSpace(format)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(format)); + if (value == null || !new Regex(format).IsMatch(value.ToString())) yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" }); } diff --git a/src/Umbraco.Core/ReflectionUtilities.cs b/src/Umbraco.Core/ReflectionUtilities.cs index 622d81f5f2..a8e6836ca1 100644 --- a/src/Umbraco.Core/ReflectionUtilities.cs +++ b/src/Umbraco.Core/ReflectionUtilities.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using Umbraco.Core.Exceptions; namespace Umbraco.Core { @@ -29,10 +28,14 @@ namespace Umbraco.Core /// The declaring type. /// The field type. /// The name of the field. - /// A field getter function. - /// Occurs when is null or empty. - /// Occurs when the field does not exist. - /// Occurs when does not match the type of the field. + /// + /// A field getter function. + /// + /// fieldName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match field . type. + /// Could not find field .. public static Func EmitFieldGetter(string fieldName) { var field = GetField(fieldName); @@ -45,10 +48,14 @@ namespace Umbraco.Core /// The declaring type. /// The field type. /// The name of the field. - /// A field setter action. - /// Occurs when is null or empty. - /// Occurs when the field does not exist. - /// Occurs when does not match the type of the field. + /// + /// A field setter action. + /// + /// fieldName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match field . type. + /// Could not find field .. public static Action EmitFieldSetter(string fieldName) { var field = GetField(fieldName); @@ -61,19 +68,36 @@ namespace Umbraco.Core /// The declaring type. /// The field type. /// The name of the field. - /// A field getter and setter functions. - /// Occurs when is null or empty. - /// Occurs when the field does not exist. - /// Occurs when does not match the type of the field. + /// + /// A field getter and setter functions. + /// + /// fieldName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match field . type. + /// Could not find field .. public static (Func, Action) EmitFieldGetterAndSetter(string fieldName) { var field = GetField(fieldName); return (EmitFieldGetter(field), EmitFieldSetter(field)); } + /// + /// Gets the field. + /// + /// The type of the declaring. + /// The type of the value. + /// Name of the field. + /// + /// fieldName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match field . type. + /// Could not find field .. private static FieldInfo GetField(string fieldName) { - if (string.IsNullOrWhiteSpace(fieldName)) throw new ArgumentNullOrEmptyException(nameof(fieldName)); + if (fieldName == null) throw new ArgumentNullException(nameof(fieldName)); + if (string.IsNullOrWhiteSpace(fieldName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(fieldName)); // get the field var field = typeof(TDeclaring).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); @@ -120,13 +144,18 @@ namespace Umbraco.Core /// The property type. /// The name of the property. /// A value indicating whether the property and its getter must exist. - /// A property getter function. If is false, returns null when the property or its getter does not exist. - /// Occurs when is null or empty. - /// Occurs when the property or its getter does not exist. - /// Occurs when does not match the type of the property. + /// + /// A property getter function. If is false, returns null when the property or its getter does not exist. + /// + /// propertyName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match property . type. + /// Could not find property getter for .. public static Func EmitPropertyGetter(string propertyName, bool mustExist = true) { - if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName)); + if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); + if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName)); var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); @@ -146,13 +175,18 @@ namespace Umbraco.Core /// The property type. /// The name of the property. /// A value indicating whether the property and its setter must exist. - /// A property setter function. If is false, returns null when the property or its setter does not exist. - /// Occurs when is null or empty. - /// Occurs when the property or its setter does not exist. - /// Occurs when does not match the type of the property. + /// + /// A property setter function. If is false, returns null when the property or its setter does not exist. + /// + /// propertyName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match property . type. + /// Could not find property setter for .. public static Action EmitPropertySetter(string propertyName, bool mustExist = true) { - if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName)); + if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); + if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName)); var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); @@ -172,13 +206,18 @@ namespace Umbraco.Core /// The property type. /// The name of the property. /// A value indicating whether the property and its getter and setter must exist. - /// A property getter and setter functions. If is false, returns null when the property or its getter or setter does not exist. - /// Occurs when is null or empty. - /// Occurs when the property or its getter or setter does not exist. - /// Occurs when does not match the type of the property. + /// + /// A property getter and setter functions. If is false, returns null when the property or its getter or setter does not exist. + /// + /// propertyName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Value type does not match property . type. + /// Could not find property getter and setter for .. public static (Func, Action) EmitPropertyGetterAndSetter(string propertyName, bool mustExist = true) { - if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName)); + if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); + if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName)); var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); @@ -402,13 +441,17 @@ namespace Umbraco.Core /// A lambda representing the method. /// The name of the method. /// A value indicating whether the constructor must exist. - /// The method. If is false, returns null when the method does not exist. + /// + /// The method. If is false, returns null when the method does not exist. + /// + /// methodName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Occurs when does not match the method signature.. + /// Occurs when no proper method with name could be found. /// - /// The method arguments are determined by generic arguments. + /// The method arguments are determined by generic arguments. /// - /// Occurs when is null or empty. - /// Occurs when no proper method with name could be found. - /// Occurs when Occurs when does not match the method signature. public static TLambda EmitMethod(string methodName, bool mustExist = true) { return EmitMethod(typeof(TDeclaring), methodName, mustExist); @@ -421,16 +464,21 @@ namespace Umbraco.Core /// The declaring type. /// The name of the method. /// A value indicating whether the constructor must exist. - /// The method. If is false, returns null when the method does not exist. + /// + /// The method. If is false, returns null when the method does not exist. + /// + /// methodName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Occurs when does not match the method signature.. + /// Occurs when no proper method with name could be found. /// - /// The method arguments are determined by generic arguments. + /// The method arguments are determined by generic arguments. /// - /// Occurs when is null or empty. - /// Occurs when no proper method with name could be found. - /// Occurs when Occurs when does not match the method signature. public static TLambda EmitMethod(Type declaring, string methodName, bool mustExist = true) { - if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentNullOrEmptyException(nameof(methodName)); + if (methodName == null) throw new ArgumentNullException(nameof(methodName)); + if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(methodName)); var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda(true, out var isFunction); @@ -510,17 +558,21 @@ namespace Umbraco.Core /// A lambda representing the method. /// The name of the method. /// A value indicating whether the constructor must exist. - /// The method. If is false, returns null when the method does not exist. + /// + /// The method. If is false, returns null when the method does not exist. + /// + /// methodName + /// Value can't be empty or consist only of white-space characters. - + /// or + /// Occurs when does not match the method signature.. + /// Occurs when no proper method with name could be found. /// - /// The method arguments are determined by generic arguments. + /// The method arguments are determined by generic arguments. /// - /// Occurs when is null or empty. - /// Occurs when no proper method with name could be found. - /// Occurs when Occurs when does not match the method signature. public static TLambda EmitMethod(string methodName, bool mustExist = true) { - if (string.IsNullOrWhiteSpace(methodName)) - throw new ArgumentNullOrEmptyException(nameof(methodName)); + if (methodName == null) throw new ArgumentNullException(nameof(methodName)); + if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(methodName)); // validate lambda type var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda(false, out var isFunction); diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index 085a7b7a5b..dc271452e1 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using System.Web.Security; using Microsoft.AspNet.Identity; using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; @@ -217,7 +216,8 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException(nameof(user)); - if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullOrEmptyException(nameof(passwordHash)); + if (passwordHash == null) throw new ArgumentNullException(nameof(passwordHash)); + if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentException("Value can't be empty.", nameof(passwordHash)); user.PasswordHash = passwordHash; @@ -329,7 +329,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException(nameof(user)); - if (login == null) throw new ArgumentNullException("login"); + if (login == null) throw new ArgumentNullException(nameof(login)); var logins = user.Logins; var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id); @@ -348,7 +348,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException(nameof(user)); - if (login == null) throw new ArgumentNullException("login"); + if (login == null) throw new ArgumentNullException(nameof(login)); var provider = login.LoginProvider; var key = login.ProviderKey; @@ -379,7 +379,7 @@ namespace Umbraco.Core.Security public Task FindAsync(UserLoginInfo login) { ThrowIfDisposed(); - if (login == null) throw new ArgumentNullException("login"); + if (login == null) throw new ArgumentNullException(nameof(login)); //get all logins associated with the login id var result = _externalLoginService.Find(login).ToArray(); @@ -413,7 +413,8 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException(nameof(user)); - if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName"); + if (roleName == null) throw new ArgumentNullException(nameof(roleName)); + if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(roleName)); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName); @@ -434,7 +435,8 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException(nameof(user)); - if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName"); + if (roleName == null) throw new ArgumentNullException(nameof(roleName)); + if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(roleName)); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName); diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index e49dcf4a12..4d6400a38f 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; -using System.Text.RegularExpressions; using Umbraco.Core.Events; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -2793,7 +2791,8 @@ namespace Umbraco.Core.Services.Implement private IContentType GetContentType(IScope scope, string contentTypeAlias) { - if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias)); + if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias)); + if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias)); scope.ReadLock(Constants.Locks.ContentTypes); @@ -2808,7 +2807,8 @@ namespace Umbraco.Core.Services.Implement private IContentType GetContentType(string contentTypeAlias) { - if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias)); + if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias)); + if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias)); using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -2900,7 +2900,7 @@ namespace Umbraco.Core.Services.Implement { foreach (var property in blueprint.Properties) { - var propertyCulture = property.PropertyType.VariesByCulture() ? culture : null; + var propertyCulture = property.PropertyType.VariesByCulture() ? culture : null; content.SetValue(property.Alias, property.GetValue(propertyCulture), propertyCulture); } diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 1f1f0d9ac3..f14fdb6c42 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -590,10 +590,9 @@ namespace Umbraco.Core.Services.Implement public TItem Copy(TItem original, string alias, string name, TItem parent) { if (original == null) throw new ArgumentNullException(nameof(original)); - if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias)); - - if (parent != null && parent.HasIdentity == false) - throw new InvalidOperationException("Parent must have an identity."); + if (alias == null) throw new ArgumentNullException(nameof(alias)); + if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias)); + if (parent != null && parent.HasIdentity == false) throw new InvalidOperationException("Parent must have an identity."); // this is illegal //var originalb = (ContentTypeCompositionBase)original; diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index ab075c4ade..b1a9601fac 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using System.Linq; using Umbraco.Core.Events; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -1323,7 +1322,8 @@ namespace Umbraco.Core.Services.Implement private IMediaType GetMediaType(string mediaTypeAlias) { - if (string.IsNullOrWhiteSpace(mediaTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(mediaTypeAlias)); + if (mediaTypeAlias == null) throw new ArgumentNullException(nameof(mediaTypeAlias)); + if (string.IsNullOrWhiteSpace(mediaTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(mediaTypeAlias)); using (var scope = ScopeProvider.CreateScope()) { @@ -1333,7 +1333,7 @@ namespace Umbraco.Core.Services.Implement var mediaType = _mediaTypeRepository.Get(query).FirstOrDefault(); if (mediaType == null) - throw new Exception($"No MediaType matching the passed in Alias: '{mediaTypeAlias}' was found"); // causes rollback // causes rollback + throw new InvalidOperationException($"No media type matched the specified alias '{mediaTypeAlias}'."); scope.Complete(); return mediaType; diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 8c69664712..29eda5bb0b 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -1344,7 +1344,8 @@ namespace Umbraco.Core.Services.Implement private IMemberType GetMemberType(IScope scope, string memberTypeAlias) { - if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(memberTypeAlias)); + if (memberTypeAlias == null) throw new ArgumentNullException(nameof(memberTypeAlias)); + if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(memberTypeAlias)); scope.ReadLock(Constants.Locks.MemberTypes); @@ -1358,7 +1359,8 @@ namespace Umbraco.Core.Services.Implement private IMemberType GetMemberType(string memberTypeAlias) { - if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(memberTypeAlias)); + if (memberTypeAlias == null) throw new ArgumentNullException(nameof(memberTypeAlias)); + if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(memberTypeAlias)); using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Core/Services/Implement/UserService.cs b/src/Umbraco.Core/Services/Implement/UserService.cs index 0ea77dedcc..363bc72bc3 100644 --- a/src/Umbraco.Core/Services/Implement/UserService.cs +++ b/src/Umbraco.Core/Services/Implement/UserService.cs @@ -1,15 +1,11 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Data.Common; -using System.Data.SqlClient; -using System.Data.SqlServerCe; using System.Globalization; using System.Linq; using System.Linq.Expressions; using Umbraco.Core.Configuration; using Umbraco.Core.Events; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -17,7 +13,6 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; -using Umbraco.Core.Security; namespace Umbraco.Core.Services.Implement { @@ -107,7 +102,8 @@ namespace Umbraco.Core.Services.Implement /// private IUser CreateUserWithIdentity(string username, string email, string passwordValue, bool isApproved = true) { - if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullOrEmptyException(nameof(username)); + if (username == null) throw new ArgumentNullException(nameof(username)); + if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(username)); // TODO: PUT lock here!! diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 4df1105bf7..94b9b5617a 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -10,9 +10,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Web.Security; using Newtonsoft.Json; -using Umbraco.Core.Configuration; using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Strings; @@ -1102,7 +1100,8 @@ namespace Umbraco.Core /// The safe url segment. public static string ToUrlSegment(this string text) { - if (string.IsNullOrWhiteSpace(text)) throw new ArgumentNullOrEmptyException(nameof(text)); + if (text == null) throw new ArgumentNullException(nameof(text)); + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); return Current.ShortStringHelper.CleanStringForUrlSegment(text); } @@ -1115,7 +1114,8 @@ namespace Umbraco.Core /// The safe url segment. public static string ToUrlSegment(this string text, string culture) { - if (string.IsNullOrWhiteSpace(text)) throw new ArgumentNullOrEmptyException(nameof(text)); + if (text == null) throw new ArgumentNullException(nameof(text)); + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); } diff --git a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs b/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs index b31fa6a8df..24fb070663 100644 --- a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs +++ b/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Umbraco.Core.Exceptions; namespace Umbraco.Core.Xml { @@ -37,7 +34,8 @@ namespace Umbraco.Core.Xml // allowed 'inline', not just at the beginning... whether or not we want to support that is up // for discussion. - if (string.IsNullOrWhiteSpace(xpathExpression)) throw new ArgumentNullOrEmptyException(nameof(xpathExpression)); + if (xpathExpression == null) throw new ArgumentNullException(nameof(xpathExpression)); + if (string.IsNullOrWhiteSpace(xpathExpression)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(xpathExpression)); if (getPath == null) throw new ArgumentNullException(nameof(getPath)); if (publishedContentExists == null) throw new ArgumentNullException(nameof(publishedContentExists)); diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 3fd8dcaea5..8d0399397e 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -4,9 +4,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml; -using System.Xml.Linq; using System.Xml.XPath; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; namespace Umbraco.Core.Xml @@ -25,9 +23,10 @@ namespace Umbraco.Core.Xml /// public static void SetAttribute(XmlDocument xml, XmlNode n, string name, string value) { - if (xml == null) throw new ArgumentNullException("xml"); - if (n == null) throw new ArgumentNullException("n"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xml == null) throw new ArgumentNullException(nameof(xml)); + if (n == null) throw new ArgumentNullException(nameof(n)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); if (n.Attributes == null) { @@ -229,8 +228,9 @@ namespace Umbraco.Core.Xml /// a XmlAttribute public static XmlAttribute AddAttribute(XmlDocument xd, string name, string value) { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrEmpty(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xd == null) throw new ArgumentNullException(nameof(xd)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrEmpty(name)) throw new ArgumentException("Value can't be empty.", nameof(name)); var temp = xd.CreateAttribute(name); temp.Value = value; @@ -246,8 +246,9 @@ namespace Umbraco.Core.Xml /// a XmlNode public static XmlNode AddTextNode(XmlDocument xd, string name, string value) { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xd == null) throw new ArgumentNullException(nameof(xd)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var temp = xd.CreateNode(XmlNodeType.Element, name, ""); temp.AppendChild(xd.CreateTextNode(value)); @@ -264,9 +265,10 @@ namespace Umbraco.Core.Xml /// a XmlNode public static XmlNode SetTextNode(XmlDocument xd, XmlNode parent, string name, string value) { - if (xd == null) throw new ArgumentNullException("xd"); - if (parent == null) throw new ArgumentNullException("parent"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xd == null) throw new ArgumentNullException(nameof(xd)); + if (parent == null) throw new ArgumentNullException(nameof(parent)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var child = parent.SelectSingleNode(name); if (child != null) @@ -289,7 +291,8 @@ namespace Umbraco.Core.Xml { if (xd == null) throw new ArgumentNullException(nameof(xd)); if (parent == null) throw new ArgumentNullException(nameof(parent)); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var child = parent.SelectSingleNode(name) ?? xd.CreateNode(XmlNodeType.Element, name, ""); child.InnerXml = value; @@ -305,8 +308,9 @@ namespace Umbraco.Core.Xml /// A XmlNode public static XmlNode AddCDataNode(XmlDocument xd, string name, string value) { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xd == null) throw new ArgumentNullException(nameof(xd)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var temp = xd.CreateNode(XmlNodeType.Element, name, ""); temp.AppendChild(xd.CreateCDataSection(value)); @@ -323,9 +327,10 @@ namespace Umbraco.Core.Xml /// a XmlNode public static XmlNode SetCDataNode(XmlDocument xd, XmlNode parent, string name, string value) { - if (xd == null) throw new ArgumentNullException("xd"); - if (parent == null) throw new ArgumentNullException("parent"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + if (xd == null) throw new ArgumentNullException(nameof(xd)); + if (parent == null) throw new ArgumentNullException(nameof(parent)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var child = parent.SelectSingleNode(name); if (child != null) diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index cce54c81a4..a96385a923 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -865,7 +865,7 @@ namespace Umbraco.Tests.Services var userService = ServiceContext.UserService; // Act & Assert - Assert.Throws(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io")); + Assert.Throws(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io")); } [Test] diff --git a/src/Umbraco.Web/GridTemplateExtensions.cs b/src/Umbraco.Web/GridTemplateExtensions.cs index 2c6e66c68b..0bc45e265e 100644 --- a/src/Umbraco.Web/GridTemplateExtensions.cs +++ b/src/Umbraco.Web/GridTemplateExtensions.cs @@ -1,13 +1,10 @@ using System; -using System.Web.Mvc.Html; using System.Web.Mvc; -using System.IO; -using Umbraco.Core.Exceptions; +using System.Web.Mvc.Html; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web { - public static class GridTemplateExtensions { public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedProperty property, string framework = "bootstrap3") @@ -26,18 +23,20 @@ namespace Umbraco.Web public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias) { - if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias)); + if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); + if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias)); return html.GetGridHtml(contentItem, propertyAlias, "bootstrap3"); } public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string framework) { - if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias)); + if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); + if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias)); var view = "Grid/" + framework; var prop = contentItem.GetProperty(propertyAlias); - if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); + if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); // TODO NullReferenceException should not be thrown by user code var model = prop.GetValue(); var asString = model as string; @@ -60,17 +59,19 @@ namespace Umbraco.Web } public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias) { - if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias)); + if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); + if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias)); return GetGridHtml(contentItem, html, propertyAlias, "bootstrap3"); } public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework) { - if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias)); + if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); + if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias)); var view = "Grid/" + framework; var prop = contentItem.GetProperty(propertyAlias); - if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); + if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); // TODO NullReferenceException should not be thrown by user code var model = prop.GetValue(); var asString = model as string; @@ -78,12 +79,5 @@ namespace Umbraco.Web return html.Partial(view, model); } - - private class FakeView : IView - { - public void Render(ViewContext viewContext, TextWriter writer) - { - } - } } } diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 7249500441..18f74db569 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -8,11 +8,7 @@ using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; using Umbraco.Core.IO; -using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Security; using Current = Umbraco.Web.Composing.Current; @@ -169,8 +165,9 @@ namespace Umbraco.Web /// public static IHtmlString Action(this HtmlHelper htmlHelper, string actionName, Type surfaceType) { + if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName)); if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); - if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName)); var routeVals = new RouteValueDictionary(new {area = ""}); @@ -221,7 +218,7 @@ namespace Umbraco.Web { _viewContext = viewContext; _method = method; - _controllerName = controllerName; + _controllerName = controllerName; _encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals); } @@ -230,7 +227,7 @@ namespace Umbraco.Web private readonly FormMethod _method; private bool _disposed; private readonly string _encryptedString; - private readonly string _controllerName; + private readonly string _controllerName; protected override void Dispose(bool disposing) { @@ -243,9 +240,9 @@ namespace Umbraco.Web || _controllerName == "UmbProfile" || _controllerName == "UmbLoginStatus" || _controllerName == "UmbLogin") - { + { _viewContext.Writer.Write(AntiForgery.GetHtml().ToString()); - } + } //write out the hidden surface form routes _viewContext.Writer.Write(""); @@ -355,8 +352,10 @@ namespace Umbraco.Web IDictionary htmlAttributes, FormMethod method) { - if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action)); - if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); return html.BeginUmbracoForm(action, controllerName, "", additionalRouteVals, htmlAttributes, method); } @@ -374,8 +373,10 @@ namespace Umbraco.Web object additionalRouteVals, IDictionary htmlAttributes) { - if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action)); - if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName)); return html.BeginUmbracoForm(action, controllerName, "", additionalRouteVals, htmlAttributes); } @@ -575,7 +576,9 @@ namespace Umbraco.Web IDictionary htmlAttributes, FormMethod method) { - if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action)); + + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); var area = ""; @@ -687,8 +690,10 @@ namespace Umbraco.Web IDictionary htmlAttributes, FormMethod method) { - if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); var formAction = Current.UmbracoContext.OriginalRequestUrl.PathAndQuery; return html.RenderForm(formAction, method, htmlAttributes, controllerName, action, area, additionalRouteVals); diff --git a/src/Umbraco.Web/HttpUrlHelperExtensions.cs b/src/Umbraco.Web/HttpUrlHelperExtensions.cs index 2eb45465cf..4e5533d327 100644 --- a/src/Umbraco.Web/HttpUrlHelperExtensions.cs +++ b/src/Umbraco.Web/HttpUrlHelperExtensions.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Linq.Expressions; using System.Web.Http.Routing; using Umbraco.Core; -using Umbraco.Core.Exceptions; using Umbraco.Web.Composing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -53,7 +52,8 @@ namespace Umbraco.Web /// public static string GetUmbracoApiService(this UrlHelper url, string actionName, Type apiControllerType, object id = null) { - if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName)); + if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName)); if (apiControllerType == null) throw new ArgumentNullException(nameof(apiControllerType)); var area = ""; @@ -95,8 +95,10 @@ namespace Umbraco.Web /// public static string GetUmbracoApiService(this UrlHelper url, string actionName, string controllerName, string area, object id = null) { - if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName)); - if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName)); string routeName; if (area.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Web/Models/Trees/TreeNode.cs b/src/Umbraco.Web/Models/Trees/TreeNode.cs index 63614b141f..dc383bd18b 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNode.cs +++ b/src/Umbraco.Web/Models/Trees/TreeNode.cs @@ -1,10 +1,9 @@ -using System.Runtime.Serialization; -using Umbraco.Core.IO; +using System; using System.Collections.Generic; +using System.Runtime.Serialization; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; +using Umbraco.Core.IO; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Trees @@ -27,7 +26,8 @@ namespace Umbraco.Web.Models.Trees /// internal TreeNode(string nodeId, string parentId, string getChildNodesUrl, string menuUrl) { - if (string.IsNullOrWhiteSpace(nodeId)) throw new ArgumentNullOrEmptyException(nameof(nodeId)); + if (nodeId == null) throw new ArgumentNullException(nameof(nodeId)); + if (string.IsNullOrWhiteSpace(nodeId)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(nodeId)); Id = nodeId; ParentId = parentId; diff --git a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs index 61a659615f..493d9deed2 100644 --- a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs +++ b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs @@ -6,7 +6,6 @@ using System.Web.Routing; using System.Web.SessionState; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; using Umbraco.Web.WebApi; namespace Umbraco.Web.Mvc @@ -48,7 +47,8 @@ namespace Umbraco.Web.Mvc bool isMvc = true, string areaPathPrefix = "") { - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); if (controllerSuffixName == null) throw new ArgumentNullException(nameof(controllerSuffixName)); if (controllerType == null) throw new ArgumentNullException(nameof(controllerType)); diff --git a/src/Umbraco.Web/Search/SearchableTreeAttribute.cs b/src/Umbraco.Web/Search/SearchableTreeAttribute.cs index a2311ae989..d81a85bb4b 100644 --- a/src/Umbraco.Web/Search/SearchableTreeAttribute.cs +++ b/src/Umbraco.Web/Search/SearchableTreeAttribute.cs @@ -1,5 +1,4 @@ using System; -using Umbraco.Core.Exceptions; namespace Umbraco.Web.Search { @@ -8,42 +7,47 @@ namespace Umbraco.Web.Search { public const int DefaultSortOrder = 1000; - /// - /// This constructor defines both the angular service and method name to use - /// - /// - /// - public SearchableTreeAttribute(string serviceName, string methodName) : this(serviceName, methodName, DefaultSortOrder) { } - - /// - /// This constructor defines both the angular service and method name to use and explicitly defines a sort order for the results - /// - /// - /// - /// - public SearchableTreeAttribute(string serviceName, string methodName, int sortOrder) - { - if (string.IsNullOrWhiteSpace(serviceName)) throw new ArgumentNullOrEmptyException(nameof(serviceName)); - if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentNullOrEmptyException(nameof(methodName)); - MethodName = methodName; - SortOrder = sortOrder; - ServiceName = serviceName; - } - - /// - /// This constructor will assume that the method name equals `format(searchResult, appAlias, treeAlias)` - /// - /// - public SearchableTreeAttribute(string serviceName) - { - if (string.IsNullOrWhiteSpace(serviceName)) throw new ArgumentNullOrEmptyException(nameof(serviceName)); - MethodName = ""; - ServiceName = serviceName; - SortOrder = DefaultSortOrder; - } + public string ServiceName { get; } public string MethodName { get; } - public string ServiceName { get; } + public int SortOrder { get; } + + /// + /// This constructor will assume that the method name equals `format(searchResult, appAlias, treeAlias)`. + /// + /// Name of the service. + public SearchableTreeAttribute(string serviceName) + : this(serviceName, string.Empty) + { } + + /// + /// This constructor defines both the Angular service and method name to use. + /// + /// Name of the service. + /// Name of the method. + public SearchableTreeAttribute(string serviceName, string methodName) + : this(serviceName, methodName, DefaultSortOrder) + { } + + /// + /// This constructor defines both the Angular service and method name to use and explicitly defines a sort order for the results + /// + /// Name of the service. + /// Name of the method. + /// The sort order. + /// serviceName + /// or + /// methodName + /// Value can't be empty or consist only of white-space characters. - serviceName + public SearchableTreeAttribute(string serviceName, string methodName, int sortOrder) + { + if (serviceName == null) throw new ArgumentNullException(nameof(serviceName)); + if (string.IsNullOrWhiteSpace(serviceName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(serviceName)); + + ServiceName = serviceName; + MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName)); + SortOrder = sortOrder; + } } } diff --git a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs b/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs index 33aabbaf94..6e05a929b3 100644 --- a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs +++ b/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs @@ -75,7 +75,9 @@ namespace Umbraco.Web.Security /// public static void ForUmbracoBackOffice(this AuthenticationOptions options, string style, string icon, string callbackPath = null) { - if (string.IsNullOrEmpty(options.AuthenticationType)) throw new ArgumentNullOrEmptyException("options.AuthenticationType"); + // TODO Change exceptions to InvalidOperationException, as the value isn't an argument + if (options.AuthenticationType == null) throw new ArgumentNullException(nameof(options.AuthenticationType)); + if (string.IsNullOrEmpty(options.AuthenticationType)) throw new ArgumentException("Value can't be empty.", nameof(options.AuthenticationType)); //Ensure the prefix is set if (options.AuthenticationType.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix) == false) diff --git a/src/Umbraco.Web/UrlHelperExtensions.cs b/src/Umbraco.Web/UrlHelperExtensions.cs index 249ce76193..7e63ff16b8 100644 --- a/src/Umbraco.Web/UrlHelperExtensions.cs +++ b/src/Umbraco.Web/UrlHelperExtensions.cs @@ -2,15 +2,12 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; -using System.Management.Instrumentation; using System.Web.Mvc; using System.Web.Routing; using ClientDependency.Core.Config; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.Exceptions; using Umbraco.Web.Composing; -using Umbraco.Web.Editors; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -70,7 +67,8 @@ namespace Umbraco.Web /// public static string GetUmbracoApiService(this UrlHelper url, string actionName, Type apiControllerType, RouteValueDictionary routeVals = null) { - if (string.IsNullOrEmpty(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName)); + if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + if (string.IsNullOrEmpty(actionName)) throw new ArgumentException("Value can't be empty.", nameof(actionName)); if (apiControllerType == null) throw new ArgumentNullException(nameof(apiControllerType)); var area = ""; @@ -99,8 +97,10 @@ namespace Umbraco.Web /// public static string GetUmbracoApiService(this UrlHelper url, string actionName, string controllerName, string area, RouteValueDictionary routeVals = null) { - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); - if (string.IsNullOrEmpty(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName)); + if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + if (string.IsNullOrEmpty(actionName)) throw new ArgumentException("Value can't be empty.", nameof(actionName)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); if (routeVals == null) { diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 48c26c93dd..8e410bf584 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -4,8 +4,6 @@ using System.Linq; using System.Web; using System.Web.Mvc; using Umbraco.Core; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Web.Composing; @@ -302,8 +300,10 @@ namespace Umbraco.Web /// public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals) { - if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); var encryptedRoute = CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); @@ -333,7 +333,8 @@ namespace Umbraco.Web /// public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals) { - if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); var area = ""; @@ -392,8 +393,10 @@ namespace Umbraco.Web /// internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null) { - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentNullOrEmptyException(nameof(controllerAction)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction)); + if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction)); if (area == null) throw new ArgumentNullException(nameof(area)); //need to create a params string as Base64 to put into our hidden field to use during the routes diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs index efee045890..28f09b46b7 100644 --- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs @@ -1,16 +1,13 @@ using System; +using System.Net; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; -using Umbraco.Core.Exceptions; -using Umbraco.Web.Composing; -using Umbraco.Web.Editors; - using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Web.Actions; using Umbraco.Core.Security; -using System.Net; +using Umbraco.Web.Actions; +using Umbraco.Web.Composing; namespace Umbraco.Web.WebApi.Filters { @@ -46,7 +43,9 @@ namespace Umbraco.Web.WebApi.Filters public EnsureUserPermissionForContentAttribute(string paramName) { - if (string.IsNullOrEmpty(paramName)) throw new ArgumentNullOrEmptyException(nameof(paramName)); + if (paramName == null) throw new ArgumentNullException(nameof(paramName)); + if (string.IsNullOrEmpty(paramName)) throw new ArgumentException("Value can't be empty.", nameof(paramName)); + _paramName = paramName; _permissionToCheck = ActionBrowse.ActionLetter; } diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs index 24bf2ea9a8..60e2889fd5 100644 --- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs @@ -3,7 +3,6 @@ using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core; -using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Web.Composing; using Umbraco.Web.Editors; @@ -38,14 +37,18 @@ namespace Umbraco.Web.WebApi.Filters public EnsureUserPermissionForMediaAttribute(string paramName) { - if (string.IsNullOrEmpty(paramName)) throw new ArgumentNullOrEmptyException(nameof(paramName)); + if (paramName == null) throw new ArgumentNullException(nameof(paramName)); + if (string.IsNullOrEmpty(paramName)) throw new ArgumentException("Value can't be empty.", nameof(paramName)); + _paramName = paramName; } // TODO: v8 guess this is not used anymore, source is ignored?! public EnsureUserPermissionForMediaAttribute(string paramName, DictionarySource source) { - if (string.IsNullOrEmpty(paramName)) throw new ArgumentNullOrEmptyException(nameof(paramName)); + if (paramName == null) throw new ArgumentNullException(nameof(paramName)); + if (string.IsNullOrEmpty(paramName)) throw new ArgumentException("Value can't be empty.", nameof(paramName)); + _paramName = paramName; } diff --git a/src/Umbraco.Web/WebApi/Filters/OutgoingDateTimeFormatAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OutgoingDateTimeFormatAttribute.cs index 713bce7d1b..7f05d59a18 100644 --- a/src/Umbraco.Web/WebApi/Filters/OutgoingDateTimeFormatAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/OutgoingDateTimeFormatAttribute.cs @@ -1,10 +1,8 @@ -using Newtonsoft.Json.Converters; -using System; +using System; using System.Linq; using System.Net.Http.Formatting; using System.Web.Http.Controllers; -using Umbraco.Core; -using Umbraco.Core.Exceptions; +using Newtonsoft.Json.Converters; namespace Umbraco.Web.WebApi.Filters { @@ -21,7 +19,9 @@ namespace Umbraco.Web.WebApi.Filters /// public JsonDateTimeFormatAttributeAttribute(string format) { - if (string.IsNullOrEmpty(format)) throw new ArgumentNullOrEmptyException(nameof(format)); + if (format == null) throw new ArgumentNullException(nameof(format)); + if (string.IsNullOrEmpty(format)) throw new ArgumentException("Value can't be empty.", nameof(format)); + _format = format; } From 71f3bbfb19fab84d83e9014e1a6132405d4b6e6a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 7 Oct 2019 22:12:14 +0200 Subject: [PATCH 024/173] Removed obscure exception message from RetryLimitExceededException --- .../Persistence/FaultHandling/RetryLimitExceededException.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Persistence/FaultHandling/RetryLimitExceededException.cs b/src/Umbraco.Core/Persistence/FaultHandling/RetryLimitExceededException.cs index abf8af35f5..a1a0db2983 100644 --- a/src/Umbraco.Core/Persistence/FaultHandling/RetryLimitExceededException.cs +++ b/src/Umbraco.Core/Persistence/FaultHandling/RetryLimitExceededException.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.FaultHandling /// Initializes a new instance of the class with a default error message. /// public RetryLimitExceededException() - : this("RetryLimitExceeded") + : base() { } /// @@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.FaultHandling /// /// The exception that is the cause of the current exception. public RetryLimitExceededException(Exception innerException) - : base(innerException != null ? innerException.Message : "RetryLimitExceeded", innerException) + : base(null, innerException) { } /// From 624c0d677692512d13569ebddbe03bb443ed55c0 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 8 Oct 2019 22:49:57 +0200 Subject: [PATCH 025/173] Reverted breaking changes to interfaces and implemented custom messages by replacing known default message strings with custom ones if provided. --- .../PropertyEditors/DataValueEditor.cs | 6 +-- .../PropertyEditors/IDataValueEditor.cs | 5 +-- .../PropertyEditors/IValueFormatValidator.cs | 3 +- .../IValueRequiredValidator.cs | 3 +- .../Validators/RegexValidator.cs | 9 ++-- .../Validators/RequiredValidator.cs | 19 +++----- .../Services/PropertyValidationService.cs | 2 +- .../Editors/Filters/ContentModelValidator.cs | 43 +++++++++++++++---- .../Filters/ContentSaveModelValidator.cs | 8 ++-- .../Filters/ContentSaveValidationAttribute.cs | 24 ++++++----- .../MediaItemSaveValidationAttribute.cs | 19 ++++---- .../Filters/MediaSaveModelValidator.cs | 5 ++- .../Filters/MemberSaveModelValidator.cs | 6 +-- .../Filters/MemberSaveValidationAttribute.cs | 17 +++++--- .../PropertyEditors/TagsPropertyEditor.cs | 2 +- 15 files changed, 93 insertions(+), 78 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index f1c76c2b8b..2f215c6032 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -86,7 +86,7 @@ namespace Umbraco.Core.PropertyEditors public string ValueType { get; set; } /// - public IEnumerable Validate(object value, bool required, string requiredMessage, string format, string formatMessage) + public IEnumerable Validate(object value, bool required, string format) { List results = null; var r = Validators.SelectMany(v => v.Validate(value, ValueType, Configuration)).ToList(); @@ -98,14 +98,14 @@ namespace Umbraco.Core.PropertyEditors if (required) { - r = RequiredValidator.ValidateRequired(value, ValueType, requiredMessage).ToList(); + r = RequiredValidator.ValidateRequired(value, ValueType).ToList(); if (r.Any()) { if (results == null) results = r; else results.AddRange(r); } } var stringValue = value?.ToString(); if (!string.IsNullOrWhiteSpace(format) && !string.IsNullOrWhiteSpace(stringValue)) { - r = FormatValidator.ValidateFormat(value, ValueType, format, formatMessage).ToList(); + r = FormatValidator.ValidateFormat(value, ValueType, format).ToList(); if (r.Any()) { if (results == null) results = r; else results.AddRange(r); } } diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index e31015f60c..c0e6d548f0 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -41,14 +41,11 @@ namespace Umbraco.Core.PropertyEditors /// /// Validates a property value using custom messages. - /// /// The property value. /// A value indicating whether the property value is required. - /// A custom validation message to use when the property value is required. /// A specific format (regex) that the property value must respect. - /// A custom validation message to use when the property value is does not match the specific format (regex). - IEnumerable Validate(object value, bool required, string requiredMessage, string format, string formatMessage); + IEnumerable Validate(object value, bool required, string format); /// /// Gets the validators to use to validate the edited value. diff --git a/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs index a361f387e3..9f29d65ffd 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueFormatValidator.cs @@ -14,12 +14,11 @@ namespace Umbraco.Core.PropertyEditors /// The value to validate. /// The value type. /// A format definition. - /// A custom validation message to use when the property value is does not match the specific format (regex). /// Validation results. /// /// The is expected to be a valid regular expression. /// This is used to validate values against the property type validation regular expression. /// - IEnumerable ValidateFormat(object value, string valueType, string format, string formatMessage); + IEnumerable ValidateFormat(object value, string valueType, string format); } } diff --git a/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs index 7055e1287b..1b4074c96f 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueRequiredValidator.cs @@ -13,11 +13,10 @@ namespace Umbraco.Core.PropertyEditors /// /// The value to validate. /// The value type. - /// A custom validation message to use when the property value is required. /// Validation results. /// /// This is used to validate values when the property type specifies that a value is required. /// - IEnumerable ValidateRequired(object value, string valueType, string requiredMessage); + IEnumerable ValidateRequired(object value, string valueType); } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs index 53686d745c..4a3b7fe7e2 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RegexValidator.cs @@ -62,11 +62,11 @@ namespace Umbraco.Core.PropertyEditors.Validators throw new InvalidOperationException("The validator has not been configured."); } - return ValidateFormat(value, valueType, _regex, string.Empty); + return ValidateFormat(value, valueType, _regex); } /// - public IEnumerable ValidateFormat(object value, string valueType, string format, string formatMessage) + public IEnumerable ValidateFormat(object value, string valueType, string format) { if (string.IsNullOrWhiteSpace(format)) { @@ -75,10 +75,7 @@ namespace Umbraco.Core.PropertyEditors.Validators if (value == null || !new Regex(format).IsMatch(value.ToString())) { - var message = string.IsNullOrWhiteSpace(formatMessage) - ? _textService.Localize("validation", "invalidPattern") - : formatMessage; - yield return new ValidationResult(message, new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" }); } } } diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs index d752c4ca47..1aa29870e5 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs @@ -27,29 +27,23 @@ namespace Umbraco.Core.PropertyEditors.Validators /// public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { - return ValidateRequired(value, valueType, string.Empty); + return ValidateRequired(value, valueType); } /// - public IEnumerable ValidateRequired(object value, string valueType, string requiredMessage) + public IEnumerable ValidateRequired(object value, string valueType) { if (value == null) { - var message = string.IsNullOrWhiteSpace(requiredMessage) - ? _textService.Localize("validation", "invalidNull") - : requiredMessage; - yield return new ValidationResult(message, new[] {"value"}); + yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] { "value" }); yield break; } if (valueType.InvariantEquals(ValueTypes.Json)) { - var message = string.IsNullOrWhiteSpace(requiredMessage) - ? _textService.Localize("validation", "invalidEmpty") - : requiredMessage; if (value.ToString().DetectIsEmptyJson()) { - yield return new ValidationResult(message, new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); } yield break; @@ -57,10 +51,7 @@ namespace Umbraco.Core.PropertyEditors.Validators if (value.ToString().IsNullOrWhiteSpace()) { - var message = string.IsNullOrWhiteSpace(requiredMessage) - ? _textService.Localize("validation", "invalidEmpty") - : requiredMessage; - yield return new ValidationResult(message, new[] { "value" }); + yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" }); } } } diff --git a/src/Umbraco.Core/Services/PropertyValidationService.cs b/src/Umbraco.Core/Services/PropertyValidationService.cs index 2e1ba0b8eb..b846095bd1 100644 --- a/src/Umbraco.Core/Services/PropertyValidationService.cs +++ b/src/Umbraco.Core/Services/PropertyValidationService.cs @@ -133,7 +133,7 @@ namespace Umbraco.Core.Services var editor = _propertyEditors[propertyType.PropertyEditorAlias]; var configuration = _dataTypeService.GetDataType(propertyType.DataTypeId).Configuration; var valueEditor = editor.GetValueEditor(configuration); - return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.MandatoryMessage, propertyType.ValidationRegExp, propertyType.ValidationRegExpMessage).Any(); + return !valueEditor.Validate(value, propertyType.Mandatory, propertyType.ValidationRegExp).Any(); } } } diff --git a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs index 67f2e2d090..22f52e9293 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs @@ -5,10 +5,10 @@ using System.Net; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; -using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors.Filters @@ -43,8 +43,11 @@ namespace Umbraco.Web.Editors.Filters where TModelSave: IContentSave where TModelWithProperties : IContentProperties { - protected ContentModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor) + private readonly ILocalizedTextService _textService; + + protected ContentModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor) { + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); } /// @@ -122,6 +125,18 @@ namespace Umbraco.Web.Editors.Filters { var properties = modelWithProperties.Properties.ToDictionary(x => x.Alias, x => x); + // Retrieve default messages used for required and regex validatation. We'll replace these + // if set with custom ones if they've been provided for a given property. + var requiredDefaultMessages = new[] + { + _textService.Localize("validation", "invalidNull"), + _textService.Localize("validation", "invalidEmpty") + }; + var formatDefaultMessages = new[] + { + _textService.Localize("validation", "invalidPattern"), + }; + foreach (var p in dto.Properties) { var editor = p.PropertyEditor; @@ -141,7 +156,7 @@ namespace Umbraco.Web.Editors.Filters var postedValue = postedProp.Value; - ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState); + ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState, requiredDefaultMessages, formatDefaultMessages); } @@ -157,22 +172,34 @@ namespace Umbraco.Web.Editors.Filters /// /// /// + /// + /// protected virtual void ValidatePropertyValue( TModelSave model, TModelWithProperties modelWithProperties, IDataEditor editor, ContentPropertyDto property, object postedValue, - ModelStateDictionary modelState) + ModelStateDictionary modelState, + string[] requiredDefaultMessages, + string[] formatDefaultMessages) { - // validate var valueEditor = editor.GetValueEditor(property.DataType.Configuration); - foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.IsRequiredMessage, property.ValidationRegExp, property.ValidationRegExpMessage)) + foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp)) { + // If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate(). + if (property.IsRequired && !string.IsNullOrWhiteSpace(property.IsRequiredMessage) && requiredDefaultMessages.Contains(r.ErrorMessage)) + { + r.ErrorMessage = property.IsRequiredMessage; + } + + if (!string.IsNullOrWhiteSpace(property.ValidationRegExp) && !string.IsNullOrWhiteSpace(property.ValidationRegExpMessage) && formatDefaultMessages.Contains(r.ErrorMessage)) + { + r.ErrorMessage = property.ValidationRegExpMessage; + } + modelState.AddPropertyError(r, property.Alias, property.Culture); } } - - } } diff --git a/src/Umbraco.Web/Editors/Filters/ContentSaveModelValidator.cs b/src/Umbraco.Web/Editors/Filters/ContentSaveModelValidator.cs index b74cd71f11..39bd6ab0f4 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentSaveModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentSaveModelValidator.cs @@ -1,8 +1,6 @@ -using System.Web.Http.ModelBinding; -using Umbraco.Core; -using Umbraco.Core.Logging; +using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors.Filters @@ -12,7 +10,7 @@ namespace Umbraco.Web.Editors.Filters /// internal class ContentSaveModelValidator : ContentModelValidator { - public ContentSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor) + public ContentSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor, textService) { } } diff --git a/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs index 18fd334b1c..fbce2d0414 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs @@ -23,28 +23,30 @@ namespace Umbraco.Web.Editors.Filters /// internal sealed class ContentSaveValidationAttribute : ActionFilterAttribute { - public ContentSaveValidationAttribute(): this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.ContentService, Current.Services.UserService, Current.Services.EntityService) + private readonly ILogger _logger; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _textService; + private readonly IContentService _contentService; + private readonly IUserService _userService; + private readonly IEntityService _entityService; + + public ContentSaveValidationAttribute(): this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.ContentService, Current.Services.UserService, Current.Services.EntityService) { } - public ContentSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IContentService contentService, IUserService userService, IEntityService entityService) + public ContentSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IContentService contentService, IUserService userService, IEntityService entityService) { - _logger = logger; - _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); } - private readonly ILogger _logger; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IContentService _contentService; - private readonly IUserService _userService; - private readonly IEntityService _entityService; - public override void OnActionExecuting(HttpActionContext actionContext) { var model = (ContentItemSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new ContentSaveModelValidator(_logger, _umbracoContextAccessor); + var contentItemValidator = new ContentSaveModelValidator(_logger, _umbracoContextAccessor, _textService); if (!ValidateAtLeastOneVariantIsBeingSaved(model, actionContext)) return; if (!contentItemValidator.ValidateExistingContent(model, actionContext)) return; diff --git a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs index 7351c476e4..449ef95675 100644 --- a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs @@ -1,7 +1,6 @@ -using System.Linq; +using System; using System.Net; using System.Net.Http; -using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core; @@ -21,25 +20,27 @@ namespace Umbraco.Web.Editors.Filters { private readonly ILogger _logger; private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _textService; private readonly IMediaService _mediaService; private readonly IEntityService _entityService; - public MediaItemSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MediaService, Current.Services.EntityService) + public MediaItemSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MediaService, Current.Services.EntityService) { } - public MediaItemSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMediaService mediaService, IEntityService entityService) + public MediaItemSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMediaService mediaService, IEntityService entityService) { - _logger = logger; - _umbracoContextAccessor = umbracoContextAccessor; - _mediaService = mediaService; - _entityService = entityService; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); } public override void OnActionExecuting(HttpActionContext actionContext) { var model = (MediaItemSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new MediaSaveModelValidator(_logger, _umbracoContextAccessor); + var contentItemValidator = new MediaSaveModelValidator(_logger, _umbracoContextAccessor, _textService); if (ValidateUserAccess(model, actionContext)) { diff --git a/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs b/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs index 83c0885d62..87b55fea76 100644 --- a/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs @@ -1,5 +1,6 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors.Filters @@ -9,8 +10,8 @@ namespace Umbraco.Web.Editors.Filters /// internal class MediaSaveModelValidator : ContentModelValidator> { - public MediaSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor) + public MediaSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor, textService) { } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs index 7fc2b1e648..9b47965493 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs @@ -21,10 +21,10 @@ namespace Umbraco.Web.Editors.Filters { private readonly IMemberTypeService _memberTypeService; - public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService) - : base(logger, umbracoContextAccessor) + public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService) + : base(logger, umbracoContextAccessor, textService) { - _memberTypeService = memberTypeService; + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); } /// diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs index 04b0112d56..7e2c204596 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs @@ -1,4 +1,5 @@ -using System.Web.Http.Controllers; +using System; +using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core.Logging; using Umbraco.Core.Services; @@ -14,23 +15,25 @@ namespace Umbraco.Web.Editors.Filters { private readonly ILogger _logger; private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _textService; private readonly IMemberTypeService _memberTypeService; public MemberSaveValidationAttribute() - : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MemberTypeService) + : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MemberTypeService) { } - public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService) + public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService) { - _logger = logger; - _umbracoContextAccessor = umbracoContextAccessor; - _memberTypeService = memberTypeService; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); } public override void OnActionExecuting(HttpActionContext actionContext) { var model = (MemberSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _memberTypeService); + var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _textService, _memberTypeService); //now do each validation step if (contentItemValidator.ValidateExistingContent(model, actionContext)) if (contentItemValidator.ValidateProperties(model, model, actionContext)) diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs index 250f1a665a..fd7e8694a3 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs @@ -77,7 +77,7 @@ namespace Umbraco.Web.PropertyEditors private class RequiredJsonValueValidator : IValueRequiredValidator { /// - public IEnumerable ValidateRequired(object value, string valueType, string requiredMessage) + public IEnumerable ValidateRequired(object value, string valueType) { if (value == null) { From ef03b0c6115cca168b3ad2787a6d8921ab3d28a1 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Wed, 9 Oct 2019 00:39:28 +0100 Subject: [PATCH 026/173] Stop caching of preview macro results --- .../Editors/MacroRenderingController.cs | 21 +++++--- src/Umbraco.Web/IUmbracoComponentRenderer.cs | 10 ++++ src/Umbraco.Web/UmbracoComponentRenderer.cs | 52 ++----------------- src/Umbraco.Web/UmbracoHelper.cs | 6 +-- 4 files changed, 30 insertions(+), 59 deletions(-) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index efad07ce89..f8feae25eb 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -135,14 +135,19 @@ namespace Umbraco.Web.Editors // must have an active variation context! _variationContextAccessor.VariationContext = new VariationContext(culture); - var result = Request.CreateResponse(); - //need to create a specific content result formatted as HTML since this controller has been configured - //with only json formatters. - result.Content = new StringContent( - _componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(), - Encoding.UTF8, - "text/html"); - return result; + using (UmbracoContext.ForcedPreview(true)) + { + + var result = Request.CreateResponse(); + //need to create a specific content result formatted as HTML since this controller has been configured + //with only json formatters. + result.Content = new StringContent( + _componentRenderer.RenderMacro(publishedContent, m.Alias, macroParams).ToString(), + Encoding.UTF8, + "text/html"); + + return result; + } } [HttpPost] diff --git a/src/Umbraco.Web/IUmbracoComponentRenderer.cs b/src/Umbraco.Web/IUmbracoComponentRenderer.cs index 4dc9036e6b..dfe607fd4a 100644 --- a/src/Umbraco.Web/IUmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/IUmbracoComponentRenderer.cs @@ -42,5 +42,15 @@ namespace Umbraco.Web /// The parameters. /// IHtmlString RenderMacro(int contentId, string alias, IDictionary parameters); + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// + /// The alias. + /// The parameters. + /// + IHtmlString RenderMacro(IPublishedContent content, string alias, IDictionary parameters); + } } diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index f6c3d30da3..19e4223e2b 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -92,12 +92,12 @@ namespace Umbraco.Web if (contentId == default) throw new ArgumentException("Invalid content id " + contentId); - var content = _umbracoContextAccessor.UmbracoContext.Content?.GetById(true, contentId); + var content = _umbracoContextAccessor.UmbracoContext.Content?.GetById(contentId); if (content == null) throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId); - return RenderMacro(alias, parameters, content); + return RenderMacro(content, alias, parameters); } /// @@ -107,7 +107,7 @@ namespace Umbraco.Web /// The parameters. /// The content used for macro rendering /// - private IHtmlString RenderMacro(string alias, IDictionary parameters, IPublishedContent content) + public IHtmlString RenderMacro(IPublishedContent content, string alias, IDictionary parameters) { if (content == null) throw new ArgumentNullException(nameof(content)); @@ -117,51 +117,7 @@ namespace Umbraco.Web x => x.Key.ToLowerInvariant(), i => (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); - var macroControl = _macroRenderer.Render(alias, content, macroProps).GetAsControl(); - - string html; - if (macroControl is LiteralControl control) - { - // no need to execute, we already have text - html = control.Text; - } - else - { - using (var containerPage = new FormlessPage()) - { - containerPage.Controls.Add(macroControl); - - using (var output = new StringWriter()) - { - // .Execute() does a PushTraceContext/PopTraceContext and writes trace output straight into 'output' - // and I do not see how we could wire the trace context to the current context... so it creates dirty - // trace output right in the middle of the page. - // - // The only thing we can do is fully disable trace output while .Execute() runs and restore afterwards - // which means trace output is lost if the macro is a control (.ascx or user control) that is invoked - // from within Razor -- which makes sense anyway because the control can _not_ run correctly from - // within Razor since it will never be inserted into the page pipeline (which may even not exist at all - // if we're running MVC). - // - // I'm sure there's more things that will get lost with this context changing but I guess we'll figure - // those out as we go along. One thing we lose is the content type response output. - // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So - // here we'll save the content type response and reset it after execute is called. - - var contentType = _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType; - var traceIsEnabled = containerPage.Trace.IsEnabled; - containerPage.Trace.IsEnabled = false; - _umbracoContextAccessor.UmbracoContext.HttpContext.Server.Execute(containerPage, output, true); - containerPage.Trace.IsEnabled = traceIsEnabled; - //reset the content type - _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; - - //Now, we need to ensure that local links are parsed - html = TemplateUtilities.ParseInternalLinks(output.ToString(), _umbracoContextAccessor.UmbracoContext.UrlProvider); - } - } - - } + string html = _macroRenderer.Render(alias, content, macroProps).GetAsText(); return new HtmlString(html); } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 367d90a504..01d64cdd3e 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -146,7 +146,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias) { - return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, new { }); + return ComponentRenderer.RenderMacro(AssignedContentItem, alias, new Dictionary()); } /// @@ -157,7 +157,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, object parameters) { - return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters.ToDictionary()); + return ComponentRenderer.RenderMacro(AssignedContentItem, alias, parameters.ToDictionary()); } /// @@ -168,7 +168,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, IDictionary parameters) { - return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters); + return ComponentRenderer.RenderMacro(AssignedContentItem, alias, parameters); } #endregion From d25a38e448d229d25eac26440b95b7fbf22da381 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 9 Oct 2019 08:15:27 +0200 Subject: [PATCH 027/173] Renamed client-side service file to match function name --- .../{validationhelper.service.js => validationmessage.service.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Umbraco.Web.UI.Client/src/common/services/{validationhelper.service.js => validationmessage.service.js} (100%) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/validationmessage.service.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/common/services/validationhelper.service.js rename to src/Umbraco.Web.UI.Client/src/common/services/validationmessage.service.js From 7f889e6d1b0c73a9f70382f10eb39645740a847b Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 09:55:12 +0200 Subject: [PATCH 028/173] Get EnableComposer/DisableComposer attributes for all assemblies --- src/Umbraco.Core/Composing/Composers.cs | 21 +++-- src/Umbraco.Core/Runtime/CoreRuntime.cs | 2 +- .../Components/ComponentTests.cs | 79 +++++++++++-------- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 4 +- 4 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Composing/Composers.cs b/src/Umbraco.Core/Composing/Composers.cs index 0510740e42..a4eba294bc 100644 --- a/src/Umbraco.Core/Composing/Composers.cs +++ b/src/Umbraco.Core/Composing/Composers.cs @@ -18,19 +18,31 @@ namespace Umbraco.Core.Composing private readonly Composition _composition; private readonly IProfilingLogger _logger; private readonly IEnumerable _composerTypes; + private readonly IEnumerable _assemblies; private const int LogThresholdMilliseconds = 100; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The composition. /// The composer types. + /// The assemblies. /// A profiling logger. - public Composers(Composition composition, IEnumerable composerTypes, IProfilingLogger logger) + /// + /// composition + /// or + /// composerTypes + /// or + /// assemblies + /// or + /// logger + /// + public Composers(Composition composition, IEnumerable composerTypes, IEnumerable assemblies, IProfilingLogger logger) { _composition = composition ?? throw new ArgumentNullException(nameof(composition)); _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); + _assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -103,7 +115,7 @@ namespace Umbraco.Core.Composing .ToList(); // enable or disable composers - EnableDisableComposers(composerTypeList); + EnableDisableComposers(composerTypeList, _assemblies); void GatherInterfaces(Type type, Func getTypeInAttribute, HashSet iset, List set2) where TAttribute : Attribute @@ -218,7 +230,7 @@ namespace Umbraco.Core.Composing return text.ToString(); } - private static void EnableDisableComposers(ICollection types) + private static void EnableDisableComposers(ICollection types, IEnumerable assemblies) { var enabled = new Dictionary(); @@ -240,7 +252,6 @@ namespace Umbraco.Core.Composing enableInfo.Weight = weight2; } - var assemblies = types.Select(x => x.Assembly).Distinct(); foreach (var assembly in assemblies) { foreach (var attr in assembly.GetCustomAttributes()) diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 5b069641c4..abc2a924b4 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -148,7 +148,7 @@ namespace Umbraco.Core.Runtime // get composers, and compose var composerTypes = ResolveComposerTypes(typeLoader); composition.WithCollectionBuilder(); - var composers = new Composers(composition, composerTypes, ProfilingLogger); + var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, ProfilingLogger); composers.Compose(); // create the factory diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 2ba94d8c78..a82975e8c5 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -65,10 +65,11 @@ namespace Umbraco.Tests.Components public void Boot1A() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - goes away with RuntimeLevel.Unknown @@ -104,10 +105,11 @@ namespace Umbraco.Tests.Components public void Boot1B() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - stays with RuntimeLevel.Run @@ -120,10 +122,11 @@ namespace Umbraco.Tests.Components public void Boot2() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); // 21 is required by 20 // => reorder components accordingly @@ -135,10 +138,11 @@ namespace Umbraco.Tests.Components public void Boot3() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); // i23 requires 22 // 24, 25 implement i23 @@ -152,10 +156,11 @@ namespace Umbraco.Tests.Components public void BrokenRequire() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); try { @@ -175,10 +180,11 @@ namespace Umbraco.Tests.Components public void BrokenRequired() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 13 is required by 1 @@ -196,6 +202,7 @@ namespace Umbraco.Tests.Components Terminated.Clear(); var register = MockRegister(); + var typeLoader = MockTypeLoader(); var factory = MockFactory(m => { m.Setup(x => x.TryGetInstance(It.Is(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource()); @@ -210,10 +217,10 @@ namespace Umbraco.Tests.Components throw new NotSupportedException(type.FullName); }); }); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer1), typeof(Composer5), typeof(Composer5a) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Assert.IsEmpty(Composed); composers.Compose(); @@ -236,10 +243,11 @@ namespace Umbraco.Tests.Components public void Requires1() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -251,10 +259,11 @@ namespace Umbraco.Tests.Components public void Requires2A() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -267,11 +276,12 @@ namespace Umbraco.Tests.Components public void Requires2B() { var register = MockRegister(); + var typeLoader = MockTypeLoader(); var factory = MockFactory(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); var builder = composition.WithCollectionBuilder(); @@ -287,35 +297,36 @@ namespace Umbraco.Tests.Components public void WeakDependencies() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer10) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); Assert.AreEqual(typeof(Composer10), Composed[0]); types = new[] { typeof(Composer11) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); var requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer2) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer12) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); @@ -326,10 +337,11 @@ namespace Umbraco.Tests.Components public void DisableMissing() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -341,16 +353,17 @@ namespace Umbraco.Tests.Components public void AttributesPriorities() { var register = MockRegister(); - var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); + var typeLoader = MockTypeLoader(); + var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer26) }; // 26 disabled by assembly attribute - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(0, Composed.Count); // 26 gone types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27 - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); // both @@ -367,7 +380,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); Console.WriteLine(report); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index d0258a100f..63422dd32c 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Runtimes var composerTypes = typeLoader.GetTypes() // all of them .Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components .Where(x => x != typeof(WebInitialComposer) && x != typeof(WebFinalComposer)); // exclude web runtime - var composers = new Composers(composition, composerTypes, profilingLogger); + var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, profilingLogger); composers.Compose(); // must registers stuff that WebRuntimeComponent would register otherwise @@ -272,7 +272,7 @@ namespace Umbraco.Tests.Runtimes .Where(x => !x.FullName.StartsWith("Umbraco.Tests")); // single? //var componentTypes = new[] { typeof(CoreRuntimeComponent) }; - var composers = new Composers(composition, composerTypes, profilingLogger); + var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, profilingLogger); // get components to compose themselves composers.Compose(); From be21ddb38ef3f8a09a18b8614b803a1f79132435 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 10:20:10 +0200 Subject: [PATCH 029/173] Added GetAssemblyAttributes to TypeLoader --- src/Umbraco.Core/Composing/TypeLoader.cs | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index fe7a561eca..1a5cd6751f 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -505,6 +505,38 @@ namespace Umbraco.Core.Composing #endregion + #region Get Assembly Attributes + + /// + /// Gets the assembly attributes. + /// + /// The attribute type. + /// + /// The assembly attributes of the specified type. + /// + public IEnumerable GetAssemblyAttributes() + where T : Attribute + { + return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()); + } + + /// + /// Gets the assembly attributes. + /// + /// The attribute types. + /// + /// The assembly attributes of the specified types. + /// + /// attributeTypes + public IEnumerable GetAssemblyAttributes(params Type[] attributeTypes) + { + if (attributeTypes == null) throw new ArgumentNullException(nameof(attributeTypes)); + + return AssembliesToScan.SelectMany(a => attributeTypes.SelectMany(at => a.GetCustomAttributes(at))).ToList(); + } + + #endregion + #region Get Types /// From 03bbcec8946f74dc89d9a86be1b9827766cec8a3 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 10:22:39 +0200 Subject: [PATCH 030/173] Enable/disable composers based on supplied attributes (instead of assemblies) --- src/Umbraco.Core/Composing/Composers.cs | 34 ++++++++-------- src/Umbraco.Core/Runtime/CoreRuntime.cs | 3 +- .../Components/ComponentTests.cs | 40 +++++++++---------- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 4 +- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Composing/Composers.cs b/src/Umbraco.Core/Composing/Composers.cs index a4eba294bc..9b03c18baa 100644 --- a/src/Umbraco.Core/Composing/Composers.cs +++ b/src/Umbraco.Core/Composing/Composers.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Composing private readonly Composition _composition; private readonly IProfilingLogger _logger; private readonly IEnumerable _composerTypes; - private readonly IEnumerable _assemblies; + private readonly IEnumerable _enableDisableAttributes; private const int LogThresholdMilliseconds = 100; @@ -27,22 +27,23 @@ namespace Umbraco.Core.Composing /// /// The composition. /// The composer types. - /// The assemblies. + /// The enable/disable attributes. /// A profiling logger. /// /// composition /// or /// composerTypes /// or - /// assemblies + /// enableDisableAttributes /// or /// logger /// - public Composers(Composition composition, IEnumerable composerTypes, IEnumerable assemblies, IProfilingLogger logger) + + public Composers(Composition composition, IEnumerable composerTypes, IEnumerable enableDisableAttributes, IProfilingLogger logger) { _composition = composition ?? throw new ArgumentNullException(nameof(composition)); _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); - _assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies)); + _enableDisableAttributes = enableDisableAttributes ?? throw new ArgumentNullException(nameof(enableDisableAttributes)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -115,7 +116,7 @@ namespace Umbraco.Core.Composing .ToList(); // enable or disable composers - EnableDisableComposers(composerTypeList, _assemblies); + EnableDisableComposers(_enableDisableAttributes, composerTypeList); void GatherInterfaces(Type type, Func getTypeInAttribute, HashSet iset, List set2) where TAttribute : Attribute @@ -230,7 +231,7 @@ namespace Umbraco.Core.Composing return text.ToString(); } - private static void EnableDisableComposers(ICollection types, IEnumerable assemblies) + private static void EnableDisableComposers(IEnumerable enableDisableAttributes, ICollection types) { var enabled = new Dictionary(); @@ -252,19 +253,16 @@ namespace Umbraco.Core.Composing enableInfo.Weight = weight2; } - foreach (var assembly in assemblies) + foreach (var attr in enableDisableAttributes.OfType()) { - foreach (var attr in assembly.GetCustomAttributes()) - { - var type = attr.EnabledType; - UpdateEnableInfo(type, 2, enabled, true); - } + var type = attr.EnabledType; + UpdateEnableInfo(type, 2, enabled, true); + } - foreach (var attr in assembly.GetCustomAttributes()) - { - var type = attr.DisabledType; - UpdateEnableInfo(type, 2, enabled, false); - } + foreach (var attr in enableDisableAttributes.OfType()) + { + var type = attr.DisabledType; + UpdateEnableInfo(type, 2, enabled, false); } foreach (var composerType in types) diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index abc2a924b4..6c462dc064 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -147,8 +147,9 @@ namespace Umbraco.Core.Runtime // get composers, and compose var composerTypes = ResolveComposerTypes(typeLoader); + var enableDisableAttributes = typeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute)); composition.WithCollectionBuilder(); - var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, ProfilingLogger); + var composers = new Composers(composition, composerTypes, enableDisableAttributes, ProfilingLogger); composers.Compose(); // create the factory diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index a82975e8c5..1a528f85f6 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - goes away with RuntimeLevel.Unknown @@ -109,7 +109,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - stays with RuntimeLevel.Run @@ -126,7 +126,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 21 is required by 20 // => reorder components accordingly @@ -142,7 +142,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // i23 requires 22 // 24, 25 implement i23 @@ -160,7 +160,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); try { @@ -184,7 +184,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = TypeArray(); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 13 is required by 1 @@ -220,7 +220,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer1), typeof(Composer5), typeof(Composer5a) }; - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Assert.IsEmpty(Composed); composers.Compose(); @@ -247,7 +247,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -263,7 +263,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -281,7 +281,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); var builder = composition.WithCollectionBuilder(); @@ -301,32 +301,32 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer10) }; - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); Assert.AreEqual(typeof(Composer10), Composed[0]); types = new[] { typeof(Composer11) }; - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer2) }; - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer12) }; - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); @@ -341,7 +341,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -357,13 +357,13 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); var types = new[] { typeof(Composer26) }; // 26 disabled by assembly attribute - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(0, Composed.Count); // 26 gone types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27 - composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); // both @@ -380,7 +380,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); - var composers = new Composers(composition, types, typeLoader.AssembliesToScan, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); Console.WriteLine(report); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 63422dd32c..7ab329b9a0 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Runtimes var composerTypes = typeLoader.GetTypes() // all of them .Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components .Where(x => x != typeof(WebInitialComposer) && x != typeof(WebFinalComposer)); // exclude web runtime - var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, profilingLogger); + var composers = new Composers(composition, composerTypes, Enumerable.Empty(), profilingLogger); composers.Compose(); // must registers stuff that WebRuntimeComponent would register otherwise @@ -272,7 +272,7 @@ namespace Umbraco.Tests.Runtimes .Where(x => !x.FullName.StartsWith("Umbraco.Tests")); // single? //var componentTypes = new[] { typeof(CoreRuntimeComponent) }; - var composers = new Composers(composition, composerTypes, typeLoader.AssembliesToScan, profilingLogger); + var composers = new Composers(composition, composerTypes, Enumerable.Empty(), profilingLogger); // get components to compose themselves composers.Compose(); From fa2a4d867b06e5c0775fdc66f53beaea50c44b06 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 15:37:13 +0200 Subject: [PATCH 031/173] Return assemblies as list and added documentation --- src/Umbraco.Core/Composing/Composers.cs | 12 +++++------- src/Umbraco.Core/Composing/TypeLoader.cs | 8 ++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Composing/Composers.cs b/src/Umbraco.Core/Composing/Composers.cs index 9b03c18baa..004c2527e6 100644 --- a/src/Umbraco.Core/Composing/Composers.cs +++ b/src/Umbraco.Core/Composing/Composers.cs @@ -26,18 +26,16 @@ namespace Umbraco.Core.Composing /// Initializes a new instance of the class. /// /// The composition. - /// The composer types. - /// The enable/disable attributes. - /// A profiling logger. - /// - /// composition + /// The types. + /// The and/or attributes. + /// The profiling logger. + /// composition /// or /// composerTypes /// or /// enableDisableAttributes /// or - /// logger - /// + /// logger public Composers(Composition composition, IEnumerable composerTypes, IEnumerable enableDisableAttributes, IProfilingLogger logger) { diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 1a5cd6751f..7c70b4feba 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -508,20 +508,20 @@ namespace Umbraco.Core.Composing #region Get Assembly Attributes /// - /// Gets the assembly attributes. + /// Gets the assembly attributes of the specified type . /// /// The attribute type. /// - /// The assembly attributes of the specified type. + /// The assembly attributes of the specified type . /// public IEnumerable GetAssemblyAttributes() where T : Attribute { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()); + return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); } /// - /// Gets the assembly attributes. + /// Gets the assembly attributes of the specified . /// /// The attribute types. /// From bb26f07d01c12c95a6c194b0fb0c3962feb54f7e Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 15:38:07 +0200 Subject: [PATCH 032/173] Added parameterless GetAssemblyAttributes overload returning all attributes --- src/Umbraco.Core/Composing/TypeLoader.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 7c70b4feba..b8c4db6183 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -520,6 +520,17 @@ namespace Umbraco.Core.Composing return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); } + /// + /// Gets all the assembly attributes. + /// + /// + /// All assembly attributes. + /// + public IEnumerable GetAssemblyAttributes() + { + return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + } + /// /// Gets the assembly attributes of the specified . /// From 932f7297f3473957c074325f72f632728eb8426a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 9 Oct 2019 15:50:06 +0200 Subject: [PATCH 033/173] Fixed failing test by passing in correct enable/disable attributes --- src/Umbraco.Tests/Components/ComponentTests.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 1a528f85f6..042cac1281 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -5,7 +5,6 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Compose; using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -13,8 +12,6 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; -[assembly:DisableComposer(typeof(Umbraco.Tests.Components.ComponentTests.Composer26))] - namespace Umbraco.Tests.Components { [TestFixture] @@ -356,14 +353,15 @@ namespace Umbraco.Tests.Components var typeLoader = MockTypeLoader(); var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var types = new[] { typeof(Composer26) }; // 26 disabled by assembly attribute - var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); + var types = new[] { typeof(Composer26) }; + var enableDisableAttributes = new[] { new DisableComposerAttribute(typeof(Composer26)) }; + var composers = new Composers(composition, types, enableDisableAttributes, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(0, Composed.Count); // 26 gone types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27 - composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); + composers = new Composers(composition, types, enableDisableAttributes, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); // both @@ -519,7 +517,6 @@ namespace Umbraco.Tests.Components public class Composer25 : TestComposerBase, IComposer23 { } - // disabled by assembly attribute public class Composer26 : TestComposerBase { } From 82af5f26e54275281f02292918950081e3d8ad51 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Wed, 9 Oct 2019 16:16:19 +0100 Subject: [PATCH 034/173] Added a fix to handle hyphens in file names --- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index a98cd8f212..5e2d0afc65 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -203,11 +203,26 @@ namespace Umbraco.Web.Search foreach (var f in fields) { + var queryWordsReplaced = new string[querywords.Length]; + + // when searching file names containing hyphens we need to replace the hyphens with spaces + if (f.Equals(UmbracoExamineIndex.UmbracoFileFieldName)) + { + for (var index = 0; index < querywords.Length; index++) + { + queryWordsReplaced[index] = querywords[index].Replace("\\-", " ").Trim(" "); + } + } + else + { + queryWordsReplaced = querywords; + } + //additional fields normally sb.Append(f); sb.Append(":"); sb.Append("("); - foreach (var w in querywords) + foreach (var w in queryWordsReplaced) { sb.Append(w.ToLower()); sb.Append("* "); From 0c957c2fd9e494785c6623256dc8ae0f456cfb7b Mon Sep 17 00:00:00 2001 From: Jeavon Date: Wed, 9 Oct 2019 16:46:57 +0100 Subject: [PATCH 035/173] Also replace underscores --- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 5e2d0afc65..fd87dd0660 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -210,7 +210,7 @@ namespace Umbraco.Web.Search { for (var index = 0; index < querywords.Length; index++) { - queryWordsReplaced[index] = querywords[index].Replace("\\-", " ").Trim(" "); + queryWordsReplaced[index] = querywords[index].Replace("\\-", " ").Replace("_", " ").Trim(" "); } } else From 702aa41ff2a8801c5f24976dc9ee103afb0f4754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Kottal?= Date: Wed, 9 Oct 2019 22:19:28 +0200 Subject: [PATCH 036/173] Adds nametemplates for grid editors --- .../Configuration/Grid/IGridEditorConfig.cs | 1 + src/Umbraco.Core/PropertyEditors/GridEditor.cs | 3 +++ .../views/propertyeditors/grid/grid.controller.js | 13 ++++++++++++- .../src/views/propertyeditors/grid/grid.html | 4 ++-- src/Umbraco.Web.UI/config/grid.editors.config.js | 5 ++++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs index e447f7f493..9a11b0ef3e 100644 --- a/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs @@ -6,6 +6,7 @@ namespace Umbraco.Core.Configuration.Grid public interface IGridEditorConfig { string Name { get; } + string NameTemplate { get; } string Alias { get; } string View { get; } string Render { get; } diff --git a/src/Umbraco.Core/PropertyEditors/GridEditor.cs b/src/Umbraco.Core/PropertyEditors/GridEditor.cs index 986eed9ccc..cc3561fbc2 100644 --- a/src/Umbraco.Core/PropertyEditors/GridEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/GridEditor.cs @@ -18,6 +18,9 @@ namespace Umbraco.Core.PropertyEditors [JsonProperty("name", Required = Required.Always)] public string Name { get; set; } + [JsonProperty("nameTemplate")] + public string NameTemplate { get; set; } + [JsonProperty("alias", Required = Required.Always)] public string Alias { get; set; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index ccc390252b..0a4ab83dea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -8,7 +8,8 @@ angular.module("umbraco") angularHelper, $element, eventsService, - editorService + editorService, + $interpolate ) { // Grid status variables @@ -680,6 +681,11 @@ angular.module("umbraco") $scope.showRowConfigurations = !$scope.showRowConfigurations; }; + $scope.getTemplateName = function (control) { + if (control.editor.nameExp) return control.editor.nameExp(control) + return control.editor.name; + } + // ********************************************* // Initialization @@ -923,6 +929,11 @@ angular.module("umbraco") localizationService.localize("grid_" + value.alias, undefined, value.name).then(function (v) { value.name = v; }); + // setup nametemplate + + value.nameExp = !!value.nameTemplate + ? $interpolate(value.nameTemplate) + : undefined; }); $scope.contentReady = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index 6b739a9b86..3995ed703d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -182,7 +182,7 @@
- {{control.editor.name}} + {{ getTemplateName(control) }}
@@ -190,7 +190,7 @@
- {{control.editor.name}} + {{ getTemplateName(control) }}
diff --git a/src/Umbraco.Web.UI/config/grid.editors.config.js b/src/Umbraco.Web.UI/config/grid.editors.config.js index 12fa726f21..49b843689b 100644 --- a/src/Umbraco.Web.UI/config/grid.editors.config.js +++ b/src/Umbraco.Web.UI/config/grid.editors.config.js @@ -7,12 +7,14 @@ }, { "name": "Image", + "nameTemplate": "{{ 'Image: ' + (value.udi | ncNodeName) }}", "alias": "media", "view": "media", "icon": "icon-picture" }, { "name": "Macro", + "nameTemplate": "{{ 'Macro: ' + value.macroAlias }}", "alias": "macro", "view": "macro", "icon": "icon-settings-alt" @@ -25,6 +27,7 @@ }, { "name": "Headline", + "nameTemplate": "{{ 'Headline: ' + value }}", "alias": "headline", "view": "textstring", "icon": "icon-coin", @@ -43,4 +46,4 @@ "markup": "
#value#
" } } -] \ No newline at end of file +] From 4e95e13c5d06af5f3da9c33c12ddc931443e4450 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Sat, 12 Oct 2019 15:13:57 +0200 Subject: [PATCH 037/173] Reverted a comment update now interface change has also been reverted. --- src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index c0e6d548f0..041d3f6422 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.PropertyEditors bool CanCopy { get; } /// - /// Validates a property value using custom messages. + /// Validates a property value. /// /// The property value. /// A value indicating whether the property value is required. From 1a92dbfd8fee30689822a09a57a2365d40c1bc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Tue, 15 Oct 2019 09:28:25 +0200 Subject: [PATCH 038/173] Properly set / unset Culture flag instead of removing _all_ flags when "Vary by Culture" is false. --- .../Models/Mapping/ContentTypeMapDefinition.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index 528d5f6de5..9b8936a43c 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -223,7 +223,11 @@ namespace Umbraco.Web.Models.Mapping target.DataTypeKey = source.DataTypeKey; target.Mandatory = source.Validation.Mandatory; target.ValidationRegExp = source.Validation.Pattern; - target.Variations = source.AllowCultureVariant ? ContentVariation.Culture : ContentVariation.Nothing; + target.Variations = source.AllowCultureVariant + // Set culture flag + ? target.Variations | ContentVariation.Culture + // Unset culture flag + : target.Variations & ~ContentVariation.Culture; if (source.Id > 0) target.Id = source.Id; @@ -395,9 +399,11 @@ namespace Umbraco.Web.Models.Mapping if (!(target is IMemberType)) { - target.Variations = ContentVariation.Nothing; - if (source.AllowCultureVariant) - target.Variations |= ContentVariation.Culture; + target.Variations = source.AllowCultureVariant + // Set culture flag + ? target.Variations | ContentVariation.Culture + // Unset culture flag + : target.Variations & ~ContentVariation.Culture; } // handle property groups and property types From 10a2303458519fff6162d667cd14927f3a9ef79a Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 18 Oct 2019 10:19:11 +1100 Subject: [PATCH 039/173] Renames AsyncLock -> SystemLock --- src/Umbraco.Core/MainDom.cs | 4 +-- .../{AsyncLock.cs => SystemLock.cs} | 35 ++----------------- .../SafeXmlReaderWriter.cs | 2 +- .../LegacyXmlPublishedCache/XmlStore.cs | 2 +- .../XmlStoreFilePersister.cs | 2 +- 5 files changed, 8 insertions(+), 37 deletions(-) rename src/Umbraco.Core/{AsyncLock.cs => SystemLock.cs} (80%) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index d1012fb669..613abd6946 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core private readonly object _locko = new object(); // async lock representing the main domain lock - private readonly AsyncLock _asyncLock; + private readonly SystemLock _asyncLock; private IDisposable _asyncLocker; // event wait handle used to notify current main domain that it should @@ -68,7 +68,7 @@ namespace Umbraco.Core var hash = (appId + ":::" + appPath).ToSHA1(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; - _asyncLock = new AsyncLock(lockName); + _asyncLock = new SystemLock(lockName); var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT"; _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); diff --git a/src/Umbraco.Core/AsyncLock.cs b/src/Umbraco.Core/SystemLock.cs similarity index 80% rename from src/Umbraco.Core/AsyncLock.cs rename to src/Umbraco.Core/SystemLock.cs index 6dd866705e..e637c7b08f 100644 --- a/src/Umbraco.Core/AsyncLock.cs +++ b/src/Umbraco.Core/SystemLock.cs @@ -21,18 +21,18 @@ namespace Umbraco.Core // been closed, the Semaphore system object is destroyed - so in any case // an iisreset should clean up everything // - internal class AsyncLock + internal class SystemLock { private readonly SemaphoreSlim _semaphore; private readonly Semaphore _semaphore2; private readonly IDisposable _releaser; private readonly Task _releaserTask; - public AsyncLock() + public SystemLock() : this (null) { } - public AsyncLock(string name) + public SystemLock(string name) { // WaitOne() waits until count > 0 then decrements count // Release() increments count @@ -67,35 +67,6 @@ namespace Umbraco.Core : new NamedSemaphoreReleaser(_semaphore2); } - //NOTE: We don't use the "Async" part of this lock at all - //TODO: Remove this and rename this class something like SystemWideLock, then we can re-instate this logic if we ever need an Async lock again - - //public Task LockAsync() - //{ - // var wait = _semaphore != null - // ? _semaphore.WaitAsync() - // : _semaphore2.WaitOneAsync(); - - // return wait.IsCompleted - // ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named - // : wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()), - // this, CancellationToken.None, - // TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - //} - - //public Task LockAsync(int millisecondsTimeout) - //{ - // var wait = _semaphore != null - // ? _semaphore.WaitAsync(millisecondsTimeout) - // : _semaphore2.WaitOneAsync(millisecondsTimeout); - - // return wait.IsCompleted - // ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named - // : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()), - // this, CancellationToken.None, - // TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - //} - public IDisposable Lock() { if (_semaphore != null) diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs index c0b9383b57..aa88f28dc0 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return scopeProvider?.Context?.GetEnlisted(EnlistKey); } - public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, AsyncLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer) + public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, SystemLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer) { var scopeContext = scopeProvider.Context; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index 3b675c2f07..01f1ce264c 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -305,7 +305,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private XmlDocument _xmlDocument; // supplied xml document (for tests) private volatile XmlDocument _xml; // master xml document - private readonly AsyncLock _xmlLock = new AsyncLock(); // protects _xml + private readonly SystemLock _xmlLock = new SystemLock(); // protects _xml // to be used by PublishedContentCache only // for non-preview content only diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs index 145a19872a..56c09b18ac 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs @@ -24,7 +24,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private bool _released; private Timer _timer; private DateTime _initialTouch; - private readonly AsyncLock _runLock = new AsyncLock(); // ensure we run once at a time + private readonly SystemLock _runLock = new SystemLock(); // ensure we run once at a time // note: // as long as the runner controls the runs, we know that we run once at a time, but From 81ae83556493e8f4dc66fc08a40a3e62295b6582 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 18 Oct 2019 10:30:58 +1100 Subject: [PATCH 040/173] Implements Disposable pattern correctly for the NamedSemaphoreReleaser --- src/Umbraco.Core/SystemLock.cs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/SystemLock.cs b/src/Umbraco.Core/SystemLock.cs index e637c7b08f..a04eb2f980 100644 --- a/src/Umbraco.Core/SystemLock.cs +++ b/src/Umbraco.Core/SystemLock.cs @@ -100,6 +100,12 @@ namespace Umbraco.Core _handle = GCHandle.Alloc(_semaphore); } + #region IDisposable Support + + // This code added to correctly implement the disposable pattern. + + private bool disposedValue = false; // To detect redundant calls + public void Dispose() { Dispose(true); @@ -108,12 +114,21 @@ namespace Umbraco.Core private void Dispose(bool disposing) { - // critical - _handle.Free(); - _semaphore.Release(); - _semaphore.Dispose(); - } + if (!disposedValue) + { + if (disposing) + { + _semaphore.Release(); + _semaphore.Dispose(); + } + // free unmanaged resources (unmanaged objects) and override a finalizer below. + _handle.Free(); + + disposedValue = true; + } + } + // we WANT to release the semaphore because it's a system object, ie a critical // non-managed resource - and if it is not released then noone else can acquire // the lock - so we inherit from CriticalFinalizerObject which means that the @@ -142,6 +157,9 @@ namespace Umbraco.Core // we do NOT want the finalizer to throw - never ever } } + + #endregion + } private class SemaphoreSlimReleaser : IDisposable From b5f29f2390fa346a8edc098d5e42b0bef4ce89fe Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 18 Oct 2019 11:08:58 +1100 Subject: [PATCH 041/173] Ensure the EventWaitHandle is cleaned up, MainDom and BackgroundTaskRunner was not using HostingEnvironment.Stop correctly, ensures no exception is thrown when setting task result on a background task, ensure multiple terminations don't occur, ensure terminations are done if shutdown fails. --- src/Umbraco.Core/MainDom.cs | 41 ++++++++++++++++--- src/Umbraco.Core/SystemLock.cs | 2 + src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../Scheduling/BackgroundTaskRunner.cs | 41 ++++++++++++++----- 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 613abd6946..41ed8eb242 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core /// When an AppDomain starts, it tries to acquire the main domain status. /// When an AppDomain stops (eg the application is restarting) it should release the main domain status. /// - internal class MainDom : IMainDom, IRegisteredObject + internal class MainDom : IMainDom, IRegisteredObject, IDisposable { #region Vars @@ -204,14 +204,43 @@ namespace Umbraco.Core // IRegisteredObject void IRegisteredObject.Stop(bool immediate) { - try - { - OnSignal("environment"); // will run once - } - finally + OnSignal("environment"); // will run once + + if (immediate) { + //only unregister when it's the final call, else we won't be notified of the final call HostingEnvironment.UnregisterObject(this); + + // The web app is stopping immediately, dispose eagerly + Dispose(true); } } + + #region IDisposable Support + + // This code added to correctly implement the disposable pattern. + + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _signal?.Close(); + _signal?.Dispose(); + } + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(true); + } + + #endregion } } diff --git a/src/Umbraco.Core/SystemLock.cs b/src/Umbraco.Core/SystemLock.cs index a04eb2f980..04150d58c1 100644 --- a/src/Umbraco.Core/SystemLock.cs +++ b/src/Umbraco.Core/SystemLock.cs @@ -120,6 +120,8 @@ namespace Umbraco.Core { _semaphore.Release(); _semaphore.Dispose(); + + } // free unmanaged resources (unmanaged objects) and override a finalizer below. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c19722433f..2b9631c747 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -122,7 +122,7 @@ --> - + diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 47e97593f0..dab6a8865e 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -700,16 +700,23 @@ namespace Umbraco.Web.Scheduling // processing asynchronously before calling the UnregisterObject method. _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); - Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait - // raise the completed event only after the running threading task has completed - lock (_locker) + try { - if (_runningTask != null) - _runningTask.ContinueWith(_ => Terminate(false)); - else - Terminate(false); + Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait } + finally + { + // raise the completed event only after the running threading task has completed + lock (_locker) + { + if (_runningTask != null) + _runningTask.ContinueWith(_ => Terminate(false)); + else + Terminate(false); + } + } + } else { @@ -719,8 +726,14 @@ namespace Umbraco.Web.Scheduling // otherwise, its registration will be removed by the application manager. _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); - Shutdown(true, true); // cancel all tasks, wait for the current one to end - Terminate(true); + try + { + Shutdown(true, true); // cancel all tasks, wait for the current one to end + } + finally + { + Terminate(true); + } } } @@ -732,7 +745,13 @@ namespace Umbraco.Web.Scheduling // raise the Terminated event // complete the awaitable completion source, if any - HostingEnvironment.UnregisterObject(this); + if (immediate) + { + //only unregister when it's the final call, else we won't be notified of the final call + HostingEnvironment.UnregisterObject(this); + } + + if (_terminated) return; // already taken care of TaskCompletionSource terminatedSource; lock (_locker) @@ -747,7 +766,7 @@ namespace Umbraco.Web.Scheduling OnEvent(Terminated, "Terminated"); - terminatedSource.SetResult(0); + terminatedSource.TrySetResult(0); } } } From 44ed611b7a3f5bc2542df9823a572cb8a9914391 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 18 Oct 2019 11:45:09 +1100 Subject: [PATCH 042/173] Ensure MainDom is always registered with HostingEnvironment, not just when it acquires maindom --- src/Umbraco.Core/MainDom.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 41ed8eb242..10096567f3 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -25,8 +25,8 @@ namespace Umbraco.Core private readonly object _locko = new object(); // async lock representing the main domain lock - private readonly SystemLock _asyncLock; - private IDisposable _asyncLocker; + private readonly SystemLock _systemLock; + private IDisposable _systemLocker; // event wait handle used to notify current main domain that it should // release the lock because a new domain wants to be the main domain @@ -48,6 +48,8 @@ namespace Umbraco.Core // initializes a new instance of MainDom public MainDom(ILogger logger) { + HostingEnvironment.RegisterObject(this); + _logger = logger; var appId = string.Empty; @@ -68,7 +70,7 @@ namespace Umbraco.Core var hash = (appId + ":::" + appPath).ToSHA1(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; - _asyncLock = new SystemLock(lockName); + _systemLock = new SystemLock(lockName); var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT"; _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); @@ -142,7 +144,7 @@ namespace Umbraco.Core { // in any case... _isMainDom = false; - _asyncLocker.Dispose(); + _systemLocker?.Dispose(); _logger.Info("Released ({SignalSource})", source); } } @@ -173,7 +175,7 @@ namespace Umbraco.Core // and the other one will timeout, which is accepted //TODO: This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset? - _asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds); + _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); _isMainDom = true; // we need to reset the event, because otherwise we would end up @@ -189,8 +191,6 @@ namespace Umbraco.Core _signal.WaitOneAsync() .ContinueWith(_ => OnSignal("signal")); - HostingEnvironment.RegisterObject(this); - _logger.Info("Acquired."); return true; } From beb16a2f79b847dcc2cb6de5752d7e56b2ede3ce Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 15:47:37 +1100 Subject: [PATCH 043/173] Ensures MainDom.Register absolutely does not call install logic if it's not MainDom, adds logging --- src/Umbraco.Core/MainDom.cs | 6 ++++++ .../PublishedCache/NuCache/PublishedSnapshotService.cs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 10096567f3..e4beafc110 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -101,6 +101,12 @@ namespace Umbraco.Core lock (_locko) { if (_signaled) return false; + if (_isMainDom == false) + { + _logger.Warn("Register called when MainDom has not been acquired"); + return false; + } + install?.Invoke(); if (release != null) _callbacks.Add(new KeyValuePair(weight, release)); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 6e7916c77f..3fcf61a7bc 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -132,6 +132,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // if both local databases exist then GetTree will open them, else new databases will be created _localContentDb = BTree.GetTree(localContentDbPath, _localDbExists); _localMediaDb = BTree.GetTree(localMediaDbPath, _localDbExists); + + _logger.Info($"Registered with MainDom, local db exists? {_localDbExists}"); }, () => { @@ -144,6 +146,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _mediaStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned _localMediaDb = null; } + + _logger.Info("Released from MainDom"); }); // stores are created with a db so they can write to it, but they do not read from it, From eb670fd1ac91c8d1074a2897cf0c253783dfe0e8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 15:59:25 +1100 Subject: [PATCH 044/173] ensure OnSignal logic is all run within the lock, ensures all actions are run even if one throw on shutdown. --- src/Umbraco.Core/MainDom.cs | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index e4beafc110..f2505c3f78 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -126,32 +126,32 @@ namespace Umbraco.Core if (_signaled) return; if (_isMainDom == false) return; // probably not needed _signaled = true; - } - try - { - _logger.Info("Stopping ({SignalSource})", source); - foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) + try { - try + _logger.Info("Stopping ({SignalSource})", source); + foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) { - callback(); // no timeout on callbacks + try + { + callback(); // no timeout on callbacks + } + catch (Exception e) + { + _logger.Error(e, "Error while running callback"); + continue; + } } - catch (Exception e) - { - _logger.Error(e, "Error while running callback, remaining callbacks will not run."); - throw; - } - + _logger.Debug("Stopped ({SignalSource})", source); } - _logger.Debug("Stopped ({SignalSource})", source); - } - finally - { - // in any case... - _isMainDom = false; - _systemLocker?.Dispose(); - _logger.Info("Released ({SignalSource})", source); + finally + { + // in any case... + _isMainDom = false; + _systemLocker?.Dispose(); + _logger.Info("Released ({SignalSource})", source); + } + } } @@ -211,7 +211,7 @@ namespace Umbraco.Core void IRegisteredObject.Stop(bool immediate) { OnSignal("environment"); // will run once - + if (immediate) { //only unregister when it's the final call, else we won't be notified of the final call @@ -241,7 +241,7 @@ namespace Umbraco.Core disposedValue = true; } } - + public void Dispose() { Dispose(true); From 7afdf9e9a94ffb81828c35ad75345ee86132ab4d Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 11:40:51 +1100 Subject: [PATCH 045/173] ensure maindom is disposed as soon as the hosting environment is signaled, remove the GCHandle --- src/Umbraco.Core/MainDom.cs | 6 +++--- src/Umbraco.Core/SystemLock.cs | 23 +++++++++++------------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index f2505c3f78..ccecb4aa82 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -212,13 +212,13 @@ namespace Umbraco.Core { OnSignal("environment"); // will run once + // The web app is stopping, dispose eagerly + Dispose(true); + if (immediate) { //only unregister when it's the final call, else we won't be notified of the final call HostingEnvironment.UnregisterObject(this); - - // The web app is stopping immediately, dispose eagerly - Dispose(true); } } diff --git a/src/Umbraco.Core/SystemLock.cs b/src/Umbraco.Core/SystemLock.cs index 04150d58c1..4eaae7082b 100644 --- a/src/Umbraco.Core/SystemLock.cs +++ b/src/Umbraco.Core/SystemLock.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core private readonly Task _releaserTask; public SystemLock() - : this (null) + : this(null) { } public SystemLock(string name) @@ -92,12 +92,10 @@ namespace Umbraco.Core private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable { private readonly Semaphore _semaphore; - private GCHandle _handle; internal NamedSemaphoreReleaser(Semaphore semaphore) { _semaphore = semaphore; - _handle = GCHandle.Alloc(_semaphore); } #region IDisposable Support @@ -116,21 +114,22 @@ namespace Umbraco.Core { if (!disposedValue) { - if (disposing) + try { _semaphore.Release(); - _semaphore.Dispose(); - - } - - // free unmanaged resources (unmanaged objects) and override a finalizer below. - _handle.Free(); - + finally + { + try + { + _semaphore.Dispose(); + } + catch { } + } disposedValue = true; } } - + // we WANT to release the semaphore because it's a system object, ie a critical // non-managed resource - and if it is not released then noone else can acquire // the lock - so we inherit from CriticalFinalizerObject which means that the From 7ace5baf9b2439ced8d86251e65a5a5be8b308da Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Thu, 17 Oct 2019 18:46:11 +0100 Subject: [PATCH 046/173] Allow nucache content and/or media db files to be reused if they already exist. --- src/Umbraco.Core/Runtime/CoreRuntime.cs | 28 ++++++++++--------- .../NuCache/PublishedSnapshotService.cs | 8 ++++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 5b069641c4..5cca74124d 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -140,23 +140,25 @@ namespace Umbraco.Core.Runtime Compose(composition); // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - AcquireMainDom(mainDom); + if (AcquireMainDom(mainDom)) + { + // determine our runtime level + DetermineRuntimeLevel(databaseFactory, ProfilingLogger); - // determine our runtime level - DetermineRuntimeLevel(databaseFactory, ProfilingLogger); + // get composers, and compose + var composerTypes = ResolveComposerTypes(typeLoader); + composition.WithCollectionBuilder(); + var composers = new Composers(composition, composerTypes, ProfilingLogger); + composers.Compose(); - // get composers, and compose - var composerTypes = ResolveComposerTypes(typeLoader); - composition.WithCollectionBuilder(); - var composers = new Composers(composition, composerTypes, ProfilingLogger); - composers.Compose(); + // create the factory + _factory = Current.Factory = composition.CreateFactory(); - // create the factory - _factory = Current.Factory = composition.CreateFactory(); + // create & initialize the components + _components = _factory.GetInstance(); + _components.Initialize(); + } - // create & initialize the components - _components = _factory.GetInstance(); - _components.Initialize(); } catch (Exception e) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 3fcf61a7bc..81a3aa9e21 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -128,10 +128,12 @@ namespace Umbraco.Web.PublishedCache.NuCache var path = GetLocalFilesPath(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); - _localDbExists = File.Exists(localContentDbPath) && File.Exists(localMediaDbPath); + var localContentDbExists = File.Exists(localContentDbPath); + var localMediaDbExists = File.Exists(localMediaDbPath); + _localDbExists = localContentDbExists && localMediaDbExists; // if both local databases exist then GetTree will open them, else new databases will be created - _localContentDb = BTree.GetTree(localContentDbPath, _localDbExists); - _localMediaDb = BTree.GetTree(localMediaDbPath, _localDbExists); + _localContentDb = BTree.GetTree(localContentDbPath, localContentDbExists); + _localMediaDb = BTree.GetTree(localMediaDbPath, localMediaDbExists); _logger.Info($"Registered with MainDom, local db exists? {_localDbExists}"); }, From e998fce5d1cecf1592f72c8bfb378af9060d3769 Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Thu, 17 Oct 2019 19:00:00 +0100 Subject: [PATCH 047/173] Trying to lock could throw exceptions so always make sure to properly clean up the local DB. --- .../PublishedCache/NuCache/ContentStore.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 1bd58c3878..3fe4b8aecd 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -234,11 +234,17 @@ namespace Umbraco.Web.PublishedCache.NuCache var lockInfo = new WriteLockInfo(); try { - Lock(lockInfo); - - if (_localDb == null) return; - _localDb.Dispose(); - _localDb = null; + try{ + // Trying to lock could throw exceptions so always make sure to clean up. + Lock(lockInfo); + } + catch + { + if (_localDb == null) return; + _localDb.Dispose(); + _localDb = null; + } + } finally { From ee098e019451cc3da11465e18f6b464598ebd64d Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Thu, 17 Oct 2019 19:06:28 +0100 Subject: [PATCH 048/173] Make sure the local dbs are disposed of if the content store was not created. --- .../PublishedCache/NuCache/PublishedSnapshotService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 81a3aa9e21..e08a0e3c52 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -144,8 +144,11 @@ namespace Umbraco.Web.PublishedCache.NuCache lock (_storesLock) { _contentStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned + // Make sure the local dbs are disposed of if the content store was not created. + _localContentDb?.Dispose(); _localContentDb = null; _mediaStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned + _localMediaDb?.Dispose(); _localMediaDb = null; } From 77f34e84665cced396a3924390bd3edc59eba62c Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Thu, 17 Oct 2019 19:32:21 +0100 Subject: [PATCH 049/173] Always reset the signal on a timeout exception waiting on the lock. --- src/Umbraco.Core/MainDom.cs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index ccecb4aa82..ae512241e1 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -180,17 +180,27 @@ namespace Umbraco.Core // if more than 1 instance reach that point, one will get the lock // and the other one will timeout, which is accepted - //TODO: This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset? - _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); + //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. + try + { + _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); + } + catch + { + throw; + } + finally + { + // we need to reset the event, because otherwise we would end up + // signaling ourselves and committing suicide immediately. + // only 1 instance can reach that point, but other instances may + // have started and be trying to get the lock - they will timeout, + // which is accepted + + _signal.Reset(); + } _isMainDom = true; - - // we need to reset the event, because otherwise we would end up - // signaling ourselves and committing suicide immediately. - // only 1 instance can reach that point, but other instances may - // have started and be trying to get the lock - they will timeout, - // which is accepted - - _signal.Reset(); + //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread From d774ff8bc15bd549caee73bc2b149b13df84d262 Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Fri, 18 Oct 2019 07:17:56 +0100 Subject: [PATCH 050/173] Undoing a bad previous change where logic in a catch should have been in a finally. Also dispose could throw an error so catch it and complete the clean up. --- .../PublishedCache/NuCache/ContentStore.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 3fe4b8aecd..179b262568 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -238,11 +238,21 @@ namespace Umbraco.Web.PublishedCache.NuCache // Trying to lock could throw exceptions so always make sure to clean up. Lock(lockInfo); } - catch + catch { throw; } + finally { - if (_localDb == null) return; - _localDb.Dispose(); - _localDb = null; + if (_localDb != null) + { + try + { + _localDb.Dispose(); + } + catch { /* TBD: May already be throwing so don't throw again */} + finally + { + _localDb = null; + } + } } } From 478bc708b958a9efc6a59b6f1e55aec53e8b1eab Mon Sep 17 00:00:00 2001 From: JohnBlair Date: Fri, 18 Oct 2019 07:31:15 +0100 Subject: [PATCH 051/173] Defensive programming around disposal of local content and media dbs. --- .../NuCache/PublishedSnapshotService.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index e08a0e3c52..d66bcd806b 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -145,10 +145,18 @@ namespace Umbraco.Web.PublishedCache.NuCache { _contentStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned // Make sure the local dbs are disposed of if the content store was not created. - _localContentDb?.Dispose(); + try + { + _localContentDb?.Dispose(); + } + catch { /* Carry on with cleanup */ } _localContentDb = null; _mediaStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned - _localMediaDb?.Dispose(); + try + { + _localMediaDb?.Dispose(); + } + catch { } _localMediaDb = null; } From b04f9c17ae74458b4203f9a875be03363cad1824 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 11:55:05 +1100 Subject: [PATCH 052/173] Makes some updates based on code reviews --- src/Umbraco.Core/MainDom.cs | 6 +--- src/Umbraco.Core/Runtime/CoreRuntime.cs | 30 +++++++++---------- .../PublishedCache/NuCache/ContentStore.cs | 30 +++++++++---------- .../NuCache/PublishedSnapshotService.cs | 22 ++++++++------ 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index ae512241e1..79c6f08bd1 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -184,11 +184,7 @@ namespace Umbraco.Core try { _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); - } - catch - { - throw; - } + } finally { // we need to reset the event, because otherwise we would end up diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 5cca74124d..5839ba6cfd 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -140,24 +140,24 @@ namespace Umbraco.Core.Runtime Compose(composition); // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - if (AcquireMainDom(mainDom)) - { - // determine our runtime level - DetermineRuntimeLevel(databaseFactory, ProfilingLogger); + AcquireMainDom(mainDom); - // get composers, and compose - var composerTypes = ResolveComposerTypes(typeLoader); - composition.WithCollectionBuilder(); - var composers = new Composers(composition, composerTypes, ProfilingLogger); - composers.Compose(); + // determine our runtime level + DetermineRuntimeLevel(databaseFactory, ProfilingLogger); - // create the factory - _factory = Current.Factory = composition.CreateFactory(); + // get composers, and compose + var composerTypes = ResolveComposerTypes(typeLoader); + composition.WithCollectionBuilder(); + var composers = new Composers(composition, composerTypes, ProfilingLogger); + composers.Compose(); + + // create the factory + _factory = Current.Factory = composition.CreateFactory(); + + // create & initialize the components + _components = _factory.GetInstance(); + _components.Initialize(); - // create & initialize the components - _components = _factory.GetInstance(); - _components.Initialize(); - } } catch (Exception e) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 179b262568..6cf34f68bb 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -128,7 +128,8 @@ namespace Umbraco.Web.PublishedCache.NuCache Monitor.Enter(_rlocko, ref rtaken); // see SnapDictionary - try { } finally + try { } + finally { _wlocked++; lockInfo.Count = true; @@ -234,27 +235,24 @@ namespace Umbraco.Web.PublishedCache.NuCache var lockInfo = new WriteLockInfo(); try { - try{ + try + { // Trying to lock could throw exceptions so always make sure to clean up. Lock(lockInfo); } - catch { throw; } - finally + finally { - if (_localDb != null) + try { - try - { - _localDb.Dispose(); - } - catch { /* TBD: May already be throwing so don't throw again */} - finally - { - _localDb = null; - } + _localDb?.Dispose(); + } + catch { /* TBD: May already be throwing so don't throw again */} + finally + { + _localDb = null; } } - + } finally { @@ -294,7 +292,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public void UpdateContentTypes(IEnumerable types) { //nothing to do if this is empty, no need to lock/allocate/iterate/etc... - if (!types.Any()) return; + if (!types.Any()) return; var lockInfo = new WriteLockInfo(); try diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index d66bcd806b..d89ff47313 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -57,7 +57,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private BPlusTree _localContentDb; private BPlusTree _localMediaDb; - private bool _localDbExists; + private bool _localContentDbExists; + private bool _localMediaDbExists; // define constant - determines whether to use cache when previewing // to store eg routes, property converted values, anything - caching @@ -128,14 +129,13 @@ namespace Umbraco.Web.PublishedCache.NuCache var path = GetLocalFilesPath(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); - var localContentDbExists = File.Exists(localContentDbPath); - var localMediaDbExists = File.Exists(localMediaDbPath); - _localDbExists = localContentDbExists && localMediaDbExists; + _localContentDbExists = File.Exists(localContentDbPath); + _localMediaDbExists = File.Exists(localMediaDbPath); // if both local databases exist then GetTree will open them, else new databases will be created - _localContentDb = BTree.GetTree(localContentDbPath, localContentDbExists); - _localMediaDb = BTree.GetTree(localMediaDbPath, localMediaDbExists); + _localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists); + _localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists); - _logger.Info($"Registered with MainDom, local db exists? {_localDbExists}"); + _logger.Info($"Registered with MainDom, local content db exists? {_localContentDbExists}, local media db exists? {_localMediaDbExists}"); }, () => { @@ -200,11 +200,15 @@ namespace Umbraco.Web.PublishedCache.NuCache var okContent = false; var okMedia = false; - if (_localDbExists) + if (_localContentDbExists) { okContent = LockAndLoadContent(LoadContentFromLocalDbLocked); if (!okContent) - _logger.Warn("Loading content from local db raised warnings, will reload from database."); + _logger.Warn("Loading content from local db raised warnings, will reload from database."); + } + + if (_localMediaDbExists) + { okMedia = LockAndLoadMedia(LoadMediaFromLocalDbLocked); if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); From 8a18a5b1cbe94180b7245cce5c145eeebdce347a Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 12:13:40 +1100 Subject: [PATCH 053/173] ensure there is no casing issues with app physical path when generating a hash --- src/Umbraco.Core/MainDom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 79c6f08bd1..2fda0b2cb0 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -66,7 +66,7 @@ namespace Umbraco.Core // we *cannot* use the process ID here because when an AppPool restarts it is // a new process for the same application path - var appPath = HostingEnvironment.ApplicationPhysicalPath; + var appPath = HostingEnvironment.ApplicationPhysicalPath.ToLowerInvariant(); var hash = (appId + ":::" + appPath).ToSHA1(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; From 0ee9c2d327a1e8629db99a0292732175eec205df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 22 Oct 2019 15:22:34 +0200 Subject: [PATCH 054/173] ability to disable confirm button in umb-confirm --- src/Umbraco.Web.UI.Client/src/views/components/umb-confirm.html | 1 + 1 file changed, 1 insertion(+) 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 384c5ccaf7..d8e4f09b8a 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,6 +14,7 @@ action="confirm()" button-style="{{confirmButtonStyle || 'primary'}}" state="confirmButtonState" + disabled="confirmDisabled === true" label-key="{{confirmLabelKey || 'general_ok'}}">
From 98df5eae6739fd1529910028e2fb687289af2718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 22 Oct 2019 15:22:52 +0200 Subject: [PATCH 055/173] never underline icons in table --- src/Umbraco.Web.UI.Client/src/less/components/umb-table.less | 1 + 1 file changed, 1 insertion(+) 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 5e766b7578..94c0318fca 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 @@ -161,6 +161,7 @@ input.umb-table__input { line-height: 20px; color: @ui-option-type; vertical-align: bottom; + text-decoration: none; } .umb-table-body__checkicon, From 0f07ecba5463257d4b641e669f2c46ce2ffaf761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 22 Oct 2019 15:23:46 +0200 Subject: [PATCH 056/173] umb-confirm ability to set wether the confirm button is disabled via confirmDisabled --- .../src/common/directives/components/umbconfirm.directive.js | 1 + 1 file changed, 1 insertion(+) 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 1ddd09357a..9114cfb1c1 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 @@ -56,6 +56,7 @@ function confirmDirective() { onCancel: '=', caption: '@', confirmButtonStyle: '@', + confirmDisabled: ' Date: Tue, 22 Oct 2019 15:24:36 +0200 Subject: [PATCH 057/173] links to edit references --- .../src/views/datatypes/delete.html | 20 +++++++++---------- .../datatypes/views/datatype.references.html | 4 ++-- 2 files changed, 12 insertions(+), 12 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 6c415e9f38..32140cf6f2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -49,9 +49,9 @@ - - {{::relation.name}} - {{::property.name}}{{$last ? '' : ', '}} + + {{::reference.name}} + {{::property.name}}{{$last ? '' : ', '}} @@ -73,9 +73,9 @@ - - {{::relation.name}} - {{::property.name}}{{$last ? '' : ', '}} + + {{::reference.name}} + {{::property.name}}{{$last ? '' : ', '}} @@ -97,9 +97,9 @@ - - {{::relation.name}} - {{::property.name}}{{$last ? '' : ', '}} + + {{::reference.name}} + {{::property.name}}{{$last ? '' : ', '}} @@ -112,7 +112,7 @@
- {{::reference.name}}
{{::reference.alias}}
{{::reference.properties | umbCmsJoinArray:', ':'name'}}
- + @@ -98,7 +98,7 @@
{{::reference.name}}
{{::reference.alias}}
{{::reference.properties | umbCmsJoinArray:', ':'name'}}
- + From 89e6cc3611c688177b7b405da40f35a5680b355d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 22 Oct 2019 15:26:59 +0200 Subject: [PATCH 058/173] do close dialog, even thought people might open in a tab. --- 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 32140cf6f2..aedb7c7545 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -50,7 +50,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} @@ -74,7 +74,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} @@ -98,7 +98,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} From 6776d6cde6d4e73a87c1eae23078aa715fc74999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 22 Oct 2019 15:35:50 +0200 Subject: [PATCH 059/173] hide dialog if opening reference in this tab/window. --- .../src/views/datatypes/datatype.delete.controller.js | 6 ++++++ src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html | 6 +++--- 2 files changed, 9 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 c6c58d9fa6..4542cde343 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 @@ -51,6 +51,12 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat navigationService.hideDialog(); }; + vm.onReferenceClicked = function(event) { + if (event.metaKey !== true) { + navigationService.hideDialog(); + } + }; + vm.labels = {}; localizationService .localize("editdatatype_acceptDeleteConsequence", [$scope.currentNode.name]) 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 aedb7c7545..52adfcccec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/delete.html @@ -50,7 +50,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} @@ -74,7 +74,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} @@ -98,7 +98,7 @@ - {{::reference.name}} + {{::reference.name}} {{::property.name}}{{$last ? '' : ', '}} From fff646ad15c61c793fb42a16707ef25969ce4a6d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 14:12:23 +1100 Subject: [PATCH 060/173] MainDom will terminate on first (or only) call to Stop --- src/Umbraco.Core/MainDom.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 2fda0b2cb0..e54bd0dcb3 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -218,14 +218,10 @@ namespace Umbraco.Core { OnSignal("environment"); // will run once - // The web app is stopping, dispose eagerly + // The web app is stopping, need to wind down Dispose(true); - if (immediate) - { - //only unregister when it's the final call, else we won't be notified of the final call - HostingEnvironment.UnregisterObject(this); - } + HostingEnvironment.UnregisterObject(this); } #region IDisposable Support From 31ddc1d935a02169e8f27ed1ede0814a20788d40 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 14:17:30 +1100 Subject: [PATCH 061/173] Changes BackgroundTaskRunner to shutdown faster and to ensures that any latched tasks are canceled even with Stop(immediate == false) is executed. --- .../Scheduling/BackgroundTaskRunner.cs | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index dab6a8865e..e518f49ae0 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -338,14 +338,18 @@ namespace Umbraco.Web.Scheduling if (_isRunning == false) return; // done already } + var hasTasks = _tasks.Count > 0; + + if (!force && hasTasks) + _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); + // complete the queue // will stop waiting on the queue or on a latch _tasks.Complete(); - if (force) + if (!hasTasks || force) { - // we must bring everything down, now - Thread.Sleep(100); // give time to Complete() + // we must bring everything down, now lock (_locker) { // was Complete() enough? @@ -354,13 +358,13 @@ namespace Umbraco.Web.Scheduling // try to cancel running async tasks (cannot do much about sync tasks) // break latched tasks // stop processing the queue - _shutdownTokenSource.Cancel(false); // false is the default - _shutdownTokenSource.Dispose(); + _shutdownTokenSource?.Cancel(false); // false is the default + _shutdownTokenSource?.Dispose(); _shutdownTokenSource = null; } // tasks in the queue will be executed... - if (wait == false) return; + if (!wait) return; _runningTask?.Wait(CancellationToken.None); // wait for whatever is running to end... } @@ -503,7 +507,7 @@ namespace Umbraco.Web.Scheduling // returns the task that completed // - latched.Latch completes when the latch releases // - _tasks.Completion completes when the runner completes - // - tokenTaskSource.Task completes when this task, or the whole runner, is cancelled + // - tokenTaskSource.Task completes when this task, or the whole runner is cancelled var task = await Task.WhenAny(latched.Latch, _tasks.Completion, tokenTaskSource.Task); // ok to run now @@ -693,13 +697,11 @@ namespace Umbraco.Web.Scheduling if (onTerminating) OnEvent(Terminating, "Terminating"); - if (immediate == false) + if (!immediate) { - // The Stop method is first called with the immediate parameter set to false. The object can either complete - // processing, call the UnregisterObject method, and then return or it can return immediately and complete - // processing asynchronously before calling the UnregisterObject method. - - _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); + // immediate == false when the app is trying to wind down, immediate == true will be called either: + // after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop. + // So Stop may be called twice or sometimes only once. try { @@ -716,24 +718,32 @@ namespace Umbraco.Web.Scheduling Terminate(false); } } - + + // If we are called with immediate == false, wind down above and then shutdown within 2 seconds, + // we want to shut down the app as quick as possible, if we wait until immediate == true, this can + // take a very long time since immediate will only be true when a new request is received on the new + // appdomain (or another iis timeout occurs ... which can take soeme time). + Task.Delay(2000, _shutdownToken).ContinueWith(_ => StopImmediate()); + } else { - // If the registered object does not complete processing before the application manager's time-out - // period expires, the Stop method is called again with the immediate parameter set to true. When the - // immediate parameter is true, the registered object must call the UnregisterObject method before returning; - // otherwise, its registration will be removed by the application manager. + // If we are called with immediate == true - cancel and shut down now. - _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); - try - { - Shutdown(true, true); // cancel all tasks, wait for the current one to end - } - finally - { - Terminate(true); - } + StopImmediate(); + } + } + + private void StopImmediate() + { + _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); + try + { + Shutdown(true, true); // cancel all tasks, wait for the current one to end + } + finally + { + Terminate(true); } } From 52b0edf1f50e1f2d4ef60c1a36b43d3edd15220e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 24 Oct 2019 14:24:01 +1100 Subject: [PATCH 062/173] For backgroundtaskrunner, run shutdown on a threadpool thread to not block the shutdown of other IRegisteredObjects --- .../Scheduling/BackgroundTaskRunner.cs | 80 ++++++++++++------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index e518f49ae0..403ffd47a8 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -697,43 +697,69 @@ namespace Umbraco.Web.Scheduling if (onTerminating) OnEvent(Terminating, "Terminating"); + // Run the Stop commands on another thread since IRegisteredObject.Stop calls are called sequentially + // with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop. if (!immediate) { - // immediate == false when the app is trying to wind down, immediate == true will be called either: - // after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop. - // So Stop may be called twice or sometimes only once. - - try - { - Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait - } - finally - { - // raise the completed event only after the running threading task has completed - lock (_locker) - { - if (_runningTask != null) - _runningTask.ContinueWith(_ => Terminate(false)); - else - Terminate(false); - } - } - - // If we are called with immediate == false, wind down above and then shutdown within 2 seconds, - // we want to shut down the app as quick as possible, if we wait until immediate == true, this can - // take a very long time since immediate will only be true when a new request is received on the new - // appdomain (or another iis timeout occurs ... which can take soeme time). - Task.Delay(2000, _shutdownToken).ContinueWith(_ => StopImmediate()); - + Task.Run(StopInitial, CancellationToken.None); } else { - // If we are called with immediate == true - cancel and shut down now. + lock(_locker) + { + if (_terminated) return; + Task.Run(StopImmediate, CancellationToken.None); + } + } + } + /// + /// Called when immediate == false for IRegisteredObject.Stop(bool immediate) + /// + /// + /// Called on a threadpool thread + /// + private void StopInitial() + { + // immediate == false when the app is trying to wind down, immediate == true will be called either: + // after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop. + // So Stop may be called twice or sometimes only once. + + try + { + Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait + } + finally + { + // raise the completed event only after the running threading task has completed + lock (_locker) + { + if (_runningTask != null) + _runningTask.ContinueWith(_ => Terminate(false)); + else + Terminate(false); + } + } + + // If the shutdown token was not canceled in the Shutdown call above, it means there was still tasks + // being processed, in which case we'll give it a couple seconds + if (!_shutdownToken.IsCancellationRequested) + { + // If we are called with immediate == false, wind down above and then shutdown within 2 seconds, + // we want to shut down the app as quick as possible, if we wait until immediate == true, this can + // take a very long time since immediate will only be true when a new request is received on the new + // appdomain (or another iis timeout occurs ... which can take some time). + Thread.Sleep(2000); //we are already on a threadpool thread StopImmediate(); } } + /// + /// Called when immediate == true for IRegisteredObject.Stop(bool immediate) + /// + /// + /// Called on a threadpool thread + /// private void StopImmediate() { _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); From f513ed547699ff3b2be931e547aaa515c4983aad Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 24 Oct 2019 14:35:30 +1100 Subject: [PATCH 063/173] Changes call transition from StopInitial to StopImmediate instead of terminate to ensure that all cancelation tokens are canceled and cleared --- src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 403ffd47a8..4158594109 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -735,9 +735,9 @@ namespace Umbraco.Web.Scheduling lock (_locker) { if (_runningTask != null) - _runningTask.ContinueWith(_ => Terminate(false)); + _runningTask.ContinueWith(_ => StopImmediate()); else - Terminate(false); + StopImmediate(); } } From f20394556cb59bafe5fbba30c5c60db7c1c31d1f Mon Sep 17 00:00:00 2001 From: elitsa Date: Fri, 25 Oct 2019 11:58:04 +0200 Subject: [PATCH 064/173] Removed trailing newlines --- .../src/views/propertyeditors/grid/grid.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 0a4ab83dea..32f495e32b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -662,7 +662,6 @@ angular.module("umbraco") return ((spans / $scope.model.config.items.columns) * 100).toFixed(8); }; - $scope.clearPrompt = function (scopedObject, e) { scopedObject.deletePrompt = false; e.preventDefault(); @@ -686,7 +685,6 @@ angular.module("umbraco") return control.editor.name; } - // ********************************************* // Initialization // these methods are called from ng-init on the template From 76bbf01cbe647708583f8284a010f014752cbab0 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Tue, 29 Oct 2019 15:25:10 +0000 Subject: [PATCH 065/173] Changed field to a List as suggested in review --- src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs | 2 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 87b4400d58..5c9c965cd1 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -54,7 +54,7 @@ namespace Umbraco.Tests.UmbracoExamine { var runtimeState = Mock.Of(); Mock.Get(runtimeState).Setup(x => x.Level).Returns(level); - Mock.Get(runtimeState).SetupGet(m => m.ApplicationUrl).Returns(new Uri("https://LocalHost/umbraco")); + Mock.Get(runtimeState).SetupGet(m => m.ApplicationUrl).Returns(new Uri("https://localhost/umbraco")); return runtimeState; } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 7a62097490..463f4b09df 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -67,7 +67,7 @@ namespace Umbraco.Web.Search string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; - var fields = new[] { "id", "__NodeId", "__Key" }; + var fields = new List { "id", "__NodeId", "__Key" }; // TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string // manipulation for things like start paths, member types, etc... @@ -87,7 +87,7 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Member: indexName = Constants.UmbracoIndexes.MembersIndexName; type = "member"; - fields = new[] { "id", "__NodeId", "__Key", "email", "loginName" }; + fields.AddRange(new[]{ "email", "loginName"}); if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") { sb.Append("+__NodeTypeAlias:"); @@ -97,7 +97,7 @@ namespace Umbraco.Web.Search break; case UmbracoEntityTypes.Media: type = "media"; - fields = new[] { "id", "__NodeId", UmbracoExamineIndex.UmbracoFileFieldName}; + fields.AddRange(new[] { UmbracoExamineIndex.UmbracoFileFieldName }); var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; @@ -162,7 +162,7 @@ namespace Umbraco.Web.Search return _mapper.MapEnumerable(results); } - private bool BuildQuery(StringBuilder sb, string query, string searchFrom, string[] fields, string type) + private bool BuildQuery(StringBuilder sb, string query, string searchFrom, List fields, string type) { //build a lucene query: // the nodeName will be boosted 10x without wildcards From 965d6cbeb72ee426031488d6dad861e3fe3cc7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Knippers?= Date: Wed, 30 Oct 2019 10:47:08 +0100 Subject: [PATCH 066/173] Added enum extension methods + tests for SetFlag and UnsetFlag. --- src/Umbraco.Core/EnumExtensions.cs | 38 +++++++++++++++++ .../CoreThings/EnumExtensionsTests.cs | 42 +++++++++++++++++++ .../Mapping/ContentTypeMapDefinition.cs | 16 +++---- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/EnumExtensions.cs b/src/Umbraco.Core/EnumExtensions.cs index 1443a27876..b2e5f32c9a 100644 --- a/src/Umbraco.Core/EnumExtensions.cs +++ b/src/Umbraco.Core/EnumExtensions.cs @@ -41,5 +41,43 @@ namespace Umbraco.Core return (num & nums) > 0; } + + /// + /// Sets a flag of the given input enum + /// + /// + /// Enum to set flag of + /// Flag to set + /// A new enum with the flag set + public static T SetFlag(this T input, T flag) + where T : Enum + { + var i = Convert.ToUInt64(input); + var f = Convert.ToUInt64(flag); + + // bitwise OR to set flag f of enum i + var result = i | f; + + return (T)Enum.ToObject(typeof(T), result); + } + + /// + /// Unsets a flag of the given input enum + /// + /// + /// Enum to unset flag of + /// Flag to unset + /// A new enum with the flag unset + public static T UnsetFlag(this T input, T flag) + where T : Enum + { + var i = Convert.ToUInt64(input); + var f = Convert.ToUInt64(flag); + + // bitwise AND combined with bitwise complement to unset flag f of enum i + var result = i & ~f; + + return (T)Enum.ToObject(typeof(T), result); + } } } diff --git a/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs b/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs index 72a55cee85..4a0c1d0f41 100644 --- a/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs +++ b/src/Umbraco.Tests/CoreThings/EnumExtensionsTests.cs @@ -51,5 +51,47 @@ namespace Umbraco.Tests.CoreThings else Assert.IsFalse(value.HasFlagAny(test)); } + + [TestCase(TreeUse.None, TreeUse.None, TreeUse.None)] + [TestCase(TreeUse.None, TreeUse.Main, TreeUse.Main)] + [TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.Dialog)] + [TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)] + [TestCase(TreeUse.Main, TreeUse.Main, TreeUse.Main)] + [TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)] + public void SetFlagTests(TreeUse value, TreeUse flag, TreeUse expected) + { + Assert.AreEqual(expected, value.SetFlag(flag)); + } + + [TestCase(TreeUse.None, TreeUse.None, TreeUse.None)] + [TestCase(TreeUse.None, TreeUse.Main, TreeUse.None)] + [TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.None)] + [TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] + [TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)] + [TestCase(TreeUse.Main, TreeUse.Main, TreeUse.None)] + [TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main)] + [TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] + [TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)] + [TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.None)] + [TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main)] + [TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)] + public void UnsetFlagTests(TreeUse value, TreeUse flag, TreeUse expected) + { + Assert.AreEqual(expected, value.UnsetFlag(flag)); + } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index 9b8936a43c..1f1ea139fc 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -224,11 +224,9 @@ namespace Umbraco.Web.Models.Mapping target.Mandatory = source.Validation.Mandatory; target.ValidationRegExp = source.Validation.Pattern; target.Variations = source.AllowCultureVariant - // Set culture flag - ? target.Variations | ContentVariation.Culture - // Unset culture flag - : target.Variations & ~ContentVariation.Culture; - + ? target.Variations.SetFlag(ContentVariation.Culture) + : target.Variations.UnsetFlag(ContentVariation.Culture); + if (source.Id > 0) target.Id = source.Id; @@ -399,11 +397,9 @@ namespace Umbraco.Web.Models.Mapping if (!(target is IMemberType)) { - target.Variations = source.AllowCultureVariant - // Set culture flag - ? target.Variations | ContentVariation.Culture - // Unset culture flag - : target.Variations & ~ContentVariation.Culture; + target.Variations = source.AllowCultureVariant + ? target.Variations.SetFlag(ContentVariation.Culture) + : target.Variations.UnsetFlag(ContentVariation.Culture); } // handle property groups and property types From 0e0f6e6678f1e3e9a7c093ed9df674a4a1670f37 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Mon, 21 Oct 2019 22:21:23 +0200 Subject: [PATCH 067/173] Ensure media path stored is the same as stored in the property --- .../Factories/ContentBaseFactory.cs | 36 +++++++------------ .../Repositories/Implement/MediaRepository.cs | 6 ++-- .../IDataEditorWithMediaPath.cs | 9 +++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../FileUploadPropertyEditor.cs | 5 ++- .../ImageCropperPropertyEditor.cs | 5 ++- 6 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs diff --git a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs index 434e0393cd..f9221ea576 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs @@ -1,17 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Persistence.Factories { internal class ContentBaseFactory { - private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled); - /// /// Builds an IContent item from a dto and content type. /// @@ -189,7 +187,7 @@ namespace Umbraco.Core.Persistence.Factories /// /// Builds a dto from an IMedia item. /// - public static MediaDto BuildDto(IMedia entity) + public static MediaDto BuildDto(PropertyEditorCollection propertyEditors, IMedia entity) { var contentDto = BuildContentDto(entity, Constants.ObjectTypes.Media); @@ -197,7 +195,7 @@ namespace Umbraco.Core.Persistence.Factories { NodeId = entity.Id, ContentDto = contentDto, - MediaVersionDto = BuildMediaVersionDto(entity, contentDto) + MediaVersionDto = BuildMediaVersionDto(propertyEditors, entity, contentDto) }; return dto; @@ -291,12 +289,19 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - private static MediaVersionDto BuildMediaVersionDto(IMedia entity, ContentDto contentDto) + private static MediaVersionDto BuildMediaVersionDto(PropertyEditorCollection propertyEditors, IMedia entity, ContentDto contentDto) { // try to get a path from the string being stored for media // TODO: only considering umbracoFile - TryMatch(entity.GetValue("umbracoFile"), out var path); + string path = null; + + if (entity.Properties.TryGetValue(Constants.Conventions.Media.File, out var property) + && propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) + { + path = dataEditor.GetMediaPath(property); + } var dto = new MediaVersionDto { @@ -308,22 +313,5 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - - // TODO: this should NOT be here?! - // more dark magic ;-( - internal static bool TryMatch(string text, out string path) - { - // In v8 we should allow exposing this via the property editor in a much nicer way so that the property editor - // can tell us directly what any URL is for a given property if it contains an asset - - path = null; - if (string.IsNullOrWhiteSpace(text)) return false; - - var m = MediaPathPattern.Match(text); - if (!m.Success || m.Groups.Count != 2) return false; - - path = m.Groups[1].Value; - return true; - } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index 25828b8126..8d7bda8943 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -228,7 +228,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.SanitizeEntityPropertiesForXmlStorage(); // create the dto - var dto = ContentBaseFactory.BuildDto(entity); + var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); // derive path and level from parent var parent = GetParentNodeDto(entity.ParentId); @@ -317,7 +317,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // create the dto - var dto = ContentBaseFactory.BuildDto(entity); + var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); // update the node dto var nodeDto = dto.ContentDto.NodeDto; @@ -542,6 +542,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement media.ResetDirtyProperties(false); return media; } - + } } diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs new file mode 100644 index 0000000000..af42ebf4d6 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + public interface IDataEditorWithMediaPath : IDataEditor + { + string GetMediaPath(Property property, string culture = null, string segment = null); + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ffe20afdb3..c40f438960 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -278,6 +278,7 @@ + diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index 702408788a..3efeb01e6e 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PropertyEditors "fileupload", Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] - public class FileUploadPropertyEditor : DataEditor + public class FileUploadPropertyEditor : DataEditor, IDataEditorWithMediaPath { private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSection; @@ -43,6 +43,9 @@ namespace Umbraco.Web.PropertyEditors return editor; } + public string GetMediaPath(Property property, string culture = null, string segment = null) => + property.GetValue(culture)?.ToString(); + /// /// Gets a value indicating whether a property is an upload field. /// diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index 9a8fb7c40b..1a4845c74e 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.PropertyEditors HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] - public class ImageCropperPropertyEditor : DataEditor + public class ImageCropperPropertyEditor : DataEditor, IDataEditorWithMediaPath { private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSettings; @@ -48,6 +48,9 @@ namespace Umbraco.Web.PropertyEditors _autoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, _contentSettings); } + public string GetMediaPath(Property property, string culture = null, string segment = null) => + GetFileSrcFromPropertyValue(property.GetValue(culture), out _); + /// /// Creates the corresponding property value editor. /// From 26f1e77813642f3c2badc9bf3ef2a1cd7e1a0383 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Wed, 30 Oct 2019 21:57:50 +0100 Subject: [PATCH 068/173] Resolve media paths from data editors # Conflicts: # src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs # src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs --- src/Umbraco.Core/ContentExtensions.cs | 15 ++++---- src/Umbraco.Core/Models/MediaExtensions.cs | 35 ++++-------------- .../Factories/ContentBaseFactory.cs | 3 +- .../IDataEditorWithMediaPath.cs | 6 +-- .../Routing/MediaUrlProviderTests.cs | 29 +++++++++++---- .../Services/MediaServiceTests.cs | 2 +- .../Entities/MockedContentTypes.cs | 37 +++++++++++++++++-- .../FileUploadPropertyEditor.cs | 5 +-- .../ImageCropperPropertyEditor.cs | 10 ++--- .../Routing/DefaultMediaUrlProvider.cs | 32 +++++++++------- 10 files changed, 102 insertions(+), 72 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index a7d40b0b7d..3edad0c963 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -13,6 +13,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; @@ -23,6 +24,8 @@ namespace Umbraco.Core // this ain't pretty private static IMediaFileSystem _mediaFileSystem; private static IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem); + private static readonly PropertyEditorCollection _propertyEditors; + private static PropertyEditorCollection PropertyEditors = _propertyEditors ?? (_propertyEditors = Current.PropertyEditors); #region IContent @@ -162,14 +165,12 @@ namespace Umbraco.Core // Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an // existing IMedia with extension SetValue causes exception 'Illegal characters in path' string oldpath = null; - if (property.GetValue(culture, segment) is string svalue) + var value = property.GetValue(culture, segment); + + if (PropertyEditors.TryGet(propertyTypeAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) { - if (svalue.DetectIsJson()) - { - // the property value is a JSON serialized image crop data set - grab the "src" property as the file source - var jObject = JsonConvert.DeserializeObject(svalue); - svalue = jObject != null ? jObject.GetValueAsString("src") : svalue; - } + var svalue = dataEditor.GetMediaPath(value); oldpath = MediaFileSystem.GetRelativePath(svalue); } diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs index 1166698adb..96f183b6e6 100644 --- a/src/Umbraco.Core/Models/MediaExtensions.cs +++ b/src/Umbraco.Core/Models/MediaExtensions.cs @@ -1,10 +1,8 @@ -using System; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Linq; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Models { @@ -18,29 +16,12 @@ namespace Umbraco.Core.Models if (!media.Properties.TryGetValue(propertyAlias, out var property)) return string.Empty; - // TODO: would need to be adjusted to variations, when media become variants - if (!(property.GetValue() is string jsonString)) - return string.Empty; - - if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField) - return jsonString; - - if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.ImageCropper) + if (Current.PropertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) { - if (jsonString.DetectIsJson() == false) - return jsonString; - - try - { - var json = JsonConvert.DeserializeObject(jsonString); - if (json["src"] != null) - return json["src"].Value(); - } - catch (Exception ex) - { - logger.Error(ex, "Could not parse the string '{JsonString}' to a json object", jsonString); - return string.Empty; - } + // TODO: would need to be adjusted to variations, when media become variants + var value = property.GetValue(); + return dataEditor.GetMediaPath(value); } // Without knowing what it is, just adding a string here might not be very nice diff --git a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs index f9221ea576..2b2bed1d9e 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs @@ -300,7 +300,8 @@ namespace Umbraco.Core.Persistence.Factories && propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) && editor is IDataEditorWithMediaPath dataEditor) { - path = dataEditor.GetMediaPath(property); + var value = property.GetValue(); + path = dataEditor.GetMediaPath(value); } var dto = new MediaVersionDto diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs index af42ebf4d6..aea7d28758 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs @@ -1,9 +1,7 @@ -using Umbraco.Core.Models; - -namespace Umbraco.Core.PropertyEditors +namespace Umbraco.Core.PropertyEditors { public interface IDataEditorWithMediaPath : IDataEditor { - string GetMediaPath(Property property, string culture = null, string segment = null); + string GetMediaPath(object value); } } diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 6489417dc7..2f960d498d 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -4,13 +4,18 @@ using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web.PropertyEditors; using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing @@ -25,7 +30,17 @@ namespace Umbraco.Tests.Routing { base.SetUp(); - _mediaUrlProvider = new DefaultMediaUrlProvider(); + var logger = Mock.Of(); + var mediaFileSystemMock = Mock.Of(); + var contentSection = Mock.Of(); + var dataTypeService = Mock.Of(); + + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(new IDataEditor[] + { + new FileUploadPropertyEditor(logger, mediaFileSystemMock, contentSection), + new ImageCropperPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService), + })); + _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors); } public override void TearDown() @@ -54,10 +69,10 @@ namespace Umbraco.Tests.Routing const string expected = "/media/rfeiw584/test.jpg"; var configuration = new ImageCropperConfiguration(); - var imageCropperValue = new ImageCropperValue + var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue { Src = expected - }; + }); var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); @@ -121,8 +136,8 @@ namespace Umbraco.Tests.Routing PropertyType = umbracoFilePropertyType, }; - property.SetValue("en", enMediaUrl, true); - property.SetValue("da", daMediaUrl); + property.SetSourceValue("en", enMediaUrl, true); + property.SetSourceValue("da", daMediaUrl); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; @@ -131,7 +146,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(daMediaUrl, resolvedUrl); } - private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) + private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, string propertyValue, object dataTypeConfiguration) { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); @@ -147,7 +162,7 @@ namespace Umbraco.Tests.Routing new SolidPublishedProperty { Alias = "umbracoFile", - SolidValue = propertyValue, + SolidSourceValue = propertyValue, SolidHasValue = true, PropertyType = umbracoFilePropertyType } diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index 17711fbd31..b3dc274c5e 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.Services public void Can_Get_Media_With_Crop_By_Path() { var mediaService = ServiceContext.MediaService; - var mediaType = MockedContentTypes.CreateImageMediaType("Image2"); + var mediaType = MockedContentTypes.CreateImageMediaTypeWithCrop("Image2"); ServiceContext.MediaTypeService.Save(mediaType); var media = MockedMedia.CreateMediaImageWithCrop(mediaType, -1); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index e93e8e8740..41786a5fd7 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -420,10 +420,39 @@ namespace Umbraco.Tests.TestHelpers.Entities var contentCollection = new PropertyTypeCollection(false); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.UploadField, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + + mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); + + //ensure that nothing is marked as dirty + mediaType.ResetDirtyProperties(false); + + return mediaType; + } + + public static MediaType CreateImageMediaTypeWithCrop(string alias = Constants.Conventions.MediaTypes.Image) + { + var mediaType = new MediaType(-1) + { + Alias = alias, + Name = "Image", + Description = "ContentType used for images", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(false); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.ImageCropper, ValueStorageType.Ntext) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = 1043 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index 3efeb01e6e..052af18aa1 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -43,8 +43,7 @@ namespace Umbraco.Web.PropertyEditors return editor; } - public string GetMediaPath(Property property, string culture = null, string segment = null) => - property.GetValue(culture)?.ToString(); + public string GetMediaPath(object value) => value?.ToString(); /// /// Gets a value indicating whether a property is an upload field. @@ -55,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors { return property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField; } - + /// /// Ensures any files associated are removed /// diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index 1a4845c74e..b0e5bf30bd 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -48,8 +48,7 @@ namespace Umbraco.Web.PropertyEditors _autoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, _contentSettings); } - public string GetMediaPath(Property property, string culture = null, string segment = null) => - GetFileSrcFromPropertyValue(property.GetValue(culture), out _); + public string GetMediaPath(object value) => GetFileSrcFromPropertyValue(value, out _, false); /// /// Creates the corresponding property value editor. @@ -66,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Gets a value indicating whether a property is an image cropper field. /// - /// The property. + /// The property. /// A value indicating whether a property is an image cropper field, and (optionally) has a non-empty value. private static bool IsCropperField(Property property) { @@ -135,8 +134,9 @@ namespace Umbraco.Web.PropertyEditors /// /// /// The deserialized value + /// Should the path returned be the application relative path /// - private string GetFileSrcFromPropertyValue(object propVal, out JObject deserializedValue) + private string GetFileSrcFromPropertyValue(object propVal, out JObject deserializedValue, bool relative = true) { deserializedValue = null; if (propVal == null || !(propVal is string str)) return null; @@ -144,7 +144,7 @@ namespace Umbraco.Web.PropertyEditors deserializedValue = GetJObject(str, true); if (deserializedValue?["src"] == null) return null; var src = deserializedValue["src"].Value(); - return _mediaFileSystem.GetRelativePath(src); + return relative ? _mediaFileSystem.GetRelativePath(src) : src; } /// diff --git a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs index 02dc4ebf29..dacd314f94 100644 --- a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs @@ -1,7 +1,7 @@ using System; -using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Routing { @@ -10,13 +10,24 @@ namespace Umbraco.Web.Routing /// public class DefaultMediaUrlProvider : IMediaUrlProvider { + private readonly PropertyEditorCollection _propertyEditors; + + public DefaultMediaUrlProvider(PropertyEditorCollection propertyEditors) + { + _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); + } + + [Obsolete("Use the constructor with all parameters instead")] + public DefaultMediaUrlProvider() : this(Current.PropertyEditors) + { + } + /// public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, - string propertyAlias, - UrlMode mode, string culture, Uri current) + string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); - var value = prop?.GetValue(culture); + var value = prop?.GetSourceValue(culture); if (value == null) { return null; @@ -25,15 +36,10 @@ namespace Umbraco.Web.Routing var propType = prop.PropertyType; string path = null; - switch (propType.EditorAlias) + if (_propertyEditors.TryGet(propType.EditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) { - case Constants.PropertyEditors.Aliases.UploadField: - path = value.ToString(); - break; - case Constants.PropertyEditors.Aliases.ImageCropper: - //get the url from the json format - path = value is ImageCropperValue stronglyTyped ? stronglyTyped.Src : value.ToString(); - break; + path = dataEditor.GetMediaPath(value); } var url = AssembleUrl(path, current, mode); From e934dbd054ee2017d1ef5fd6a6d705d73fe35724 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Fri, 1 Nov 2019 09:38:41 +0100 Subject: [PATCH 069/173] Updated validation message comparisons with default messages to use invariant comparison. --- src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs index 22f52e9293..0d77b35528 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentModelValidator.cs @@ -188,12 +188,12 @@ namespace Umbraco.Web.Editors.Filters foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp)) { // If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate(). - if (property.IsRequired && !string.IsNullOrWhiteSpace(property.IsRequiredMessage) && requiredDefaultMessages.Contains(r.ErrorMessage)) + if (property.IsRequired && !string.IsNullOrWhiteSpace(property.IsRequiredMessage) && requiredDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase)) { r.ErrorMessage = property.IsRequiredMessage; } - if (!string.IsNullOrWhiteSpace(property.ValidationRegExp) && !string.IsNullOrWhiteSpace(property.ValidationRegExpMessage) && formatDefaultMessages.Contains(r.ErrorMessage)) + if (!string.IsNullOrWhiteSpace(property.ValidationRegExp) && !string.IsNullOrWhiteSpace(property.ValidationRegExpMessage) && formatDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase)) { r.ErrorMessage = property.ValidationRegExpMessage; } From 501f9495a9c794894f0ade1a591f5fe5752cd78b Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 26 Sep 2019 10:05:41 +0200 Subject: [PATCH 070/173] Bumb version to 8.3 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 31a9953b23..bf3a271d32 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.2.2")] -[assembly: AssemblyInformationalVersion("8.2.2")] +[assembly: AssemblyFileVersion("8.3.0")] +[assembly: AssemblyInformationalVersion("8.3.0")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 10ca65357d..6c20e8c765 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -345,9 +345,9 @@ False True - 8220 + 8300 / - http://localhost:8220/ + http://localhost:8300/ False False From 6b318207c78e30c09f0ba93e824d3296b819aad0 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Thu, 26 Sep 2019 16:32:58 +0200 Subject: [PATCH 071/173] Disable rename alias of system user groups --- .../editor/umbeditorheader.directive.js | 3 ++- .../src/less/components/umb-locked-field.less | 3 ++- .../components/editor/umb-editor-header.html | 2 +- .../views/components/umb-generate-alias.html | 2 +- .../src/views/users/group.html | 1 + .../Filters/UserGroupValidateAttribute.cs | 13 +++++++++++++ .../Models/ContentEditing/UserGroupBasic.cs | 6 ++++++ .../Models/Mapping/UserMapDefinition.cs | 18 +++++++++++------- 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index 76c2e585f8..ebb780c36e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -195,6 +195,7 @@ Use this directive to construct a header inside the main editor window. @param {string=} icon Show and edit the content icon. Opens an overlay to change the icon. @param {boolean=} hideIcon Set to true to hide icon. @param {string=} alias show and edit the content alias. +@param {boolean=} aliasLocked Set to true to lock the alias. @param {boolean=} hideAlias Set to true to hide alias. @param {string=} description Add a description to the content. @param {boolean=} hideDescription Set to true to hide description. @@ -207,7 +208,6 @@ Use this directive to construct a header inside the main editor window. function EditorHeaderDirective(editorService) { function link(scope) { - scope.vm = {}; scope.vm.dropdownOpen = false; scope.vm.currentVariant = ""; @@ -262,6 +262,7 @@ Use this directive to construct a header inside the main editor window. icon: "=", hideIcon: "@", alias: "=", + aliasLocked: "<", hideAlias: "=", description: "=", hideDescription: "@", diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-locked-field.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-locked-field.less index 8d9ae86ce7..3dd169e892 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-locked-field.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-locked-field.less @@ -29,7 +29,8 @@ color: @gray-3; } -input.umb-locked-field__input { +input.umb-locked-field__input, +.umb-locked-field__text { background: rgba(255, 255, 255, 0); // if using transparent it will hide the text in safari border-color: transparent !important; font-size: 13px; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index aca27da7be..824caa87a9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -46,7 +46,7 @@ ng-if="!hideAlias" alias="$parent.alias" alias-from="$parent.name" - enable-lock="true" + enable-lock="aliasLocked !== true" validation-position="'right'" server-validation-field="Alias"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html index 9d68ea1b63..836e8fc3f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html @@ -1,5 +1,5 @@
- {{ alias }} + {{ alias }}
[DataMember(Name = "userCount")] public int UserCount { get; set; } + + /// + /// Is the user group a system group e.g. "Administrators", "Sensitive data" or "Translators" + /// + [DataMember(Name = "isSystemUserGroup")] + public bool IsSystemUserGroup { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs index 4acda1e552..abbb67c4aa 100644 --- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs @@ -138,7 +138,7 @@ namespace Umbraco.Web.Models.Mapping } // Umbraco.Code.MapAll -ContentStartNode -UserCount -MediaStartNode -Key -Sections - // Umbraco.Code.MapAll -Notifications -Udi -Trashed -AdditionalData + // Umbraco.Code.MapAll -Notifications -Udi -Trashed -AdditionalData -IsSystemUserGroup private void Map(IReadOnlyUserGroup source, UserGroupBasic target, MapperContext context) { target.Alias = source.Alias; @@ -147,11 +147,11 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Name; target.ParentId = -1; target.Path = "-1," + source.Id; - MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); } // Umbraco.Code.MapAll -ContentStartNode -MediaStartNode -Sections -Notifications - // Umbraco.Code.MapAll -Udi -Trashed -AdditionalData + // Umbraco.Code.MapAll -Udi -Trashed -AdditionalData -IsSystemUserGroup private void Map(IUserGroup source, UserGroupBasic target, MapperContext context) { target.Alias = source.Alias; @@ -162,7 +162,7 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = -1; target.Path = "-1," + source.Id; target.UserCount = source.UserCount; - MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); } // Umbraco.Code.MapAll -Udi -Trashed -AdditionalData -AssignedPermissions @@ -198,7 +198,7 @@ namespace Umbraco.Web.Models.Mapping } // Umbraco.Code.MapAll -ContentStartNode -MediaStartNode -Sections -Notifications -Udi - // Umbraco.Code.MapAll -Trashed -AdditionalData -Users -AssignedPermissions + // Umbraco.Code.MapAll -Trashed -AdditionalData -Users -AssignedPermissions -IsSystemUserGroup private void Map(IUserGroup source, UserGroupDisplay target, MapperContext context) { target.Alias = source.Alias; @@ -211,7 +211,7 @@ namespace Umbraco.Web.Models.Mapping target.Path = "-1," + source.Id; target.UserCount = source.UserCount; - MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); //Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that // this will cause an N+1 and we'll need to change how this works. @@ -334,8 +334,12 @@ namespace Umbraco.Web.Models.Mapping // helpers - private void MapUserGroupBasic(UserGroupBasic target, IEnumerable sourceAllowedSections, int? sourceStartContentId, int? sourceStartMediaId, MapperContext context) + private void MapUserGroupBasic(UserGroupBasic target, string alias, IEnumerable sourceAllowedSections, int? sourceStartContentId, int? sourceStartMediaId, MapperContext context) { + target.IsSystemUserGroup = alias == Constants.Security.AdminGroupAlias + || alias == Constants.Security.TranslatorGroupAlias + || alias == Constants.Security.SensitiveDataGroupAlias; + var allSections = _sectionService.GetSections(); target.Sections = context.MapEnumerable(allSections.Where(x => sourceAllowedSections.Contains(x.Alias))); From 640f042acfe3c0dce198b88fa8e362ccb5ecc5e7 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Thu, 26 Sep 2019 18:39:54 +0200 Subject: [PATCH 072/173] Disallow deleting "Sensitive data" user group --- .../Models/Membership/UserGroupExtensions.cs | 13 +++++++++++++ .../users/views/groups/groups.controller.js | 2 +- .../src/views/users/views/groups/groups.html | 2 +- .../Filters/UserGroupValidateAttribute.cs | 13 ++++--------- .../Editors/UserGroupsController.cs | 4 ++-- .../Models/Mapping/UserMapDefinition.cs | 19 ++++++++++--------- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs b/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs index 3903fe405b..b1d0189c56 100644 --- a/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs @@ -15,6 +15,12 @@ namespace Umbraco.Core.Models.Membership return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon, group.StartContentId, group.StartMediaId, group.Alias, group.AllowedSections, group.Permissions); } + public static bool IsSystemUserGroup(this IUserGroup group) => + IsSystemUserGroup(group.Alias); + + public static bool IsSystemUserGroup(this IReadOnlyUserGroup group) => + IsSystemUserGroup(group.Alias); + public static IReadOnlyUserGroup ToReadOnlyGroup(this UserGroupDto group) { return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon, @@ -22,5 +28,12 @@ namespace Umbraco.Core.Models.Membership group.UserGroup2AppDtos.Select(x => x.AppAlias).ToArray(), group.DefaultPermissions == null ? Enumerable.Empty() : group.DefaultPermissions.ToCharArray().Select(x => x.ToString())); } + + private static bool IsSystemUserGroup(this string groupAlias) + { + return groupAlias == Constants.Security.AdminGroupAlias + || groupAlias == Constants.Security.SensitiveDataGroupAlias + || groupAlias == Constants.Security.TranslatorGroupAlias; + } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js index 1e51d4585e..b21859f5c4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.controller.js @@ -81,7 +81,7 @@ } // Disallow selection of the admin/translators group, the checkbox is not visible in the UI, but clicking(and thus selecting) is still possible. // Currently selection can only be used for deleting, and the Controller will also disallow deleting the admin group. - if (userGroup.alias === "admin" || userGroup.alias === "translator") + if (userGroup.isSystemUserGroup) return; listViewHelper.selectHandler(userGroup, $index, vm.userGroups, vm.selection, $event); diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html index 5d1496d90f..4d252a3ae0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/groups/groups.html @@ -86,7 +86,7 @@
+ ng-class="{'-selected': group.selected, '-selectable': group.hasAccess && !group.isSystemUserGroup}">
diff --git a/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs b/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs index 7d465a9ddc..78cd8e6a4d 100644 --- a/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/UserGroupValidateAttribute.cs @@ -51,16 +51,11 @@ namespace Umbraco.Web.Editors.Filters return; } - if (persisted.Alias != userGroupSave.Alias) + if (persisted.Alias != userGroupSave.Alias && persisted.IsSystemUserGroup()) { - if (persisted.Alias == Constants.Security.AdminGroupAlias - || persisted.Alias == Constants.Security.SensitiveDataGroupAlias - || persisted.Alias == Constants.Security.TranslatorGroupAlias) - { - var message = $"User group with alias: {persisted.Alias} cannot be changed"; - actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, message); - return; - } + var message = $"User group with alias: {persisted.Alias} cannot be changed"; + actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, message); + return; } //map the model to the persisted instance diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs index 1b64722735..b081ca6137 100644 --- a/src/Umbraco.Web/Editors/UserGroupsController.cs +++ b/src/Umbraco.Web/Editors/UserGroupsController.cs @@ -154,8 +154,8 @@ namespace Umbraco.Web.Editors public HttpResponseMessage PostDeleteUserGroups([FromUri] int[] userGroupIds) { var userGroups = Services.UserService.GetAllUserGroups(userGroupIds) - //never delete the admin group or translators group - .Where(x => x.Alias != Constants.Security.AdminGroupAlias && x.Alias != Constants.Security.TranslatorGroupAlias) + //never delete the admin group, sensitive data or translators group + .Where(x => !x.IsSystemUserGroup()) .ToArray(); foreach (var userGroup in userGroups) { diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs index abbb67c4aa..88960fb189 100644 --- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs @@ -147,7 +147,9 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Name; target.ParentId = -1; target.Path = "-1," + source.Id; - MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + target.IsSystemUserGroup = source.IsSystemUserGroup(); + + MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); } // Umbraco.Code.MapAll -ContentStartNode -MediaStartNode -Sections -Notifications @@ -162,7 +164,9 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = -1; target.Path = "-1," + source.Id; target.UserCount = source.UserCount; - MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + target.IsSystemUserGroup = source.IsSystemUserGroup(); + + MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); } // Umbraco.Code.MapAll -Udi -Trashed -AdditionalData -AssignedPermissions @@ -198,7 +202,7 @@ namespace Umbraco.Web.Models.Mapping } // Umbraco.Code.MapAll -ContentStartNode -MediaStartNode -Sections -Notifications -Udi - // Umbraco.Code.MapAll -Trashed -AdditionalData -Users -AssignedPermissions -IsSystemUserGroup + // Umbraco.Code.MapAll -Trashed -AdditionalData -Users -AssignedPermissions private void Map(IUserGroup source, UserGroupDisplay target, MapperContext context) { target.Alias = source.Alias; @@ -210,8 +214,9 @@ namespace Umbraco.Web.Models.Mapping target.ParentId = -1; target.Path = "-1," + source.Id; target.UserCount = source.UserCount; + target.IsSystemUserGroup = source.IsSystemUserGroup(); - MapUserGroupBasic(target, source.Alias, source.AllowedSections, source.StartContentId, source.StartMediaId, context); + MapUserGroupBasic(target, source.AllowedSections, source.StartContentId, source.StartMediaId, context); //Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that // this will cause an N+1 and we'll need to change how this works. @@ -334,12 +339,8 @@ namespace Umbraco.Web.Models.Mapping // helpers - private void MapUserGroupBasic(UserGroupBasic target, string alias, IEnumerable sourceAllowedSections, int? sourceStartContentId, int? sourceStartMediaId, MapperContext context) + private void MapUserGroupBasic(UserGroupBasic target, IEnumerable sourceAllowedSections, int? sourceStartContentId, int? sourceStartMediaId, MapperContext context) { - target.IsSystemUserGroup = alias == Constants.Security.AdminGroupAlias - || alias == Constants.Security.TranslatorGroupAlias - || alias == Constants.Security.SensitiveDataGroupAlias; - var allSections = _sectionService.GetSections(); target.Sections = context.MapEnumerable(allSections.Where(x => sourceAllowedSections.Contains(x.Alias))); From 73fc648718288191e66e4dd6da1984b9ea8c8212 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2019 20:45:16 +1100 Subject: [PATCH 073/173] 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 8597bbf19f..4c452ad554 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 a7eb693053d9793309630a0430c017f5420030fa Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 15:21:00 +1100 Subject: [PATCH 074/173] 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 21b97d980d..5acd94d12c 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs @@ -97,13 +97,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); } @@ -111,30 +105,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, 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 37fce725ff649bbd0fb981b999f239006dfc2f84 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 10:04:51 +0200 Subject: [PATCH 075/173] 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 | 4317 +++++++++-------- .../Cache/DistributedCacheExtensions.cs | 12 +- src/Umbraco.Web/Editors/LanguageController.cs | 35 +- 6 files changed, 2280 insertions(+), 2211 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 7f15cab2c2..fefab08c4b 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 08c8829a73..60e711f8e3 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1,2157 +1,2168 @@ - - - - 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 - Send for approval - Save list view - Schedule - Preview - Preview is disabled because there's no template assigned - Choose style - Show styles - Insert table - Generate models and close - Save and generate models - Undo - Redo - Rollback - Delete tag - Cancel - Confirm - More publishing options - - - Viewing for - Content deleted - Content unpublished - Content unpublished for languages: %0% - Content published - Content 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 - 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 child items have 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 - No content can be added for this item - 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 - Draft - Not created - 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 - Parent and destination folders cannot be the same - 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 - 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 - - - Options - 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 - 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 color - Bold - Text color - 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 - Upgrading from 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 - - - Insufficient user permissions to publish all descendant documents - - - - - - - - Validation failed for required language '%0%'. This language was saved but not published. - 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 colors - - - 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 - - - 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 - - - Content - Forms - Media - Members - Packages - Settings - 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 - - This node has no child nodes to sort - - - 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 - 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 - Content published - and is visible on the website - %0% documents published and visible on the website - %0% published and visible on the website - %0% documents published for languages %1% and visible on the website - Content saved - Remember to publish to make changes visible - A schedule for publishing has been updated - %0% saved - Sent For Approval - Changes have been sent for approval - %0% changes have been sent for approval - Media saved - Media saved without any errors - Member saved - Member group 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 - Content variation %0% unpublished - The mandatory language '%0%' was unpublished. All languages for this content item are now 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% - Cannot publish the document since the required '%0%' is not published - Validation failed for language '%0%' - Document type was exported to file - An error occurred while exporting the document type - The release date cannot be in the past - Cannot schedule the document for publishing since the required '%0%' is not published - Cannot schedule the document for publishing since the required '%0%' has a publish date later than a non mandatory language - The expire date cannot be in the past - The expire date cannot be before the release date - - - 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 - Rich Text 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 - 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 - Value cannot be null - Value cannot be empty - Value is invalid, it does not match the correct pattern - 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 - Culture - 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 + + + + 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 + Send for approval + Save list view + Schedule + Preview + Preview is disabled because there's no template assigned + Choose style + Show styles + Insert table + Generate models and close + Save and generate models + Undo + Redo + Rollback + Delete tag + Cancel + Confirm + More publishing options + + + Viewing for + Content deleted + Content unpublished + Content unpublished for languages: %0% + Content published + Content 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 + 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 child items have 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 + No content can be added for this item + 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 + Draft + Not created + 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 + Parent and destination folders cannot be the same + 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 + 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 + 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: + 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 + + + Options + 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 + 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 color + Bold + Text color + 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 + Upgrading from 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 + + + Insufficient user permissions to publish all descendant documents + + + + + + + + Validation failed for required language '%0%'. This language was saved but not published. + 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 colors + + + 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 + + + Content + Forms + Media + Members + Packages + Settings + 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 + + This node has no child nodes to sort + + + 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 + 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 + Content published + and is visible on the website + %0% documents published and visible on the website + %0% published and visible on the website + %0% documents published for languages %1% and visible on the website + Content saved + Remember to publish to make changes visible + A schedule for publishing has been updated + %0% saved + Sent For Approval + Changes have been sent for approval + %0% changes have been sent for approval + Media saved + Media saved without any errors + Member saved + Member group 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 + Content variation %0% unpublished + The mandatory language '%0%' was unpublished. All languages for this content item are now 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% + Cannot publish the document since the required '%0%' is not published + Validation failed for language '%0%' + Document type was exported to file + An error occurred while exporting the document type + The release date cannot be in the past + Cannot schedule the document for publishing since the required '%0%' is not published + Cannot schedule the document for publishing since the required '%0%' has a publish date later than a non mandatory language + The expire date cannot be in the past + The expire date cannot be before the release date + + + 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 + Rich Text 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 + 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 + Value cannot be null + Value cannot be empty + Value is invalid, it does not match the correct pattern + 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 + Culture + 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 - - + Getting Started + Install Umbraco Forms + + Go back Active layout: Jump to - group - -
+ group + passed + warning + failed + suggestion + Check passed + Check failed + Open backoffice search + Open/Close backoffice help + Open/Close your profile options + +
diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 80c49e279c..579448e264 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 22de853dee25c811092925cc88a85837e4c69020 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 22:02:42 +1100 Subject: [PATCH 076/173] 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 bf6012816a..23c2131a82 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -260,6 +260,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentTypeService.ScopedRefreshedEntity += OnContentTypeRefreshedEntity; MediaTypeService.ScopedRefreshedEntity += OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity += OnMemberTypeRefreshedEntity; + + LocalizationService.SavedLanguage += OnLanguageSaved; } private void TearDownRepositoryEvents() @@ -277,6 +279,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentTypeService.ScopedRefreshedEntity -= OnContentTypeRefreshedEntity; MediaTypeService.ScopedRefreshedEntity -= OnMediaTypeRefreshedEntity; MemberTypeService.ScopedRefreshedEntity -= OnMemberTypeRefreshedEntity; + + LocalizationService.SavedLanguage -= OnLanguageSaved; } public override void Dispose() @@ -1309,6 +1313,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 58f50be33738b48fdc22afdcbbf0af0928d34cfb Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Oct 2019 22:09:47 +1100 Subject: [PATCH 077/173] 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 5125772de543413c9d81458997dc41151bc6c1bb Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 15:00:13 +1100 Subject: [PATCH 078/173] 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 23c2131a82..948019e8cc 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1320,7 +1320,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 57288236250352f7e05bc14917db5999a7bcb651 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 15:49:19 +1100 Subject: [PATCH 079/173] 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 c116c12f59..5acba4b609 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 e77eb746558165bc56a52b8a1b55f8370044e65c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 4 Nov 2019 11:54:19 +0100 Subject: [PATCH 080/173] post cherry pick fix --- src/Umbraco.Web/Cache/LanguageCacheRefresher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index 8463acd6e0..e7d77207e3 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.Cache ClearAllIsolatedCacheByEntityType(); foreach (var payload in payloads) - { + { switch (payload.ChangeType) { case LanguageChangeType.Update: @@ -72,8 +72,8 @@ namespace Umbraco.Web.Cache 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) }; - ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload); + var clearContentPayload = new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }; + ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload); } // then trigger event From 1759813795cab7e085d5275e8d9a97bb2eab10be Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 5 Nov 2019 10:20:11 +0100 Subject: [PATCH 081/173] Examine management dashboard: Add missing translations (#6866) --- .../dashboard/settings/examinemanagement.html | 15 +- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 4 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4444 +++++++++-------- .../Umbraco/config/lang/en_us.xml | 4 + 4 files changed, 2242 insertions(+), 2225 deletions(-) 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 f819a0a142..632127e38c 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 @@ -100,8 +100,12 @@
-
Search
-
Search the index and view the results
+
+ Search +
+
+ Search the index and view the results +
@@ -225,7 +229,7 @@
{{vm.selectedIndex.healthStatus}}
- The index cannot be read and will need to be rebuilt + The index cannot be read and will need to be rebuilt
@@ -377,13 +381,14 @@
- The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
- This index cannot be rebuilt because it has no assigned IIndexPopulator + This index cannot be rebuilt because it has no assigned + IIndexPopulator
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index cc4d996dad..b95e6ed23f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -514,6 +514,10 @@ Værktøjer Værktøjer til at administrere indekset felter + Indexet skal bygges igen, for at kunne læses + Processen tager længere tid end forventet. Kontrollér Umbraco loggen for at se om der er sket fejl under operationen + Dette index kan ikke genbygess for det ikke har nogen + IIndexPopulator 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 19c8642dc5..2c863f2dc1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1,2220 +1,2224 @@ - - - - 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 element type - Select element type - 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.]]> - Document Types within the Settings section.]]> - 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 - 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 - Edit selected media - Insert local link - Insert character - Insert graphic headline - Insert picture - Insert link - 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: - 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, you should unpublish the node in that language 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 - fields - - - 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 - Create - 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 - Focal point - 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 - copy to clipboard - 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. - Removing a composition will delete all the associated property data. Once you save the document type there's no way back. - 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 - %1% more.]]> - %1% too many.]]> - - - - 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 - Open context menu for - Current language - Switch language to - Create new folder - Partial View - Partial View Macro - Member - - - 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 - - - Log Levels - Saved Searches - Total Items - Timestamp - Level - Machine - Message - Exception - Properties - Search With Google - Search this message with Google - Search With Bing - Search this message with Bing - Search Our Umbraco - Search this message on Our Umbraco forums and docs - Search Our Umbraco with Google - Search Our Umbraco forums using Google - Search Umbraco Source - Search within Umbraco source code on Github - Search Umbraco Issues - Search Umbraco Issues on Github - Delete this search - Find Logs with Request ID - Find Logs with Namespace - Find Logs with Machine Name - Open - - - Copy %0% - %0% from %1% - - - Open Property Actions - -
+ + + + 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 element type + Select element type + 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.]]> + Document Types within the Settings section.]]> + 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 + 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 + Edit selected media + Insert local link + Insert character + Insert graphic headline + Insert picture + Insert link + 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: + 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, you should unpublish the node in that language 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 + fields + The index cannot be read and will need to be rebuilt + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + This index cannot be rebuilt because it has no assigned + IIndexPopulator + + + 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 + Create + 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 + Focal point + 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 + copy to clipboard + 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. + Removing a composition will delete all the associated property data. Once you save the document type there's no way back. + 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 + %1% more.]]> + %1% too many.]]> + + + + 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 + Open context menu for + Current language + Switch language to + Create new folder + Partial View + Partial View Macro + Member + + + 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 + + + Log Levels + Saved Searches + Total Items + Timestamp + Level + Machine + Message + Exception + Properties + Search With Google + Search this message with Google + Search With Bing + Search this message with Bing + Search Our Umbraco + Search this message on Our Umbraco forums and docs + Search Our Umbraco with Google + Search Our Umbraco forums using Google + Search Umbraco Source + Search within Umbraco source code on Github + Search Umbraco Issues + Search Umbraco Issues on Github + Delete this search + Find Logs with Request ID + Find Logs with Namespace + Find Logs with Machine Name + Open + + + Copy %0% + %0% from %1% + + + Open Property Actions + +
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 0c572fba26..fbdf6b6faa 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -523,6 +523,10 @@ Tools Tools to manage the index fields + The index cannot be read and will need to be rebuilt + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + This index cannot be rebuilt because it has no assigned + IIndexPopulator Enter your username From fd649ea0e2c1fec7479f11d794b8245c28348857 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 5 Nov 2019 10:27:27 +0100 Subject: [PATCH 082/173] Languages: Add missing "none" translation (#6865) --- src/Umbraco.Web.UI.Client/src/views/languages/overview.html | 4 +++- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html index 4cf0ac7bf2..7ed6735292 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html @@ -58,7 +58,9 @@ {{vm.getLanguageById(language.fallbackLanguageId).name}} - (none) + + (none) + Intet fallback-sprog For at tillade flersproget indhold, som ikke er tilgængeligt i det anmodede sprog, skal du her vælge et sprog at falde tilbage på. Fallback-sprog + ingen Tilføj parameter diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 2c863f2dc1..f6f9e569ff 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1639,6 +1639,7 @@ To manage your website, simply open the Umbraco back office and start adding con 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 + none 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 fbdf6b6faa..71ab48dfa3 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1653,6 +1653,7 @@ To manage your website, simply open the Umbraco back office and start adding con 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 + none Add parameter From 8dd92bc72089564c8af445ef55d78212020bd91b Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 5 Nov 2019 11:46:55 +0100 Subject: [PATCH 083/173] NuCache dashboard: Add translation keys (#6867) --- .../src/views/dashboard/settings/nucache.html | 68 +++++++++++++------ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 35 ++++++++++ .../Umbraco/config/lang/en_us.xml | 35 ++++++++++ 3 files changed, 117 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html index 0b91959402..0b14e9c3ff 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/nucache.html @@ -4,12 +4,14 @@

- (wait) + (wait)

-
Published Cache Status
+
+ Published Cache Status +
@@ -20,7 +22,9 @@
- +
@@ -31,53 +35,75 @@
-
Caches
+
+ Caches +
-
Memory Cache
+
+ Memory Cache +
- This button lets you reload the in-memory cache, by entirely reloading it from the database - cache (but it does not rebuild that database cache). This is relatively fast. - Use it when you think that the memory cache has not been properly refreshed, after some events - triggered—which would indicate a minor Umbraco issue. - (note: triggers the reload on all servers in an LB environment). + + This button lets you reload the in-memory cache, by entirely reloading it from the database + cache (but it does not rebuild that database cache). This is relatively fast. + Use it when you think that the memory cache has not been properly refreshed, after some events + triggered—which would indicate a minor Umbraco issue. + (note: triggers the reload on all servers in an LB environment). + +
- +
-
Database Cache
+
+ Database Cache +
- This button lets you rebuild the database cache, ie the content of the cmsContentNu table. - Rebuilding can be expensive. - Use it when reloading is not enough, and you think that the database cache has not been - properly generated—which would indicate some critical Umbraco issue. + + This button lets you rebuild the database cache, ie the content of the cmsContentNu table. + Rebuilding can be expensive. + Use it when reloading is not enough, and you think that the database cache has not been + properly generated—which would indicate some critical Umbraco issue. +
- +
-
Internals
+
+ Internals +
- This button lets you trigger a NuCache snapshots collection (after running a fullCLR GC). - Unless you know what that means, you probably do not need to use it. + + This button lets you trigger a NuCache snapshots collection (after running a fullCLR GC). + Unless you know what that means, you probably do not need to use it. + +
- +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index f6f9e569ff..dd4ed9d748 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2222,4 +2222,39 @@ To manage your website, simply open the Umbraco back office and start adding con Open Property Actions + + Wait + Refresh status + Memory Cache + + + + Reload + Database Cache + + Rebuilding can be expensive. + Use it when reloading is not enough, and you think that the database cache has not been + properly generated—which would indicate some critical Umbraco issue. + ]]> + + Rebuild + Internals + + not need to use it. + ]]> + + Collect + Published Cache Status + Caches + 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 71ab48dfa3..437933026b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2238,4 +2238,39 @@ To manage your website, simply open the Umbraco back office and start adding con Open Property Actions + + Wait + Refresh status + Memory Cache + + + + Reload + Database Cache + + Rebuilding can be expensive. + Use it when reloading is not enough, and you think that the database cache has not been + properly generated—which would indicate some critical Umbraco issue. + ]]> + + Rebuild + Internals + + not need to use it. + ]]> + + Collect + Published Cache Status + Caches + From 32110540633bd5485ecdec98e7b68ec1ae2affa2 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Sat, 26 Oct 2019 13:02:19 +0100 Subject: [PATCH 084/173] look for the system.web section under the root configuration node use Element to get the root system.web. DescendantsAndSelf can return multiple --- src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs index 4d64d30e9d..c273d642ed 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs @@ -39,7 +39,8 @@ namespace Umbraco.Web.Install.InstallSteps var fileName = IOHelper.MapPath($"{SystemDirectories.Root}/web.config"); var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); - var systemWeb = xml.Root.DescendantsAndSelf("system.web").Single(); + // we only want to get the element that is under the root, (there may be more under tags we don't want them) + var systemWeb = xml.Root.Element("system.web"); // Update appSetting if it exists, or else create a new appSetting for the given key and value var machineKey = systemWeb.Descendants("machineKey").FirstOrDefault(); From 0b16c48fc36abd10cf429108939abc046c5149b7 Mon Sep 17 00:00:00 2001 From: Matt Brailsford Date: Tue, 5 Nov 2019 11:59:04 +0100 Subject: [PATCH 085/173] =?UTF-8?q?Split=20PagedResult=20into=20a=20typed?= =?UTF-8?q?=20and=20non-typed=20version=20(Fixes=20#46=E2=80=A6=20(#6871)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Umbraco.Core/Models/PagedResult.cs | 6 +----- src/Umbraco.Core/Models/PagedResultOfT.cs | 20 ++++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 3 ++- 3 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Core/Models/PagedResultOfT.cs diff --git a/src/Umbraco.Core/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs index ef4d4efdfd..4119751eb3 100644 --- a/src/Umbraco.Core/Models/PagedResult.cs +++ b/src/Umbraco.Core/Models/PagedResult.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Runtime.Serialization; namespace Umbraco.Core.Models @@ -9,7 +8,7 @@ namespace Umbraco.Core.Models /// /// [DataContract(Name = "pagedCollection", Namespace = "")] - public class PagedResult + public abstract class PagedResult { public PagedResult(long totalItems, long pageNumber, long pageSize) { @@ -39,9 +38,6 @@ namespace Umbraco.Core.Models [DataMember(Name = "totalItems")] public long TotalItems { get; private set; } - [DataMember(Name = "items")] - public IEnumerable Items { get; set; } - /// /// Calculates the skip size based on the paged parameters specified /// diff --git a/src/Umbraco.Core/Models/PagedResultOfT.cs b/src/Umbraco.Core/Models/PagedResultOfT.cs new file mode 100644 index 0000000000..efb68863dd --- /dev/null +++ b/src/Umbraco.Core/Models/PagedResultOfT.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a paged result for a model collection + /// + /// + [DataContract(Name = "pagedCollection", Namespace = "")] + public class PagedResult : PagedResult + { + public PagedResult(long totalItems, long pageNumber, long pageSize) + : base(totalItems, pageNumber, pageSize) + { } + + [DataMember(Name = "items")] + public IEnumerable Items { get; set; } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ffe20afdb3..d6b63880d6 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -273,6 +273,7 @@ + @@ -847,7 +848,7 @@ - + From c8ec7ce8ba1b6a504575c5bab3e0dd5e1438ff9c Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 5 Nov 2019 12:10:41 +0100 Subject: [PATCH 086/173] Profiling dashboard: Add translations (#6879) --- .../views/dashboard/settings/profiler.html | 58 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 37 ++++++++++++ .../Umbraco/config/lang/en_us.xml | 37 ++++++++++++ 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html index e55dbe2298..6bff0bba9b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html @@ -6,39 +6,51 @@ -

Performance profiling

+

+ Performance profiling +

-

- Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. -

-

- If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. -

-

- If you want the profiler to be activated by default for all page renderings, you can use the toggle below. - It will set a cookie in your browser, which then activates the profiler automatically. - In other words, the profiler will only be active by default in your browser - not everyone else's. -

+ +

+ Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +

+

+ If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +

+

+ If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +

+
- +
-

Friendly reminder

-

- You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. -

+

+ Friendly reminder +

+ +

+ You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +

+
-

- Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. -

-

- Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. -

+ +

+ Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +

+

+ Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +

+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index dd4ed9d748..85aea480fb 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2257,4 +2257,41 @@ To manage your website, simply open the Umbraco back office and start adding con Published Cache Status Caches + + Performance profiling + + + Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +

+

+ If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +

+

+ If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +

+ ]]> +
+ Activate the profiler by default + Friendly reminder + + + You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +

+ ]]> +
+ + + Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +

+

+ Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +

+ ]]> +
+ 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 437933026b..a184857045 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2273,4 +2273,41 @@ To manage your website, simply open the Umbraco back office and start adding con Published Cache Status Caches + + Performance profiling + + + Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +

+

+ If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +

+

+ If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +

+ ]]> +
+ Activate the profiler by default + Friendly reminder + + + You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +

+ ]]> +
+ + + Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +

+

+ Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +

+ ]]> +
+ From 68de8a1fa68d06de20dfea406e20ca94b838ab78 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Tue, 5 Nov 2019 12:20:00 +0100 Subject: [PATCH 087/173] Video dashboard (Settings & Members): Add translations (#6881) --- .../dashboard/members/membersdashboardvideos.html | 8 +++++--- .../dashboard/settings/settingsdashboardvideos.html | 12 +++++++++--- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 9 +++++++++ src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 9 +++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html index 83f3fb2f7a..a7f7d45087 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html @@ -4,13 +4,15 @@ -

Hours of Umbraco training videos are only a click away

-

Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

+

Hours of Umbraco training videos are only a click away

+ +

Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

+
-

To get you started:

+

To get you started:

  • diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html index 64a9c819ab..96d015f758 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html @@ -1,12 +1,18 @@ -

    Hours of Umbraco training videos are only a click away

    -

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    +

    + Hours of Umbraco training videos are only a click away +

    + +

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    +
    -

    To get you started:

    +

    + To get you started: +

    • diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 85aea480fb..a8c1be1829 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2294,4 +2294,13 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> + + Hours of Umbraco training videos are only a click away + + Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

      + ]]> +
      + To get you started + 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 a184857045..cca99b1244 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2310,4 +2310,13 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> + + Hours of Umbraco training videos are only a click away + + Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

      + ]]> +
      + To get you started + From 1c70d04ce29fd750055227cabac2bccd44cc1d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Kottal?= Date: Tue, 5 Nov 2019 15:33:07 +0100 Subject: [PATCH 088/173] Moves editor name part out of template. Adds null checking in templates --- .../src/views/propertyeditors/grid/grid.controller.js | 11 +++++++++-- src/Umbraco.Web.UI/config/grid.editors.config.js | 7 ++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 32f495e32b..5dce84d5dc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -681,8 +681,15 @@ angular.module("umbraco") }; $scope.getTemplateName = function (control) { - if (control.editor.nameExp) return control.editor.nameExp(control) - return control.editor.name; + var templateName = control.editor.name; + if (control.editor.nameExp) { + var valueOfTemplate = control.editor.nameExp(control); + if (valueOfTemplate != "") { + templateName += ": "; + templateName += valueOfTemplate; + } + } + return templateName; } // ********************************************* diff --git a/src/Umbraco.Web.UI/config/grid.editors.config.js b/src/Umbraco.Web.UI/config/grid.editors.config.js index 49b843689b..210d167f9e 100644 --- a/src/Umbraco.Web.UI/config/grid.editors.config.js +++ b/src/Umbraco.Web.UI/config/grid.editors.config.js @@ -7,14 +7,14 @@ }, { "name": "Image", - "nameTemplate": "{{ 'Image: ' + (value.udi | ncNodeName) }}", + "nameTemplate": "{{ value && value.udi ? (value.udi | ncNodeName) : '' }}", "alias": "media", "view": "media", "icon": "icon-picture" }, { "name": "Macro", - "nameTemplate": "{{ 'Macro: ' + value.macroAlias }}", + "nameTemplate": "{{ value && value.macroAlias ? value.macroAlias : '' }}", "alias": "macro", "view": "macro", "icon": "icon-settings-alt" @@ -27,7 +27,7 @@ }, { "name": "Headline", - "nameTemplate": "{{ 'Headline: ' + value }}", + "nameTemplate": "{{ value }}", "alias": "headline", "view": "textstring", "icon": "icon-coin", @@ -38,6 +38,7 @@ }, { "name": "Quote", + "nameTemplate": "{{ value ? value.substring(0,32) + (value.length > 32 ? '...' : '') : '' }}", "alias": "quote", "view": "textstring", "icon": "icon-quote", From a78aab628fd12545ea6ec17241287c3f63e7162e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 30 Oct 2019 17:52:37 +0100 Subject: [PATCH 089/173] Don't show rollback for deleted content --- .../src/views/components/content/umb-content-node-info.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 47a6af17be..da093f72fe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -46,6 +46,7 @@ title="{{historyLabel}}"> Date: Wed, 30 Oct 2019 18:06:48 +0100 Subject: [PATCH 090/173] Make sure we can't rollback trashed items serverside --- src/Umbraco.Core/Services/Implement/ContentService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 720713e9ad..738b487cfd 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -3083,7 +3083,7 @@ namespace Umbraco.Core.Services.Implement var version = GetVersion(versionId); //Good ole null checks - if (content == null || version == null) + if (content == null || version == null || content.Trashed) { return new OperationResult(OperationResultType.FailedCannot, evtMsgs); } From d41e5bb27f4728b2abe8780e5abd54eff1ca4ad6 Mon Sep 17 00:00:00 2001 From: KB Date: Wed, 30 Oct 2019 12:18:23 +0000 Subject: [PATCH 091/173] Update JQuery CDN In partial snippets, to use newer version without vulnerability --- .../Umbraco/PartialViewMacros/Templates/EditProfile.cshtml | 2 +- .../Umbraco/PartialViewMacros/Templates/Login.cshtml | 2 +- .../Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index 79c83a883d..7034404ca3 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -9,7 +9,7 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml index af84a603db..a7effa5606 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml @@ -11,7 +11,7 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index b9fbea4733..ca6f09bd97 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -33,7 +33,7 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); From 2bbe2c01deaaf2bd38d566cd02f50d0d552e77b0 Mon Sep 17 00:00:00 2001 From: Jeffrey Schoemaker Date: Thu, 7 Nov 2019 11:41:22 +0100 Subject: [PATCH 092/173] Added to switch to the correct repository I always forget this.... And it's nowhere in the documentation --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2679eaa411..dc4c17516b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -59,7 +59,8 @@ Great question! The short version goes like this: * **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool ![Clone the fork](img/clonefork.png) - + + * **Switch to the correct repository** - switch to the v8-dev branch * **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md) * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions) * **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first. From 60e4727e51ad2b818cd137e0f00ab6dd348e3a63 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 7 Nov 2019 12:05:16 +0100 Subject: [PATCH 093/173] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dc4c17516b..e009ee2294 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -60,7 +60,7 @@ Great question! The short version goes like this: ![Clone the fork](img/clonefork.png) - * **Switch to the correct repository** - switch to the v8-dev branch + * **Switch to the correct branch** - switch to the v8-dev branch * **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md) * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions) * **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first. From 6307a0173b5f88c684de85ce7fd4d07c81a3657d Mon Sep 17 00:00:00 2001 From: Bill Bixby Date: Thu, 7 Nov 2019 11:12:12 +0000 Subject: [PATCH 094/173] Edit text file to remove reference to App_Data\NuGetBackup folder --- build/NuSpecs/tools/ReadmeUpgrade.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build/NuSpecs/tools/ReadmeUpgrade.txt b/build/NuSpecs/tools/ReadmeUpgrade.txt index ba88f808c0..91d49d896c 100644 --- a/build/NuSpecs/tools/ReadmeUpgrade.txt +++ b/build/NuSpecs/tools/ReadmeUpgrade.txt @@ -11,17 +11,12 @@ Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P Don't forget to build! -We've done our best to transform your configuration files but in case something is not quite right: remember we -backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed. - -We've overwritten all the files in the Umbraco folder, these have been backed up in -App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or -a package might have added. Only the existing files were overwritten. If you customized anything then make -sure to do a compare and merge with the NuGetBackup folder. +We've done our best to transform your configuration files but in case something is not quite right: we recommmend you look in source control for the previous version so you can find the original files before they were transformed. This NuGet package includes build targets that extend the creation of a deploy package, which is generated by Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use Publish this won't affect you. + The following items will now be automatically included when creating a deploy package or publishing to the file system: umbraco, config\splashes and global.asax. From 55b76ecc4ef931a5aa03a4f85908895c9c0d6525 Mon Sep 17 00:00:00 2001 From: Jeffrey Schoemaker Date: Thu, 7 Nov 2019 12:46:48 +0100 Subject: [PATCH 095/173] Add some help if you're on the wrong repo (#7082) --- .github/BUILD.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/BUILD.md b/.github/BUILD.md index a9c26f3a9b..c6e870f396 100644 --- a/.github/BUILD.md +++ b/.github/BUILD.md @@ -39,6 +39,8 @@ To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the build/build.ps1 +If you only see a build.bat-file, you're probably on the wrong branch. If you switch to the correct branch (dev-v8) the file will appear and you can build it. + You might run into [Powershell quirks](#powershell-quirks). ### Build Infrastructure @@ -209,4 +211,4 @@ The best solution is to unblock the Zip file before un-zipping: right-click the ### Git Quirks -Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details). \ No newline at end of file +Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details). From d747312eb71f2b1b54ce86ed95278351801507ea Mon Sep 17 00:00:00 2001 From: John Sheard Date: Thu, 7 Nov 2019 11:24:36 +0000 Subject: [PATCH 096/173] encode name of folder rename --- .../src/common/resources/datatype.resource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7aedfccacf..60a5d235fe 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 @@ -389,7 +389,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { (umbRequestHelper.getApiUrl( "dataTypeApiBaseUrl", "PostRenameContainer", - { id: id, name: name })), + { id: id, name: encodeURIComponent(name) })), "Failed to rename the folder with id " + id); } }; From 466610bd328603e3d30ceb2ef463769f871cc0f4 Mon Sep 17 00:00:00 2001 From: Paul Seal Date: Thu, 7 Nov 2019 12:18:57 +0000 Subject: [PATCH 097/173] - added delete title to trash icons in the grid --- .../src/views/propertyeditors/grid/grid.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html index 3995ed703d..e889067321 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.html @@ -95,7 +95,7 @@
      - +
      - + Date: Sun, 27 Oct 2019 10:40:03 +0000 Subject: [PATCH 098/173] Changed the styling of Resend Invitation button and fixed the margin --- .../src/less/components/users/umb-user-details.less | 5 +++++ .../src/views/users/views/user/details.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less index 9ddad03b48..7caec3c78e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less @@ -63,6 +63,10 @@ a.umb-user-details-details__back-link { .umb-user-details-details__sidebar { flex: 0 0 @sidebarwidth; + + .umb-button{ + margin-left:0px; + } } @media (max-width: 768px) { @@ -101,6 +105,7 @@ a.umb-user-details-details__back-link { .umb-user-details-details__information-item { margin-bottom: 10px; font-size: 13px; + margin-top:10px; } .umb-user-details-details__information-item-label { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index e759be5e7a..ee77e4c14e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -322,7 +322,7 @@ rows="4"> Date: Thu, 7 Nov 2019 12:44:58 +0000 Subject: [PATCH 099/173] Accessibility Improvement - Open Template in info content app changed from anchor tag to button --- .../src/views/components/content/umb-content-node-info.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index da093f72fe..d72e977010 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -169,9 +169,9 @@ ng-change="updateTemplate(node.template)"> - +
      From 41915080b2745207cdf596921d955831394d2f2e Mon Sep 17 00:00:00 2001 From: andyneil Date: Thu, 7 Nov 2019 15:01:22 +0000 Subject: [PATCH 100/173] Fix incorrect data types for default member properties. (#7086) Issue https://github.com/umbraco/Umbraco-CMS/issues/7051 --- src/Umbraco.Core/Constants-Conventions.cs | 18 ++++++++++++------ .../Implement/MemberTypeRepository.cs | 13 +++++++++++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 6c9407667a..e78c498e66 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -221,7 +221,8 @@ namespace Umbraco.Core FailedPasswordAttempts, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts) { - Name = FailedPasswordAttemptsLabel + Name = FailedPasswordAttemptsLabel, + DataTypeId = Constants.DataTypes.LabelInt } }, { @@ -242,35 +243,40 @@ namespace Umbraco.Core LastLockoutDate, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate) { - Name = LastLockoutDateLabel + Name = LastLockoutDateLabel, + DataTypeId = Constants.DataTypes.LabelDateTime } }, { LastLoginDate, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate) { - Name = LastLoginDateLabel + Name = LastLoginDateLabel, + DataTypeId = Constants.DataTypes.LabelDateTime } }, { LastPasswordChangeDate, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate) { - Name = LastPasswordChangeDateLabel + Name = LastPasswordChangeDateLabel, + DataTypeId = Constants.DataTypes.LabelDateTime } }, { PasswordAnswer, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer) { - Name = PasswordAnswerLabel + Name = PasswordAnswerLabel, + DataTypeId = Constants.DataTypes.LabelString } }, { PasswordQuestion, new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion) { - Name = PasswordQuestionLabel + Name = PasswordQuestionLabel, + DataTypeId = Constants.DataTypes.LabelString } } }; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs index ee651819bf..c3b95dbd8f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs @@ -225,8 +225,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (builtinProperties.ContainsKey(propertyType.Alias)) { //this reset's its current data type reference which will be re-assigned based on the property editor assigned on the next line - propertyType.DataTypeId = 0; - propertyType.DataTypeKey = default; + var propDefinition = builtinProperties[propertyType.Alias]; + if (propDefinition != null) + { + propertyType.DataTypeId = propDefinition.DataTypeId; + propertyType.DataTypeKey = propDefinition.DataTypeKey; + } + else + { + propertyType.DataTypeId = 0; + propertyType.DataTypeKey = default; + } } } } From 0d2c2846d4e9c3d44c956650db33fc2955a394a3 Mon Sep 17 00:00:00 2001 From: Richard Thompson Date: Thu, 7 Nov 2019 14:55:21 +0000 Subject: [PATCH 101/173] Corrected the model variable name The model variable name is registerModel rather than profileModel. --- .../Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index ca6f09bd97..17ed95ea31 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -85,7 +85,7 @@ else easily change it. For example, if you wanted to render a custom editor for this field called "MyEditor" you would create a file at ~/Views/Shared/EditorTemplates/MyEditor.cshtml", then you will change the next line of code to render your specific editor template like: - @Html.EditorFor(m => profileModel.MemberProperties[i].Value, "MyEditor") + @Html.EditorFor(m => registerModel.MemberProperties[i].Value, "MyEditor") *@ @Html.EditorFor(m => registerModel.MemberProperties[i].Value) @Html.HiddenFor(m => registerModel.MemberProperties[i].Alias) From 787529cf0d3541e56048b8973c1bd9d2966101b6 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Thu, 7 Nov 2019 19:41:51 +0100 Subject: [PATCH 102/173] Remove IDataEditor inheritance --- src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs index aea7d28758..b16dbbaa25 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.PropertyEditors { - public interface IDataEditorWithMediaPath : IDataEditor + public interface IDataEditorWithMediaPath { string GetMediaPath(object value); } From a2551521e45e47da4318012704d4c129e659aaa8 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Mon, 11 Nov 2019 20:48:52 +0100 Subject: [PATCH 103/173] Settings dashboard: Add translations (#6880) --- .../settings/settingsdashboardintro.html | 32 ++++++++++++++----- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 30 +++++++++++++++++ .../Umbraco/config/lang/en_us.xml | 30 +++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html index cc3e57bdc4..0ad88b9894 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html @@ -1,14 +1,30 @@ -

      Start here

      -

      This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section:

      -
      Find out more:
      +

      + Start here +

      + +

      This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section:

      +
      : +
      + Find out more: +
      diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a8c1be1829..653974a6fe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -2303,4 +2303,34 @@ To manage your website, simply open the Umbraco back office and start adding con To get you started + + Start here + This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section + Find out more + + in the Documentation section of Our Umbraco + ]]> + + + Community Forum + ]]> + + + tutorial videos (some are free, some require a subscription) + ]]> + + + productivity boosting tools and commercial support + ]]> + + + training and certification opportunities + ]]> + + 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 cca99b1244..ee3c7199f1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2319,4 +2319,34 @@ To manage your website, simply open the Umbraco back office and start adding con To get you started + + Start here + This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section + Find out more + + in the Documentation section of Our Umbraco + ]]> + + + Community Forum + ]]> + + + tutorial videos (some are free, some require a subscription) + ]]> + + + productivity boosting tools and commercial support + ]]> + + + training and certification opportunities + ]]> + + From 7eef6b40370fa4cd29a112252a49efb8bd6c25b3 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 12 Nov 2019 08:50:31 +0100 Subject: [PATCH 104/173] https://github.com/umbraco/Umbraco-CMS/issues/7125 - Updates the projects to use the same versions as required by our nuspec --- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++++---- src/Umbraco.Web/Umbraco.Web.csproj | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index e28a8e674e..da22d9e809 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + 1.0.0-beta2-19324-01 runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 39826fcc38..2f81623309 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -79,7 +79,7 @@ - + 1.8.14 diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6c20e8c765..51258de413 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -84,9 +84,9 @@ - - - + + + @@ -429,4 +429,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 166eeba352..781e023f78 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -60,9 +60,9 @@ - + - + 2.7.0.100 From 10622cc430dc535beb745d9d7b9d7c24c342ae82 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Tue, 12 Nov 2019 10:26:25 +0000 Subject: [PATCH 105/173] Switched to using a dummy Uri so that runtimeState isn't needed as per review --- src/Umbraco.Examine/MediaValueSetBuilder.cs | 9 ++++----- .../UmbracoExamine/IndexInitializer.cs | 13 +------------ 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 20c92eb982..3e5b9865cc 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -16,16 +16,14 @@ namespace Umbraco.Examine { private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; - private readonly IRuntimeState _runtimeState; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, - IUserService userService, IRuntimeState runtimeState) + IUserService userService) : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; _userService = userService; - _runtimeState = runtimeState; } /// @@ -55,8 +53,9 @@ namespace Umbraco.Examine if (!string.IsNullOrEmpty(umbracoFilePath)) { - var uri = new Uri(_runtimeState.ApplicationUrl.GetLeftPart(UriPartial.Authority) + umbracoFilePath); - umbracoFile = uri.Segments.Last(); + // intentional dummy Uri + var uri = new Uri("https://localhost/" + umbracoFilePath); + umbracoFile = uri.Segments.Last(); } var values = new Dictionary> diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 5c9c965cd1..6c65ae8345 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -45,21 +45,10 @@ namespace Umbraco.Tests.UmbracoExamine public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService(), MockRuntimeState(RuntimeLevel.Run)); + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService()); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } - - public static IRuntimeState MockRuntimeState(RuntimeLevel level) - { - var runtimeState = Mock.Of(); - Mock.Get(runtimeState).Setup(x => x.Level).Returns(level); - Mock.Get(runtimeState).SetupGet(m => m.ApplicationUrl).Returns(new Uri("https://localhost/umbraco")); - - return runtimeState; - } - - public static IContentService GetMockContentService() { long longTotalRecs; From e2adb4964c5f38742025e714d921432ddfc0628c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Nov 2019 09:36:40 +0100 Subject: [PATCH 106/173] remove isProd from Gulp script --- .../gulp/tasks/dependencies.js | 25 +++++++++---------- .../gulp/tasks/removeProductionMode.js | 22 ---------------- .../gulp/util/processJs.js | 9 +++---- src/Umbraco.Web.UI.Client/gulpfile.js | 4 --- 4 files changed, 16 insertions(+), 44 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/gulp/tasks/removeProductionMode.js diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js index a9d77b03c6..def956ac9f 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js @@ -259,19 +259,18 @@ function dependencies() { //css, fonts and image files var assetsTask = gulp.src(config.sources.globs.assets, { allowEmpty: true }); - if (global.isProd === true) { - assetsTask = assetsTask.pipe(imagemin([ - imagemin.gifsicle({interlaced: true}), - imagemin.jpegtran({progressive: true}), - imagemin.optipng({optimizationLevel: 5}), - imagemin.svgo({ - plugins: [ - {removeViewBox: true}, - {cleanupIDs: false} - ] - }) - ])); - } + assetsTask = assetsTask.pipe(imagemin([ + imagemin.gifsicle({interlaced: true}), + imagemin.jpegtran({progressive: true}), + imagemin.optipng({optimizationLevel: 5}), + imagemin.svgo({ + plugins: [ + {removeViewBox: true}, + {cleanupIDs: false} + ] + }) + ])); + assetsTask = assetsTask.pipe(gulp.dest(config.root + config.targets.assets)); stream.add(assetsTask); diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/removeProductionMode.js b/src/Umbraco.Web.UI.Client/gulp/tasks/removeProductionMode.js deleted file mode 100644 index 3481d84e39..0000000000 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/removeProductionMode.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var gulp = require('gulp'); -var through2 = require('through2'); - -function createEmptyStream() { - var pass = through2.obj(); - process.nextTick(pass.end.bind(pass)); - return pass; -} - -/************************** - * Copies all angular JS files into their separate umbraco.*.js file - **************************/ -function removeProductionMode() { - - global.isProd = false; - - return createEmptyStream(); -}; - -module.exports = { removeProductionMode: removeProductionMode }; diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js index d8e4ca61d1..e3e393b661 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processJs.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processJs.js @@ -23,11 +23,10 @@ module.exports = function (files, out) { // sort files in stream by path or any custom sort comparator task = task.pipe(babel()) .pipe(sort()); - - if (global.isProd === true) { - //in production, embed the templates - task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })) - } + + //in production, embed the templates + task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } })) + task = task.pipe(concat(out)) .pipe(wrap('(function(){\n%= body %\n})();')) .pipe(gulp.dest(config.root + config.targets.js)); diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 3c1c8646fd..1e4dc591ca 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -10,8 +10,6 @@ * and then add the exports command to add the new item into the task menu. */ -global.isProd = true; - const { src, dest, series, parallel, lastRun } = require('gulp'); const { dependencies } = require('./gulp/tasks/dependencies'); @@ -20,7 +18,6 @@ const { less } = require('./gulp/tasks/less'); const { testE2e, testUnit } = require('./gulp/tasks/test'); const { views } = require('./gulp/tasks/views'); const { watchTask } = require('./gulp/tasks/watchTask'); -const { removeProductionMode } = require('./gulp/tasks/removeProductionMode'); // Load local overwrites, can be used to overwrite paths in your local setup. var fs = require('fs'); @@ -41,7 +38,6 @@ try { // *********************************************************** exports.build = series(parallel(dependencies, js, less, views), testUnit); exports.dev = series(parallel(dependencies, js, less, views), watchTask); -exports.fastdev = series(removeProductionMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // exports.runTests = series(js, testUnit); From 88f6ddeae4c3c09f709b998f6f1105bcb8f605ee Mon Sep 17 00:00:00 2001 From: Jeavon Date: Wed, 13 Nov 2019 12:07:11 +0000 Subject: [PATCH 107/173] Code tweaks from PR review - also led to adding logging for Deserialization issues and therefore needing to mock the logger for the tests --- src/Umbraco.Examine/MediaValueSetBuilder.cs | 17 +++++++++++++++-- .../UmbracoExamine/IndexInitializer.cs | 7 ++++++- .../Models/Mapping/EntityMapDefinition.cs | 3 +-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 3e5b9865cc..03e7f4944b 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; @@ -16,14 +17,16 @@ namespace Umbraco.Examine { private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; + private readonly ILogger _logger; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, - IUserService userService) + IUserService userService, ILogger logger) : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; _userService = userService; + _logger = logger; } /// @@ -40,7 +43,17 @@ namespace Umbraco.Examine if (umbracoFileSource.DetectIsJson()) { - var cropper = JsonConvert.DeserializeObject(m.GetValue(Constants.Conventions.Media.File)); + ImageCropperValue cropper = null; + try + { + cropper = JsonConvert.DeserializeObject( + m.GetValue(Constants.Conventions.Media.File)); + } + catch (Exception ex) + { + _logger.Error(ex, $"Could not Deserialize ImageCropperValue for item with key {m.Key} "); + } + if (cropper != null) { umbracoFilePath = cropper.Src; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 6c65ae8345..1653de827d 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.UmbracoExamine public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService()); + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService(), GetMockLogger()); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } @@ -146,6 +146,11 @@ namespace Umbraco.Tests.UmbracoExamine return mediaTypeServiceMock.Object; } + public static IProfilingLogger GetMockLogger() + { + return new ProfilingLogger(Mock.Of(), Mock.Of()); + } + public static UmbracoContentIndex GetUmbracoIndexer( IProfilingLogger profilingLogger, Directory luceneDir, diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs index 424054cd6c..f2099f2554 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs @@ -177,9 +177,8 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Values.ContainsKey("nodeName") ? source.Values["nodeName"] : "[no name]"; - if (source.Values.ContainsKey(UmbracoExamineIndex.UmbracoFileFieldName)) + if (source.Values.TryGetValue(UmbracoExamineIndex.UmbracoFileFieldName, out var umbracoFile)) { - var umbracoFile = source.Values[UmbracoExamineIndex.UmbracoFileFieldName]; if (umbracoFile != null) { target.Name = $"{target.Name} ({umbracoFile})"; From 5a004581ada86571341f62d6f222cc4466ca8b35 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 13 Nov 2019 23:54:00 +0100 Subject: [PATCH 108/173] Remove DuplicateKeyException --- .../Collections/ObservableDictionary.cs | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/src/Umbraco.Core/Collections/ObservableDictionary.cs b/src/Umbraco.Core/Collections/ObservableDictionary.cs index 968a4a21cd..c6aedab377 100644 --- a/src/Umbraco.Core/Collections/ObservableDictionary.cs +++ b/src/Umbraco.Core/Collections/ObservableDictionary.cs @@ -235,78 +235,5 @@ namespace Umbraco.Core.Collections } #endregion - - /// - /// The exception that is thrown when a duplicate key inserted. - /// - /// - /// - /// - [Obsolete("Throw an ArgumentException when trying to add a duplicate key instead.")] - [Serializable] - internal class DuplicateKeyException : Exception - { - /// - /// Gets the key. - /// - /// - /// The key. - /// - public string Key { get; } - - /// - /// Initializes a new instance of the class. - /// - public DuplicateKeyException() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - public DuplicateKeyException(string key) - : this(key, null) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public DuplicateKeyException(string key, Exception innerException) - : base("Attempted to insert duplicate key \"" + key + "\" in collection.", innerException) - { - Key = key; - } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected DuplicateKeyException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - Key = info.GetString(nameof(Key)); - } - - /// - /// When overridden in a derived class, sets the with information about the exception. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - /// info - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - info.AddValue(nameof(Key), Key); - - base.GetObjectData(info, context); - } - } } } From f1953bc99cc1095031a0e96c2118d3b1622a8abf Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 13 Nov 2019 23:56:00 +0100 Subject: [PATCH 109/173] Use same argument validation in AddAttribute as in SetAttribute --- src/Umbraco.Core/Xml/XmlHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 8d0399397e..2dd955086d 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -230,7 +230,7 @@ namespace Umbraco.Core.Xml { if (xd == null) throw new ArgumentNullException(nameof(xd)); if (name == null) throw new ArgumentNullException(nameof(name)); - if (string.IsNullOrEmpty(name)) throw new ArgumentException("Value can't be empty.", nameof(name)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); var temp = xd.CreateAttribute(name); temp.Value = value; From 76de5cc4c7b0659b2a78f8c9e34fc2e5e535c62e Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 13 Nov 2019 23:57:26 +0100 Subject: [PATCH 110/173] Changed NullReferenceException to InvalidOperationException --- src/Umbraco.Web/GridTemplateExtensions.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/GridTemplateExtensions.cs b/src/Umbraco.Web/GridTemplateExtensions.cs index 0bc45e265e..afa929cfbb 100644 --- a/src/Umbraco.Web/GridTemplateExtensions.cs +++ b/src/Umbraco.Web/GridTemplateExtensions.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web var view = "Grid/" + framework; var prop = contentItem.GetProperty(propertyAlias); - if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); // TODO NullReferenceException should not be thrown by user code + if (prop == null) throw new InvalidOperationException("No property type found with alias " + propertyAlias); var model = prop.GetValue(); var asString = model as string; @@ -53,10 +53,12 @@ namespace Umbraco.Web var view = "Grid/" + framework; return html.Partial(view, property.GetValue()); } + public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html) { return GetGridHtml(contentItem, html, "bodyText", "bootstrap3"); } + public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); @@ -64,6 +66,7 @@ namespace Umbraco.Web return GetGridHtml(contentItem, html, propertyAlias, "bootstrap3"); } + public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); @@ -71,7 +74,7 @@ namespace Umbraco.Web var view = "Grid/" + framework; var prop = contentItem.GetProperty(propertyAlias); - if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias); // TODO NullReferenceException should not be thrown by user code + if (prop == null) throw new InvalidOperationException("No property type found with alias " + propertyAlias); var model = prop.GetValue(); var asString = model as string; From 84495f23b556684b8fdd8b7cb861c62b255c792c Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:03:05 +0100 Subject: [PATCH 111/173] Update ufprt field HTML to use double quotes --- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 18f74db569..71ca52143a 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -245,7 +245,7 @@ namespace Umbraco.Web } //write out the hidden surface form routes - _viewContext.Writer.Write(""); + _viewContext.Writer.Write(""); base.Dispose(disposing); } From b486c54645318a1f060ab540d4bfdbe3b40ad810 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:05:52 +0100 Subject: [PATCH 112/173] Use same argument validation in BeginUmbracoForm overloads --- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 71ca52143a..0fd591e96b 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -355,7 +355,7 @@ namespace Umbraco.Web if (action == null) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName)); return html.BeginUmbracoForm(action, controllerName, "", additionalRouteVals, htmlAttributes, method); } @@ -693,7 +693,7 @@ namespace Umbraco.Web if (action == null) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName)); var formAction = Current.UmbracoContext.OriginalRequestUrl.PathAndQuery; return html.RenderForm(formAction, method, htmlAttributes, controllerName, action, area, additionalRouteVals); From 9f19546c4277c96e576aba3dc715697dfb240886 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:10:16 +0100 Subject: [PATCH 113/173] Validate options argument and authentication type --- src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs b/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs index 6e05a929b3..7fca5edcbc 100644 --- a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs +++ b/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs @@ -75,9 +75,8 @@ namespace Umbraco.Web.Security /// public static void ForUmbracoBackOffice(this AuthenticationOptions options, string style, string icon, string callbackPath = null) { - // TODO Change exceptions to InvalidOperationException, as the value isn't an argument - if (options.AuthenticationType == null) throw new ArgumentNullException(nameof(options.AuthenticationType)); - if (string.IsNullOrEmpty(options.AuthenticationType)) throw new ArgumentException("Value can't be empty.", nameof(options.AuthenticationType)); + if (options == null) throw new ArgumentNullException(nameof(options)); + if (string.IsNullOrEmpty(options.AuthenticationType)) throw new InvalidOperationException("The authentication type can't be null or empty."); //Ensure the prefix is set if (options.AuthenticationType.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix) == false) From 4a84f63ab4df901ef288332452c2ba5040faa120 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:20:04 +0100 Subject: [PATCH 114/173] Updated obsolete message for WontImplementException --- src/Umbraco.Core/Exceptions/WontImplementException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Exceptions/WontImplementException.cs b/src/Umbraco.Core/Exceptions/WontImplementException.cs index 9ca320eeda..e354bc4c3d 100644 --- a/src/Umbraco.Core/Exceptions/WontImplementException.cs +++ b/src/Umbraco.Core/Exceptions/WontImplementException.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Exceptions /// /// [Serializable] - [Obsolete("If a method or operation is not, and will not be, implemented, it is invalid, so we should throw an InvalidOperationException instead.")] + [Obsolete("If a method or operation is not, and will not be, implemented, it is invalid or not supported, so we should throw either an InvalidOperationException or NotSupportedException instead.")] public class WontImplementException : NotImplementedException { /// From 3ef10ac8fcc9d00916c3342857e979f4c755b2c1 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:28:24 +0100 Subject: [PATCH 115/173] Constrain generic type of DataOperationException to an enum --- src/Umbraco.Core/Exceptions/DataOperationException.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Exceptions/DataOperationException.cs b/src/Umbraco.Core/Exceptions/DataOperationException.cs index 233f9ccf21..c004e391fe 100644 --- a/src/Umbraco.Core/Exceptions/DataOperationException.cs +++ b/src/Umbraco.Core/Exceptions/DataOperationException.cs @@ -8,9 +8,9 @@ namespace Umbraco.Core.Exceptions /// /// /// - [Obsolete("Refactor the generic type to a concrete serializable type.")] [Serializable] internal class DataOperationException : Exception + where T : Enum { /// /// Gets the operation. @@ -74,7 +74,7 @@ namespace Umbraco.Core.Exceptions protected DataOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - Operation = (T)info.GetValue(nameof(Operation), typeof(T)); + Operation = (T)Enum.Parse(typeof(T), info.GetString(nameof(Operation))); } /// @@ -90,7 +90,7 @@ namespace Umbraco.Core.Exceptions throw new ArgumentNullException(nameof(info)); } - info.AddValue(nameof(Operation), Operation); + info.AddValue(nameof(Operation), Enum.GetName(typeof(T), Operation)); base.GetObjectData(info, context); } From 03c7bcc6c9c90208308c50c67b2fd87cdbd2f8c1 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:29:57 +0100 Subject: [PATCH 116/173] Remove serialization of ViewModel in InstallException --- src/Umbraco.Web/Install/InstallException.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web/Install/InstallException.cs b/src/Umbraco.Web/Install/InstallException.cs index dfa2bc461b..d20b8ef747 100644 --- a/src/Umbraco.Web/Install/InstallException.cs +++ b/src/Umbraco.Web/Install/InstallException.cs @@ -7,7 +7,6 @@ namespace Umbraco.Web.Install /// Used for steps to be able to return a JSON structure back to the UI. /// /// - [Obsolete("Refactor the view model into a concrete serializable type.")] [Serializable] internal class InstallException : Exception { @@ -84,7 +83,6 @@ namespace Umbraco.Web.Install : base(info, context) { View = info.GetString(nameof(View)); - ViewModel = info.GetValue(nameof(ViewModel), typeof(object)); } /// @@ -101,7 +99,6 @@ namespace Umbraco.Web.Install } info.AddValue(nameof(View), View); - info.AddValue(nameof(ViewModel), ViewModel, typeof(object)); base.GetObjectData(info, context); } From b494678dd9266bc8e1ca138d8aba2ea3f903105b Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 14 Nov 2019 00:57:13 +0100 Subject: [PATCH 117/173] Changed thrown exception types to InvalidOperationException --- .../Implement/EntityContainerRepository.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs index 89a62d997e..505cbfc816 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityContainerRepository.cs @@ -162,12 +162,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistNewItem(EntityContainer entity) { - // TODO Ensure correct exceptions are thrown (entity.Name is not an argument and NullReferenceException shouldn't be thrown by user code) if (entity == null) throw new ArgumentNullException(nameof(entity)); EnsureContainerType(entity); - if (entity.Name == null) throw new ArgumentNullException(nameof(entity.Name)); - if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(entity.Name)); + if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null."); + if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters."); entity.Name = entity.Name.Trim(); // guard against duplicates @@ -187,7 +186,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType)); if (parentDto == null) - throw new NullReferenceException("Could not find parent container with id " + entity.ParentId); + throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId); level = parentDto.Level; path = parentDto.Path; @@ -226,12 +225,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // protected override void PersistUpdatedItem(EntityContainer entity) { - // TODO Ensure correct exceptions are thrown (entity.Name is not an argument and NullReferenceException shouldn't be thrown by user code) if (entity == null) throw new ArgumentNullException(nameof(entity)); EnsureContainerType(entity); - if (entity.Name == null) throw new ArgumentNullException(nameof(entity.Name)); - if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(entity.Name)); + if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null."); + if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters."); entity.Name = entity.Name.Trim(); // find container to update @@ -261,7 +259,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType)); if (parent == null) - throw new NullReferenceException("Could not find parent container with id " + entity.ParentId); + throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId); nodeDto.Level = Convert.ToInt16(parent.Level + 1); nodeDto.Path = parent.Path + "," + nodeDto.NodeId; From c0dc545c277cef0b0add95dbd09bfed4589c950e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 14 Nov 2019 13:54:04 +0100 Subject: [PATCH 118/173] Fix for csproj --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index acd521d7c6..65055bd8e4 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -350,9 +350,6 @@ 8400 / http://localhost:8400/ - 8300 - / - http://localhost:8300/ False False @@ -434,4 +431,4 @@ - \ No newline at end of file + From a97e1318913a24002270b15bad65f3ab4bf7f3ae Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 14 Nov 2019 14:00:49 +0100 Subject: [PATCH 119/173] Bump version o 8.6 (8.5 reserved for MB release) --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 5614cb8c41..363677b826 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.4.0")] -[assembly: AssemblyInformationalVersion("8.4.0")] +[assembly: AssemblyFileVersion("8.6.0")] +[assembly: AssemblyInformationalVersion("8.6.0")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 65055bd8e4..35fa5b8149 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -347,9 +347,9 @@ False True - 8400 + 8600 / - http://localhost:8400/ + http://localhost:8600/ False False From 979ccebe75cc42bf5dfb44002fe9911230f106de Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 15 Nov 2019 21:19:22 +0100 Subject: [PATCH 120/173] Make publicly exposed internal exceptions public --- src/Umbraco.Core/Exceptions/PanicException.cs | 2 +- src/Umbraco.Web/Install/InstallException.cs | 2 +- src/Umbraco.Web/Media/Exif/ExifExceptions.cs | 2 +- src/Umbraco.Web/Media/Exif/JPEGExceptions.cs | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Exceptions/PanicException.cs b/src/Umbraco.Core/Exceptions/PanicException.cs index c3564d2834..75edf7fd73 100644 --- a/src/Umbraco.Core/Exceptions/PanicException.cs +++ b/src/Umbraco.Core/Exceptions/PanicException.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Exceptions /// /// [Serializable] - internal class PanicException : Exception + public class PanicException : Exception { /// /// Initializes a new instance of the class. diff --git a/src/Umbraco.Web/Install/InstallException.cs b/src/Umbraco.Web/Install/InstallException.cs index 194370f6ef..3dc297e6b2 100644 --- a/src/Umbraco.Web/Install/InstallException.cs +++ b/src/Umbraco.Web/Install/InstallException.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.Install /// /// [Serializable] - internal class InstallException : Exception + public class InstallException : Exception { /// /// Gets the view. diff --git a/src/Umbraco.Web/Media/Exif/ExifExceptions.cs b/src/Umbraco.Web/Media/Exif/ExifExceptions.cs index 136d1d9104..976fb849ed 100644 --- a/src/Umbraco.Web/Media/Exif/ExifExceptions.cs +++ b/src/Umbraco.Web/Media/Exif/ExifExceptions.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.Media.Exif /// /// [Serializable] - internal class NotValidExifFileException : Exception + public class NotValidExifFileException : Exception { /// /// Initializes a new instance of the class. diff --git a/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs b/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs index b18084e0be..f23dbeb4d4 100644 --- a/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs +++ b/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs @@ -50,7 +50,7 @@ namespace Umbraco.Web.Media.Exif /// /// [Serializable] - internal class NotValidJPEGFileException : Exception + public class NotValidJPEGFileException : Exception { /// /// Initializes a new instance of the class. @@ -91,7 +91,7 @@ namespace Umbraco.Web.Media.Exif /// /// [Serializable] - internal class NotValidTIFFileException : Exception + public class NotValidTIFFileException : Exception { /// /// Initializes a new instance of the class. @@ -173,7 +173,7 @@ namespace Umbraco.Web.Media.Exif /// /// [Serializable] - internal class SectionExceeds64KBException : Exception + public class SectionExceeds64KBException : Exception { /// /// Initializes a new instance of the class. From 5c160f6390c2f63d861746ff07782cd2d01d153b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 18 Nov 2019 14:20:44 +0100 Subject: [PATCH 121/173] Only replace content by EmbedItem if the selected content is of type EmbedItem. --- .../src/common/services/tinymce.service.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 4d4f8792cf..f6c0f94edd 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 @@ -543,11 +543,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s 'contenteditable': false }, embed.preview); - - if (activeElement) { + + // Only replace if actieElement is an Embed element. + if (activeElement && activeElement.nodeName.toUpperCase() === "DIV" && activeElement.classList.contains("embeditem")){ activeElement.replaceWith(wrapper); // directly replaces the html node - } - else { + } else { editor.selection.setNode(wrapper); } }, From 6faa5d79d37e8f8e7fbc775ad4ff4ab215a89205 Mon Sep 17 00:00:00 2001 From: mikkelhm Date: Mon, 18 Nov 2019 22:17:39 +0100 Subject: [PATCH 122/173] Close unclosed key element in italian language file --- src/Umbraco.Web.UI/Umbraco/config/lang/it.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml index a7735fba79..d2a1d75ee7 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml @@ -339,7 +339,7 @@ Larghezza Si Riordina - Ho finito di ordinare/key> + Ho finito di ordinare Colore di sfondo From 7c1af34b3e36ef38943da096597b05f7bc5e545f Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 19 Nov 2019 08:58:00 +0000 Subject: [PATCH 123/173] Update src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js --- .../src/common/services/tinymce.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f6c0f94edd..b0941bd5ad 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 @@ -544,7 +544,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }, embed.preview); - // Only replace if actieElement is an Embed element. + // Only replace if activeElement is an Embed element. if (activeElement && activeElement.nodeName.toUpperCase() === "DIV" && activeElement.classList.contains("embeditem")){ activeElement.replaceWith(wrapper); // directly replaces the html node } else { From 54b3e7e60d7ca45df9143487b6e62a5064bb7317 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 19 Nov 2019 12:35:36 +0100 Subject: [PATCH 124/173] Moved custom validation message database migrations into 8.4 folder and namespace --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 2 +- .../AddPropertyTypeValidationMessageColumns.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/Umbraco.Core/Migrations/Upgrade/{V_8_3_0 => V_8_4_0}/AddPropertyTypeValidationMessageColumns.cs (92%) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index a328a13bed..d1da2ef3e0 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common; using Umbraco.Core.Migrations.Upgrade.V_8_0_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_1; using Umbraco.Core.Migrations.Upgrade.V_8_1_0; -using Umbraco.Core.Migrations.Upgrade.V_8_3_0; +using Umbraco.Core.Migrations.Upgrade.V_8_4_0; namespace Umbraco.Core.Migrations.Upgrade { diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_3_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs similarity index 92% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_3_0/AddPropertyTypeValidationMessageColumns.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs index ac3f959377..4eb0931979 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_3_0/AddPropertyTypeValidationMessageColumns.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs @@ -1,7 +1,7 @@ using System.Linq; using Umbraco.Core.Persistence.Dtos; -namespace Umbraco.Core.Migrations.Upgrade.V_8_3_0 +namespace Umbraco.Core.Migrations.Upgrade.V_8_4_0 { public class AddPropertyTypeValidationMessageColumns : MigrationBase { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 999b69cc9a..08e4d6081a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -260,7 +260,7 @@ - + @@ -1573,4 +1573,4 @@ - + \ No newline at end of file From f42715548089e9fe294ba3c2ea6525b22fbd21db Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 21 Nov 2019 19:39:30 +1100 Subject: [PATCH 125/173] Removes unused exceptions --- .../Exceptions/ConnectionException.cs | 45 ------------------- .../Migrations/DataLossException.cs | 45 ------------------- .../Services/Implement/PackagingService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- src/Umbraco.Web/Media/Exif/ExifExceptions.cs | 40 ----------------- .../Media/Exif/ExifExtendedProperty.cs | 2 +- src/Umbraco.Web/Media/Exif/JPEGExceptions.cs | 40 ----------------- 7 files changed, 3 insertions(+), 175 deletions(-) delete mode 100644 src/Umbraco.Core/Exceptions/ConnectionException.cs delete mode 100644 src/Umbraco.Core/Migrations/DataLossException.cs diff --git a/src/Umbraco.Core/Exceptions/ConnectionException.cs b/src/Umbraco.Core/Exceptions/ConnectionException.cs deleted file mode 100644 index 517d0633a0..0000000000 --- a/src/Umbraco.Core/Exceptions/ConnectionException.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Exceptions -{ - /// - /// The exception that is thrown when a connection fails. - /// - /// - [Serializable] - internal class ConnectionException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public ConnectionException() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public ConnectionException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public ConnectionException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected ConnectionException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/src/Umbraco.Core/Migrations/DataLossException.cs b/src/Umbraco.Core/Migrations/DataLossException.cs deleted file mode 100644 index 1ba10bce62..0000000000 --- a/src/Umbraco.Core/Migrations/DataLossException.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Migrations -{ - /// - /// The exception that is thrown if a migration has executed, but the whole process has failed and cannot be rolled back. - /// - /// - [Serializable] - internal class DataLossException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public DataLossException() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public DataLossException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The inner exception. - public DataLossException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected DataLossException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index b4dcc70a96..d85c688a0d 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -57,7 +57,7 @@ namespace Umbraco.Core.Services.Implement } catch (HttpRequestException ex) { - throw new ConnectionException("An error occurring downloading the package from " + url, ex); + throw new HttpRequestException("An error occurring downloading the package from " + url, ex); } //successful diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d6b63880d6..6a0a77794e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -680,7 +680,6 @@ - @@ -1065,7 +1064,6 @@ - @@ -1572,4 +1570,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web/Media/Exif/ExifExceptions.cs b/src/Umbraco.Web/Media/Exif/ExifExceptions.cs index 976fb849ed..3d2ce61e9e 100644 --- a/src/Umbraco.Web/Media/Exif/ExifExceptions.cs +++ b/src/Umbraco.Web/Media/Exif/ExifExceptions.cs @@ -44,44 +44,4 @@ namespace Umbraco.Web.Media.Exif { } } - /// - /// The exception that is thrown when an invalid enum type is given to an ExifEnumProperty. - /// - /// - [Serializable] - internal class UnknownEnumTypeException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public UnknownEnumTypeException() - : base("Unknown enum type.") - { } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public UnknownEnumTypeException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public UnknownEnumTypeException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected UnknownEnumTypeException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } } diff --git a/src/Umbraco.Web/Media/Exif/ExifExtendedProperty.cs b/src/Umbraco.Web/Media/Exif/ExifExtendedProperty.cs index 40ba275fe4..63c1ce3365 100644 --- a/src/Umbraco.Web/Media/Exif/ExifExtendedProperty.cs +++ b/src/Umbraco.Web/Media/Exif/ExifExtendedProperty.cs @@ -64,7 +64,7 @@ namespace Umbraco.Web.Media.Exif return new ExifInterOperability(tagid, 3, 1, ExifBitConverter.GetBytes((ushort)((object)mValue), BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); } else - throw new UnknownEnumTypeException(); + throw new InvalidOperationException($"An invalid enum type ({basetype.FullName}) was provided for type {type.FullName}"); } } } diff --git a/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs b/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs index f23dbeb4d4..40fd9f3be8 100644 --- a/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs +++ b/src/Umbraco.Web/Media/Exif/JPEGExceptions.cs @@ -4,46 +4,6 @@ using System.Runtime.Serialization; namespace Umbraco.Web.Media.Exif { - /// - /// The exception that is thrown when the format of the image file could not be understood. - /// - /// - [Serializable] - internal class NotValidImageFileException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public NotValidImageFileException() - : base("Not a valid image file.") - { } - - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public NotValidImageFileException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. - public NotValidImageFileException(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected NotValidImageFileException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } /// /// The exception that is thrown when the format of the JPEG file could not be understood. From 042637c3b6216db99ba7eef292a68237e9308c20 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 21 Nov 2019 10:34:17 +0100 Subject: [PATCH 126/173] Move migration to 8.6.0 - 8.4 is hactoberfest, 8.5 is Models Builder only and dev/v8 is 8.6.0 --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 4 +++- .../AddPropertyTypeValidationMessageColumns.cs | 4 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) rename src/Umbraco.Core/Migrations/Upgrade/{V_8_4_0 => V_8_6_0}/AddPropertyTypeValidationMessageColumns.cs (86%) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index d1da2ef3e0..223603be14 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common; using Umbraco.Core.Migrations.Upgrade.V_8_0_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_1; using Umbraco.Core.Migrations.Upgrade.V_8_1_0; -using Umbraco.Core.Migrations.Upgrade.V_8_4_0; +using Umbraco.Core.Migrations.Upgrade.V_8_6_0; namespace Umbraco.Core.Migrations.Upgrade { @@ -182,6 +182,8 @@ namespace Umbraco.Core.Migrations.Upgrade To("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}"); To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); + + // to 8.6.0 To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs similarity index 86% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs index 4eb0931979..30eb30109e 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_4_0/AddPropertyTypeValidationMessageColumns.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs @@ -1,7 +1,7 @@ using System.Linq; using Umbraco.Core.Persistence.Dtos; -namespace Umbraco.Core.Migrations.Upgrade.V_8_4_0 +namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { public class AddPropertyTypeValidationMessageColumns : MigrationBase { @@ -14,7 +14,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_4_0 var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); AddColumnIfNotExists(columns, "mandatoryMessage"); - AddColumnIfNotExists(columns, "validationRegExpMessage"); + AddColumnIfNotExists(columns, "validationRegExpMessage"); } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 08e4d6081a..1ef6608f74 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -260,7 +260,7 @@ - + From c7c98e1750a33902d7785bc75f838f0051d2d955 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 21 Nov 2019 10:57:15 +0100 Subject: [PATCH 127/173] Add required message validation to textarea --- .../views/propertyeditors/textarea/textarea.controller.js | 8 +++++++- .../src/views/propertyeditors/textarea/textarea.html | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js index 45df813835..884cc62d43 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js @@ -1,4 +1,4 @@ -function textAreaController($scope) { +function textAreaController($scope, validationMessageService) { // macro parameter editor doesn't contains a config object, // so we create a new one to hold any properties @@ -22,5 +22,11 @@ function textAreaController($scope) { } } $scope.model.change(); + + // Set the message to use for when a mandatory field isn't completed. + // Will either use the one provided on the property type or a localised default. + validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) { + $scope.mandatoryMessage = value; + }); } angular.module('umbraco').controller("Umbraco.PropertyEditors.textAreaController", textAreaController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html index 4842a6bfb7..04bd8590d2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html @@ -3,7 +3,7 @@ - Required + {{mandatoryMessage}} {{textareaFieldForm.textarea.errorMsg}} From 7ccfe809aed31b779afb82c1e523b0e72e08b420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 21 Nov 2019 12:09:46 +0100 Subject: [PATCH 128/173] Do not check style of DocumentFragment. (cherry picked from commit 3b7d5995e4cdeab56265114416e7ea17b8ca7cbd) --- .../src/common/services/tabbable.service.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js index 3782128af6..2f7af50804 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js @@ -184,7 +184,13 @@ }); if (cached) return cached[1]; - nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node); + if (!nodeComputedStyle) { + if (node instanceof DocumentFragment) { + return false; + } else { + nodeComputedStyle = this.doc.defaultView.getComputedStyle(node); + } + } var result = false; From 213fa0e71e39dff44c109069a030fa28bc564f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 21 Nov 2019 12:14:26 +0100 Subject: [PATCH 129/173] return true, cause DocumentFragment is never displayed. (cherry picked from commit ec9c99554ace24b5a1fbeae4a22dbe29cdc62f77) --- .../src/common/services/tabbable.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js index 2f7af50804..feab7860ca 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js @@ -186,7 +186,7 @@ if (!nodeComputedStyle) { if (node instanceof DocumentFragment) { - return false; + return true;// though DocumentFragment dosnt directly have display 'none', we know that it will never be visible, and therefor we return true. (and do not cache this, cause it will change if appended to the DOM) } else { nodeComputedStyle = this.doc.defaultView.getComputedStyle(node); } From f209905d2ad58e8c2df651e1e0b81fe3c5400114 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 21 Nov 2019 13:52:42 +0100 Subject: [PATCH 130/173] Update src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js (cherry picked from commit 7798937ea7d26bd42d122a1ebdb90859e4cb11c5) --- .../src/common/services/tabbable.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js index feab7860ca..35f0d8a34a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js @@ -186,7 +186,7 @@ if (!nodeComputedStyle) { if (node instanceof DocumentFragment) { - return true;// though DocumentFragment dosnt directly have display 'none', we know that it will never be visible, and therefor we return true. (and do not cache this, cause it will change if appended to the DOM) + return true;// though DocumentFragment doesn't directly have display 'none', we know that it will never be visible, and therefore we return true. (and do not cache this, cause it will change if appended to the DOM) } else { nodeComputedStyle = this.doc.defaultView.getComputedStyle(node); } From 3402f36de319c02efa42f6afe64c23a7fd40eea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 22 Nov 2019 09:02:28 +0100 Subject: [PATCH 131/173] check if template tree exists, to avoid error if its not present. --- .../components/tree/umbtree.directive.js | 21 +++++++++++++++++++ .../src/common/services/navigation.service.js | 16 ++++++++++++++ .../views/documenttypes/edit.controller.js | 15 ++++++++----- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js index 2bd93a4b27..3d743c7e9a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js @@ -65,6 +65,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use vm.reloadNode = reloadNode; vm.syncTree = syncTree; vm.loadChildren = loadChildren; + vm.hasTree = hasTree; //wire up the exposed api object for hosting controllers if ($scope.api) { @@ -72,6 +73,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use $scope.api.load = vm.load; $scope.api.reloadNode = vm.reloadNode; $scope.api.syncTree = vm.syncTree; + $scope.api.hasTree = vm.hasTree; } //flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should @@ -203,6 +205,25 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use }); } + //given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node + function hasTree(treeAlias) { + + if (!$scope.tree) { + throw "Err in umbtree.directive.loadActiveTree, $scope.tree is null"; + } + + if (!treeAlias) { + return false; + } + + var treeRoots = getTreeRootNodes(); + var foundTree = _.find(treeRoots, function (node) { + return node.metaData.treeAlias.toUpperCase() === treeAlias.toUpperCase(); + }); + + return foundTree !== undefined; + } + //given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node function loadActiveTree(treeAlias) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index ce70e9f543..8d1caab850 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -338,6 +338,22 @@ function navigationService($routeParams, $location, $q, $injector, eventsService }); }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#hasTree + * @methodOf umbraco.services.navigationService + * + * @description + * Checks if a tree with the given alias exists. + * + * @param {String} treeAlias the tree alias to check + */ + hasTree: function (treeAlias) { + return navReadyPromise.promise.then(function () { + return mainTreeApi.hasTree(treeAlias); + }); + }, + /** Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to have to set an active tree and then sync, the new API does this in one method by using syncTree diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js index 48e0e6e46b..9d73aa8838 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js @@ -519,11 +519,16 @@ })); evts.push(eventsService.on("editors.documentType.saved", function(name, args) { - if(args.documentType.allowedTemplates.length > 0){ - navigationService.syncTree({ tree: "templates", path: [], forceReload: true }) - .then(function (syncArgs) { - navigationService.reloadNode(syncArgs.node) - }); + if(args.documentType.allowedTemplates.length > 0) { + navigationService.hasTree("templates").then(function (treeExists) { + if (treeExists) { + navigationService.syncTree({ tree: "templates", path: [], forceReload: true }) + .then(function (syncArgs) { + navigationService.reloadNode(syncArgs.node) + } + ); + } + }); } })); From e83589a0241b80c97211b29d23d0930c29504da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 22 Nov 2019 09:59:11 +0100 Subject: [PATCH 132/173] rename reference tab to info tab --- .../datatypes/datatype.edit.controller.js | 6 +- ...troller.js => datatype.info.controller.js} | 10 +- .../views/datatypes/views/datatype.info.html | 135 ++++++++++++++++++ .../datatypes/views/datatype.references.html | 112 --------------- 4 files changed, 143 insertions(+), 120 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/datatypes/views/{datatype.references.controller.js => datatype.info.controller.js} (75%) create mode 100644 src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.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 15fb103ecd..2bc94e040f 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", - "references_tabName" + "general_info" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -220,9 +220,9 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic }, { "name": values[1], - "alias": "references", + "alias": "info", "icon": "icon-molecular-network", - "view": "views/datatypes/views/datatype.references.html" + "view": "views/datatypes/views/datatype.info.html" } ]; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js similarity index 75% rename from src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.controller.js rename to src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js index a152e2e86f..d4aff871a2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.controller.js @@ -1,12 +1,12 @@ /** * @ngdoc controller - * @name Umbraco.Editors.DataType.ReferencesController + * @name Umbraco.Editors.DataType.InfoController * @function * * @description - * The controller for the references view of the datatype editor + * The controller for the info view of the datatype editor */ -function DataTypeReferencesController($scope, $routeParams, dataTypeResource, eventsService, $timeout) { +function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsService, $timeout) { var vm = this; var evts = []; @@ -34,7 +34,7 @@ function DataTypeReferencesController($scope, $routeParams, dataTypeResource, ev // load data type references when the references tab is activated evts.push(eventsService.on("app.tabChange", function (event, args) { $timeout(function () { - if (args.alias === "references") { + if (args.alias === "info") { loadRelations(); } }); @@ -52,4 +52,4 @@ function DataTypeReferencesController($scope, $routeParams, dataTypeResource, ev } -angular.module("umbraco").controller("Umbraco.Editors.DataType.ReferencesController", DataTypeReferencesController); +angular.module("umbraco").controller("Umbraco.Editors.DataType.InfoController", DataTypeInfoController); diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html new file mode 100644 index 0000000000..ff5d71c437 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html @@ -0,0 +1,135 @@ +
      + +
      +
      + + + + + + + + + + This Data Type has no references. + + + + + +
      + + + +
      + +
      + +
      + +
      +
      +
      +
      +
      Name
      +
      Alias
      +
      Used in
      +
      Open
      +
      +
      +
      +
      +
      +
      {{::reference.name}}
      +
      {{::reference.alias}}
      +
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      + +
      +
      +
      +
      + + + +
      + +
      + +
      + +
      +
      +
      +
      +
      Name
      +
      Alias
      +
      Used in
      +
      Open
      +
      +
      +
      +
      +
      +
      {{::reference.name}}
      +
      {{::reference.alias}}
      +
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      + +
      +
      +
      +
      + + + +
      + +
      + +
      + +
      +
      +
      +
      +
      Name
      +
      Alias
      +
      Used in
      +
      Open
      +
      +
      +
      +
      +
      +
      {{::reference.name}}
      +
      {{::reference.alias}}
      +
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      + +
      +
      +
      + +
      + + +
      + +
      + +
      + + + + + +
      {{model.content.id}}
      + {{model.content.key}} +
      + +
      +
      +
      + +
      + + +
      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 deleted file mode 100644 index 6c9fabc848..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.references.html +++ /dev/null @@ -1,112 +0,0 @@ -
      - - - - - - - - This Data Type has no references. - - - - - -
      - - - -
      - -
      - -
      - -
      -
      -
      -
      -
      Name
      -
      Alias
      -
      Used in
      -
      Open
      -
      -
      -
      -
      -
      -
      {{::reference.name}}
      -
      {{::reference.alias}}
      -
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      - -
      -
      -
      -
      - - - -
      - -
      - -
      - -
      -
      -
      -
      -
      Name
      -
      Alias
      -
      Used in
      -
      Open
      -
      -
      -
      -
      -
      -
      {{::reference.name}}
      -
      {{::reference.alias}}
      -
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      - -
      -
      -
      -
      - - - -
      - -
      - -
      - -
      -
      -
      -
      -
      Name
      -
      Alias
      -
      Used in
      -
      Open
      -
      -
      -
      -
      -
      -
      {{::reference.name}}
      -
      {{::reference.alias}}
      -
      {{::reference.properties | umbCmsJoinArray:', ':'name'}}
      - -
      -
      -
      - -
      - - -
      - - -
      From b9104dfaf8d8353e43e7220aeff6953e1ae27744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 22 Nov 2019 14:53:15 +0100 Subject: [PATCH 133/173] moved info properties to the into tab. --- .../src/views/datatypes/datatype.edit.controller.js | 6 +----- .../src/views/datatypes/views/datatype.info.html | 11 ++++++++--- .../src/views/datatypes/views/datatype.settings.html | 11 +---------- 3 files changed, 10 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 2bc94e040f..474b07d12c 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 @@ -24,10 +24,6 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic alias: "selectedEditor", description: "Select a property editor", label: "Property editor" - }, - selectedEditorId: { - alias: "selectedEditorId", - label: "Property editor alias" } }; @@ -221,7 +217,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic { "name": values[1], "alias": "info", - "icon": "icon-molecular-network", + "icon": "icon-info", "view": "views/datatypes/views/datatype.info.html" } ]; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html index ff5d71c437..16b2d4b263 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/views/datatype.info.html @@ -21,7 +21,7 @@ -
      +
      @@ -51,7 +51,7 @@ -
      +
      @@ -77,11 +77,12 @@
      +
      -
      +
      @@ -125,6 +126,10 @@ {{model.content.key}} + +
      {{model.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 index d37aa2fcd0..69a485e5be 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,11 +2,6 @@ - -
      {{model.content.id}}
      - {{model.content.key}} -
      -
      - - -
      -
      - Minimum %0% entries, needs %1% more. -
      -
      -
      -
      - Maximum %0% entries, %1% too many. -
      -
      - - - - - +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html new file mode 100644 index 0000000000..283f4406f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html @@ -0,0 +1,71 @@ +
      + + + + + +
      + +
      + +
      + +
      + + + +
      + +
      + +
      +
      + +
      + +
      +
      + +
      +
      + + + + + + + + +
      +
      + Minimum %0% entries, needs %1% more. +
      +
      +
      +
      + Maximum %0% entries, %1% too many. +
      +
      + +
      + + + + +
      From b0b67ca9574316ad8845af87dbfcc7f2555433f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 25 Nov 2019 15:36:56 +0100 Subject: [PATCH 137/173] removed PropertyEditorService as its not needed anymore --- .../common/services/propertyeditor.service.js | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/common/services/propertyeditor.service.js diff --git a/src/Umbraco.Web.UI.Client/src/common/services/propertyeditor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/propertyeditor.service.js deleted file mode 100644 index 0b24e78567..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/services/propertyeditor.service.js +++ /dev/null @@ -1,29 +0,0 @@ -(function() { - 'use strict'; - - function propertyEditorService() { - /** - * @ngdoc function - * @name umbraco.services.propertyEditorService#expose - * @methodOf umbraco.services.propertyEditorService - * @function - * - * @param {object} scope An object containing API for the PropertyEditor - */ - function exposeAPI(scope, api) { - if (!scope) { - throw "scope cannot be null"; - } - if (!api) { - throw "api cannot be null"; - } - scope.$emit("ExposePropertyEditorAPI", api); - } - - return { - exposeAPI: exposeAPI - }; - } - - angular.module('umbraco.services').factory('propertyEditorService', propertyEditorService); -})(); From b4ae7ebe2ce99d2fb7c114a96fa7b0a84ccd5748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 25 Nov 2019 15:38:11 +0100 Subject: [PATCH 138/173] refactored how communication is done between propertyEditorController and umbPropertyDirective --- .../property/umbproperty.directive.js | 21 +++++--------- .../components/property/umb-property.html | 2 +- .../nestedcontent/nestedcontent.component.js | 28 ++++++++++--------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 06b9e51fba..9917b884a0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -15,19 +15,20 @@ angular.module("umbraco.directives") restrict: 'E', replace: true, templateUrl: 'views/components/property/umb-property.html', - link: function (scope) { + link: function ($scope) { - scope.propertyEditorAPI = {}; + $scope.propertyActions = []; userService.getCurrentUser().then(function (u) { var isAdmin = u.userGroups.indexOf('admin') !== -1; - scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? scope.property.alias : null; + $scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? $scope.property.alias : null; }); }, //Define a controller for this directive to expose APIs to other directives controller: function ($scope, $timeout) { var self = this; + self.propertyActions = []; //set the API properties/methods @@ -35,18 +36,10 @@ angular.module("umbraco.directives") self.setPropertyError = function (errorMsg) { $scope.property.propertyErrorMessage = errorMsg; }; - - var unsubscribe = $scope.$on("ExposePropertyEditorAPI", function(event, api) { - - //avoid eventual parent properties to capture this. - event.stopPropagation(); - - $scope.propertyEditorAPI = api; - }); - $scope.$on("$destroy", function () { - unsubscribe(); - }); + self.setPropertyActions = function(actions) { + $scope.propertyActions = actions; + }; } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index 927f677463..46660fc685 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -21,7 +21,7 @@ - +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js index 03a7207720..0504e29a8b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js @@ -7,12 +7,13 @@ templateUrl: 'views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html', controller: NestedContentController, controllerAs: 'vm', - bindings: { - + require: { + umbProperty: '^umbProperty', + umbVariantContent: '?^^umbVariantContent' } }); - function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState, propertyEditorService) { + function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState) { var vm = this; var model = $scope.$parent.$parent.model; @@ -65,14 +66,14 @@ // remove dublicates aliases = aliases.filter((item, index) => aliases.indexOf(item) === index); + + var nodeName = ""; - // Retrive variant name - var culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; - var activeVariant = _.find(editorState.current.variants, function (v) { - return !v.language || v.language.culture === culture; - }); + if(vm.umbVariantContent) { + nodeName = vm.umbVariantContent.editor.content.name; + } - localizationService.localize("clipboard_labelForArrayOfItemsFrom", [model.label, activeVariant.name]).then(function(data) { + localizationService.localize("clipboard_labelForArrayOfItemsFrom", [model.label, nodeName]).then(function(data) { clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id); }); } @@ -538,13 +539,14 @@ - var api = {}; - api.propertyActions = [ + var propertyActions = [ copyAllEntriesAction ]; - propertyEditorService.exposeAPI($scope, api);// must be executed at a state where the API is set. - + this.$onInit = function () { + this.umbProperty.setPropertyActions(propertyActions); + }; + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { updateModel(); }); From ea8f1c3ae3bbccc2109beb3b5a0b8f4c16fe3a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 25 Nov 2019 15:47:45 +0100 Subject: [PATCH 139/173] make umbProperty optional, since its not present in DocumentTypeEditor --- .../nestedcontent/nestedcontent.component.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js index 0504e29a8b..64bd8eb1ec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js @@ -8,7 +8,7 @@ controller: NestedContentController, controllerAs: 'vm', require: { - umbProperty: '^umbProperty', + umbProperty: '?^umbProperty', umbVariantContent: '?^^umbVariantContent' } }); @@ -534,7 +534,7 @@ } function updatePropertyActionStates() { - copyAllEntriesAction.isDisabled = model.value.length === 0; + copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0; } @@ -544,7 +544,9 @@ ]; this.$onInit = function () { - this.umbProperty.setPropertyActions(propertyActions); + if (this.umbProperty) { + this.umbProperty.setPropertyActions(propertyActions); + } }; var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { From ba625e618181580f5306cf8f86cf5509086f0ec5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 26 Nov 2019 12:20:49 +1100 Subject: [PATCH 140/173] fix up minor inconsistency --- .../components/property/umbproperty.directive.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 9917b884a0..31e797c6b4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -4,7 +4,7 @@ * @restrict E **/ angular.module("umbraco.directives") - .directive('umbProperty', function (umbPropEditorHelper, userService) { + .directive('umbProperty', function (userService) { return { scope: { property: "=", @@ -15,21 +15,20 @@ angular.module("umbraco.directives") restrict: 'E', replace: true, templateUrl: 'views/components/property/umb-property.html', - link: function ($scope) { + link: function (scope) { - $scope.propertyActions = []; + scope.propertyActions = []; userService.getCurrentUser().then(function (u) { var isAdmin = u.userGroups.indexOf('admin') !== -1; - $scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? $scope.property.alias : null; + scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? scope.property.alias : null; }); }, //Define a controller for this directive to expose APIs to other directives - controller: function ($scope, $timeout) { + controller: function ($scope) { var self = this; - self.propertyActions = []; - + //set the API properties/methods self.property = $scope.property; From 311e2afde08131b592ea8dbe5c15514ba969ce47 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Tue, 26 Nov 2019 08:51:48 +0000 Subject: [PATCH 141/173] Use ConcurrentHashSet --- .../Collections/ConcurrentHashSet.cs | 38 +++++++++++++++---- src/Umbraco.Core/RuntimeState.cs | 9 ++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs index 54367ed588..c4dba51acd 100644 --- a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs +++ b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs @@ -70,7 +70,23 @@ namespace Umbraco.Core.Collections /// The number of elements contained in the . /// /// 2 - public int Count => GetThreadSafeClone().Count; + public int Count + { + get + { + try + { + _instanceLocker.EnterReadLock(); + return _innerSet.Count; + } + finally + { + if (_instanceLocker.IsReadLockHeld) + _instanceLocker.ExitReadLock(); + } + + } + } /// /// Gets a value indicating whether the is read-only. @@ -105,8 +121,7 @@ namespace Umbraco.Core.Collections /// public bool TryAdd(T item) { - var clone = GetThreadSafeClone(); - if (clone.Contains(item)) return false; + if (Contains(item)) return false; try { _instanceLocker.EnterWriteLock(); @@ -150,7 +165,16 @@ namespace Umbraco.Core.Collections /// The object to locate in the . public bool Contains(T item) { - return GetThreadSafeClone().Contains(item); + try + { + _instanceLocker.EnterReadLock(); + return _innerSet.Contains(item); + } + finally + { + if (_instanceLocker.IsReadLockHeld) + _instanceLocker.ExitReadLock(); + } } /// @@ -168,13 +192,13 @@ namespace Umbraco.Core.Collections HashSet clone = null; try { - _instanceLocker.EnterWriteLock(); + _instanceLocker.EnterReadLock(); clone = new HashSet(_innerSet, _innerSet.Comparer); } finally { - if (_instanceLocker.IsWriteLockHeld) - _instanceLocker.ExitWriteLock(); + if (_instanceLocker.IsReadLockHeld) + _instanceLocker.ExitReadLock(); } return clone; } diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index 65c93ece05..74ec70828b 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -1,9 +1,8 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Threading; using System.Web; using Semver; +using Umbraco.Core.Collections; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; @@ -23,7 +22,7 @@ namespace Umbraco.Core private readonly ILogger _logger; private readonly IUmbracoSettingsSection _settings; private readonly IGlobalSettings _globalSettings; - private readonly ConcurrentDictionary _applicationUrls = new ConcurrentDictionary(); + private readonly ConcurrentHashSet _applicationUrls = new ConcurrentHashSet(); private readonly Lazy _mainDom; private readonly Lazy _serverRegistrar; @@ -107,11 +106,11 @@ namespace Umbraco.Core // (this is a simplified version of what was in 7.x) // note: should this be optional? is it expensive? var url = request == null ? null : ApplicationUrlHelper.GetApplicationUrlFromCurrentRequest(request, _globalSettings); - var change = url != null && !_applicationUrls.ContainsKey(url); + var change = url != null && !_applicationUrls.Contains(url); if (change) { _logger.Info(typeof(ApplicationUrlHelper), "New url {Url} detected, re-discovering application url.", url); - _applicationUrls[url] = 0; + _applicationUrls.Add(url); } if (ApplicationUrl != null && !change) return; From 37c668b4a80c5bd1d0080fdc7e31c779bb14f6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 26 Nov 2019 11:02:56 +0100 Subject: [PATCH 142/173] ensure history line only goes between entries of the list. --- src/Umbraco.Web.UI.Client/src/less/properties.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/properties.less b/src/Umbraco.Web.UI.Client/src/less/properties.less index 152ea49bbd..8523fe9300 100644 --- a/src/Umbraco.Web.UI.Client/src/less/properties.less +++ b/src/Umbraco.Web.UI.Client/src/less/properties.less @@ -97,7 +97,8 @@ .history-line { width: 2px; - height: 100%; + top: 10px; + bottom: 10px; margin: 0 0 0 14px; background-color: @gray-8; position: absolute; From 4b8ea9499142e817eb4b684467c4e4eeffd38ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 26 Nov 2019 13:59:37 +0100 Subject: [PATCH 143/173] Property-Action for Remove All Entries in Nested Content (#7036) * Property-Action for remove all entries from Nested Content * Add "delete all items" confirmation and rephrase action link a bit * Enforce "delete" confirmation type in overlayService.confirmDelete * rename to controller for easier to solve merge conflicts * update removeAllEntriesAction.isDisabled when content changes * minor correction --- .../src/common/services/overlay.service.js | 1 + ...mponent.js => nestedcontent.controller.js} | 31 +- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4678 +++++++++-------- .../Umbraco/config/lang/en_us.xml | 2 + 5 files changed, 2375 insertions(+), 2339 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/{nestedcontent.component.js => nestedcontent.controller.js} (95%) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index 0b8965e4fe..119f40e114 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -89,6 +89,7 @@ } function confirmDelete(overlay) { + overlay.confirmType = "delete"; confirm(overlay); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js similarity index 95% rename from src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js rename to src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 64bd8eb1ec..37298fad3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -87,6 +87,33 @@ } + var removeAllEntries = function () { + localizationService.localizeMany(["content_nestedContentDeleteAllItems", "general_delete"]).then(function (data) { + overlayService.confirmDelete({ + title: data[1], + content: data[0], + close: function () { + overlayService.close(); + }, + submit: function () { + vm.nodes = []; + setDirty(); + updateModel(); + overlayService.close(); + } + }); + }); + } + + var removeAllEntriesAction = { + labelKey: 'clipboard_labelForRemoveAllEntries', + labelTokens: [], + icon: 'trash', + method: removeAllEntries, + isDisabled: true + } + + // helper to force the current form into the dirty state function setDirty() { @@ -535,12 +562,14 @@ function updatePropertyActionStates() { copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0; + removeAllEntriesAction.isDisabled = model.value.length === 0; } var propertyActions = [ - copyAllEntriesAction + copyAllEntriesAction, + removeAllEntriesAction ]; this.$onInit = function () { diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 974b1af707..0deac8b50f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -272,6 +272,7 @@ Dette oversætter til den følgende tid på serveren: Hvad betyder det?]]> Er du sikker på, at du vil slette dette element? + Er du sikker på, at du vil slette alle elementer? Egenskaben %0% anvender editoren %1% som ikke er understøttet af Nested Content. Der er ikke konfigureret nogen indholdstyper for denne egenskab. %0% fra %1% @@ -1772,6 +1773,7 @@ Mange hilsner fra Umbraco robotten Kopier %0% %0% fra %1% + Fjern alle elementer Åben egenskabshandlinger diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 69e73545f4..c64d2d3eb0 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1,2338 +1,2340 @@ - - - - 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 element type - Select element type - 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.]]> - Document Types within the Settings section.]]> - 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 - 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 - Edit selected media - Insert local link - Insert character - Insert graphic headline - Insert picture - Insert link - 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: - 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, you should unpublish the node in that language 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 - fields - The index cannot be read and will need to be rebuilt - The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation - This index cannot be rebuilt because it has no assigned - IIndexPopulator - - - 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 - Create - 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 - Focal point - 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 - copy to clipboard - 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. - Removing a composition will delete all the associated property data. Once you save the document type there's no way back. - 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 - none - - - - 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 custom validation error message (optional) - Enter a regular expression - Enter a custom validation error message (optional) - You need to add at least - You can only have - items - items selected - Invalid date - Not a number - Invalid email - Custom validation - %1% more.]]> - %1% too many.]]> - - - - 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 - Open context menu for - Current language - Switch language to - Create new folder - Partial View - Partial View Macro - Member - - - 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 - - - Log Levels - Saved Searches - Total Items - Timestamp - Level - Machine - Message - Exception - Properties - Search With Google - Search this message with Google - Search With Bing - Search this message with Bing - Search Our Umbraco - Search this message on Our Umbraco forums and docs - Search Our Umbraco with Google - Search Our Umbraco forums using Google - Search Umbraco Source - Search within Umbraco source code on Github - Search Umbraco Issues - Search Umbraco Issues on Github - Delete this search - Find Logs with Request ID - Find Logs with Namespace - Find Logs with Machine Name - Open - - - Copy %0% - %0% from %1% - - - Open Property Actions - - - Wait - Refresh status - Memory Cache - - - - Reload - Database Cache - - Rebuilding can be expensive. - Use it when reloading is not enough, and you think that the database cache has not been - properly generated—which would indicate some critical Umbraco issue. - ]]> - - Rebuild - Internals - - not need to use it. - ]]> - - Collect - Published Cache Status - Caches - - - Performance profiling - - - Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. -

      -

      - If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. -

      -

      - If you want the profiler to be activated by default for all page renderings, you can use the toggle below. - It will set a cookie in your browser, which then activates the profiler automatically. - In other words, the profiler will only be active by default in your browser - not everyone else's. -

      - ]]> -
      - Activate the profiler by default - Friendly reminder - - - You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. -

      - ]]> -
      - - - Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. -

      -

      - Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. -

      - ]]> -
      - - - Hours of Umbraco training videos are only a click away - - Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

      - ]]> -
      - To get you started - - - Start here - This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section - Find out more - - in the Documentation section of Our Umbraco - ]]> - - - Community Forum - ]]> - - - tutorial videos (some are free, some require a subscription) - ]]> - - - productivity boosting tools and commercial support - ]]> - - - training and certification opportunities - ]]> - - -
      + + + + 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. + Are you sure you want to delete all items? + No content types are configured for this property. + Add element type + Select element type + 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.]]> + Document Types within the Settings section.]]> + 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 + 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 + Edit selected media + Insert local link + Insert character + Insert graphic headline + Insert picture + Insert link + 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: + 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, you should unpublish the node in that language 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 + fields + The index cannot be read and will need to be rebuilt + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation + This index cannot be rebuilt because it has no assigned + IIndexPopulator + + + 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 + Create + 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 + Focal point + 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 + copy to clipboard + 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. + Removing a composition will delete all the associated property data. Once you save the document type there's no way back. + 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 + none + + + + 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 custom validation error message (optional) + Enter a regular expression + Enter a custom validation error message (optional) + You need to add at least + You can only have + items + items selected + Invalid date + Not a number + Invalid email + Custom validation + %1% more.]]> + %1% too many.]]> + + + + 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 + Open context menu for + Current language + Switch language to + Create new folder + Partial View + Partial View Macro + Member + + + 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 + + + Log Levels + Saved Searches + Total Items + Timestamp + Level + Machine + Message + Exception + Properties + Search With Google + Search this message with Google + Search With Bing + Search this message with Bing + Search Our Umbraco + Search this message on Our Umbraco forums and docs + Search Our Umbraco with Google + Search Our Umbraco forums using Google + Search Umbraco Source + Search within Umbraco source code on Github + Search Umbraco Issues + Search Umbraco Issues on Github + Delete this search + Find Logs with Request ID + Find Logs with Namespace + Find Logs with Machine Name + Open + + + Copy %0% + %0% from %1% + Remove all items + + + Open Property Actions + + + Wait + Refresh status + Memory Cache + + + + Reload + Database Cache + + Rebuilding can be expensive. + Use it when reloading is not enough, and you think that the database cache has not been + properly generated—which would indicate some critical Umbraco issue. + ]]> + + Rebuild + Internals + + not need to use it. + ]]> + + Collect + Published Cache Status + Caches + + + Performance profiling + + + Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages. +

      +

      + If you want to activate the profiler for a specific page rendering, simply add umbDebug=true to the querystring when requesting the page. +

      +

      + If you want the profiler to be activated by default for all page renderings, you can use the toggle below. + It will set a cookie in your browser, which then activates the profiler automatically. + In other words, the profiler will only be active by default in your browser - not everyone else's. +

      + ]]> +
      + Activate the profiler by default + Friendly reminder + + + You should never let a production site run in debug mode. Debug mode is turned off by setting debug="false" on the <compilation /> element in web.config. +

      + ]]> +
      + + + Umbraco currently does not run in debug mode, so you can't use the built-in profiler. This is how it should be for a production site. +

      +

      + Debug mode is turned on by setting debug="true" on the <compilation /> element in web.config. +

      + ]]> +
      + + + Hours of Umbraco training videos are only a click away + + Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

      + ]]> +
      + To get you started + + + Start here + This section contains the building blocks for your Umbraco site. Follow the below links to find out more about working with the items in the Settings section + Find out more + + in the Documentation section of Our Umbraco + ]]> + + + Community Forum + ]]> + + + tutorial videos (some are free, some require a subscription) + ]]> + + + productivity boosting tools and commercial support + ]]> + + + training and certification opportunities + ]]> + + +
      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 3a5170892b..c002299767 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -277,6 +277,7 @@ This translates to the following time on the server: What does this mean?]]> Are you sure you want to delete this item? + Are you sure you want to delete all items? Property %0% uses editor %1% which is not supported by Nested Content. No content types are configured for this property. Add element type @@ -2236,6 +2237,7 @@ To manage your website, simply open the Umbraco back office and start adding con Copy %0% %0% from %1% + Remove all items Open Property Actions From 67946b4af31800f626842dfe53b5161a4d1ce1a9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 27 Nov 2019 10:47:20 +1100 Subject: [PATCH 144/173] don't allocate empty dictionary when rendering macro and deal with null vals --- src/Umbraco.Web/Macros/MacroRenderer.cs | 2 +- src/Umbraco.Web/UmbracoComponentRenderer.cs | 4 ++-- src/Umbraco.Web/UmbracoHelper.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 3368def084..8d7d3bc013 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -186,7 +186,7 @@ namespace Umbraco.Web.Macros foreach (var prop in model.Properties) { var key = prop.Key.ToLowerInvariant(); - prop.Value = macroParams.ContainsKey(key) + prop.Value = macroParams != null && macroParams.ContainsKey(key) ? macroParams[key]?.ToString() ?? string.Empty : string.Empty; } diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 9f56813364..a5890c9f97 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -77,7 +77,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(int contentId, string alias, object parameters) { - return RenderMacro(contentId, alias, parameters.ToDictionary()); + return RenderMacro(contentId, alias, parameters?.ToDictionary()); } /// @@ -113,7 +113,7 @@ namespace Umbraco.Web // TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method looks for a lower case match. the whole macro concept needs to be rewritten. //NOTE: the value could have HTML encoded values, so we need to deal with that - var macroProps = parameters.ToDictionary( + var macroProps = parameters?.ToDictionary( x => x.Key.ToLowerInvariant(), i => (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index e76c23a4c1..1bc8df5b24 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -146,7 +146,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias) { - return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, new Dictionary()); + return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, null); } /// @@ -157,7 +157,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, object parameters) { - return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters.ToDictionary()); + return ComponentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters?.ToDictionary()); } /// From 880d65bb42128b7eb3ef15870cf544766f30bd09 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 27 Nov 2019 12:32:16 +0100 Subject: [PATCH 145/173] V8: Add indicators to required properties within nested content (#7217) --- .../src/less/components/umb-nested-content.less | 11 +++++++++++ .../nestedcontent/nestedcontent.controller.js | 1 + .../nestedcontent/nestedcontent.editor.html | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index 455a147395..9d8e886b7a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -14,6 +14,17 @@ pointer-events: none; } +.umb-nested-content--mandatory { + /* + yeah so this is a pain, but we must be super specific in targeting the mandatory property labels, + otherwise all properties within a reqired, nested, nested content property will all appear mandatory + */ + > ng-form > .control-group > .umb-el-wrap > .control-header label:after { + content: '*'; + color: @red; + } +} + .umb-nested-content-overlay { position: absolute; top: 0; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 37298fad3c..afdab78588 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -503,6 +503,7 @@ // Force validation to occur server side as this is the // only way we can have consistency between mandatory and // regex validation messages. Not ideal, but it works. + prop.ncMandatory = prop.validation.mandatory; prop.validation = { mandatory: false, pattern: "" diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html index db0e9a016e..a2105c5675 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html @@ -1,7 +1,7 @@ 
      - + @@ -10,4 +10,4 @@

      {{property.notSupportedMessage}}

      -
      \ No newline at end of file + From c20416a37492afb3d6a811f1080fcf895e2f0a46 Mon Sep 17 00:00:00 2001 From: Jan Skovgaard Date: Wed, 27 Nov 2019 13:38:02 +0100 Subject: [PATCH 146/173] Nested Content: Fix controls to be more accessible (#6998) * Change a to button * Added localization for copy * Correcting lost parts from merge. --- .../src/less/components/umb-nested-content.less | 2 ++ .../nestedcontent/nestedcontent.controller.js | 4 +++- .../nestedcontent.propertyeditor.html | 16 ++++++++++------ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index 9d8e886b7a..b28925bec1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -145,6 +145,8 @@ .umb-nested-content__icon { + background: transparent; + border: 0 none; display: inline-block; padding: 4px; margin: 2px; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index afdab78588..c771b5645b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -47,9 +47,11 @@ vm.hasContentTypes = model.config.contentTypes.length > 0; var labels = {}; - localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) { + vm.labels = labels; + localizationService.localizeMany(["grid_addElement", "content_createEmpty", "actions_copy"]).then(function (data) { labels.grid_addElement = data[0]; labels.content_createEmpty = data[1]; + labels.copy_icon_title = data[2] }); function setCurrentNode(node) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html index 283f4406f5..d24d3796f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html @@ -13,12 +13,16 @@
      - - - - - - + +
      From 9ae05b5b152b1c85ebb4527cece7e43d7013ee71 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 27 Nov 2019 13:40:25 +0100 Subject: [PATCH 147/173] =?UTF-8?q?V8:=20Update=20the=20descriptions=20of?= =?UTF-8?q?=20the=20Nested=20Content=20configuration=E2=80=A6=20(#7224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update the descriptions of the Nested Content configuration options * Reduced the amount of words for further readability --- .../PropertyEditors/NestedContentConfiguration.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs b/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs index b904a2250d..0f53207462 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentConfiguration.cs @@ -8,22 +8,22 @@ namespace Umbraco.Web.PropertyEditors ///
      public class NestedContentConfiguration { - [ConfigurationField("contentTypes", "Document types", "views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html", Description = "Select the document types to use as the item blueprints. Only \"element\" types can be used.")] + [ConfigurationField("contentTypes", "Element Types", "views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html", Description = "Select the Element Types to use as models for the items.")] public ContentType[] ContentTypes { get; set; } - [ConfigurationField("minItems", "Min Items", "number", Description = "Set the minimum number of items allowed.")] + [ConfigurationField("minItems", "Min Items", "number", Description = "Minimum number of items allowed.")] public int? MinItems { get; set; } - [ConfigurationField("maxItems", "Max Items", "number", Description = "Set the maximum number of items allowed.")] + [ConfigurationField("maxItems", "Max Items", "number", Description = "Maximum number of items allowed.")] public int? MaxItems { get; set; } - [ConfigurationField("confirmDeletes", "Confirm Deletes", "boolean", Description = "Set whether item deletions should require confirming.")] + [ConfigurationField("confirmDeletes", "Confirm Deletes", "boolean", Description = "Requires editor confirmation for delete actions.")] public bool ConfirmDeletes { get; set; } = true; - [ConfigurationField("showIcons", "Show Icons", "boolean", Description = "Set whether to show the items doc type icon in the list.")] + [ConfigurationField("showIcons", "Show Icons", "boolean", Description = "Show the Element Type icons.")] public bool ShowIcons { get; set; } = true; - [ConfigurationField("hideLabel", "Hide Label", "boolean", Description = "Set whether to hide the editor label and have the list take up the full width of the editor window.")] + [ConfigurationField("hideLabel", "Hide Label", "boolean", Description = "Hide the property label and let the item list span the full width of the editor window.")] public bool HideLabel { get; set; } public class ContentType From 771fa3028a5aab0bc0bde27b185cd939cb2a7d1d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 28 Nov 2019 09:09:50 +0100 Subject: [PATCH 148/173] V8: Fix Nested Content configuration overflow on smaller screens (#7006) --- .../less/components/umb-nested-content.less | 20 ++----------------- .../nestedcontent.doctypepicker.html | 7 +------ 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index b28925bec1..bf0dd9d109 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -248,7 +248,6 @@ } .umb-nested-content__placeholder { - height: 22px; padding: 4px 6px; border: 1px dashed #d8d7d9; background: 0 0; @@ -259,33 +258,18 @@ text-align: center; &--selected { - border: 1px solid #d8d7d9; + border: none; text-align: left; + padding: 0; } } -.umb-nested-content__placeholder-name{ - font-size: 15px; -} - .umb-nested-content__placeholder:hover { color: #2152a3; border-color: #2152a3; text-decoration: none; } -.umb-nested-content__placeholder-icon-holder { - width: 20px; - text-align: center; - display: inline-block; -} - -.umb-nested-content__placeholder-icon { - font-size: 18px; - vertical-align: middle; -} - - .form-horizontal .umb-nested-content--narrow .controls-row { margin-left: 40% !important; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html index f6bfdecb31..c6860140a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html @@ -24,12 +24,7 @@ {{ph = placeholder(config);""}}
      - - - - - {{ ph.name }} - + Add element type
      From 938d5d207d867b92051e82ec32fee2c662d7912f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 28 Nov 2019 10:35:07 +0100 Subject: [PATCH 149/173] =?UTF-8?q?NC=20PropertyAction=20Delete-All,=20do?= =?UTF-8?q?=20not=20test=20length=20if=20value=20is=20nu=E2=80=A6=20(#7242?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../propertyeditors/nestedcontent/nestedcontent.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index c771b5645b..635a80dbe9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -565,7 +565,7 @@ function updatePropertyActionStates() { copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0; - removeAllEntriesAction.isDisabled = model.value.length === 0; + removeAllEntriesAction.isDisabled = !model.value || model.value.length === 0; } From 71cfbcc7878e86801a6a61e7a3fb143927bc3972 Mon Sep 17 00:00:00 2001 From: abi Date: Fri, 29 Nov 2019 15:13:11 +0000 Subject: [PATCH 150/173] MediaIndexPopulator should use IUmbracoContentIndex instead of UmbracoContentIndex --- src/Umbraco.Examine/MediaIndexPopulator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs index 6dadcbe4b3..1f5b11e54f 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -10,7 +10,7 @@ namespace Umbraco.Examine /// /// Performs the data lookups required to rebuild a media index /// - public class MediaIndexPopulator : IndexPopulator + public class MediaIndexPopulator : IndexPopulator { private readonly int? _parentId; private readonly IMediaService _mediaService; @@ -69,6 +69,6 @@ namespace Umbraco.Examine pageIndex++; } while (media.Length == pageSize); } - + } } From 6bba032325437d6de9bad16919f911e7dcae7aa4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Dec 2019 16:09:39 +1100 Subject: [PATCH 151/173] re-merges --- .../NuCache/PublishedSnapshotService.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index b40470e131..a866297d72 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -128,9 +128,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the databases or it should populate them from sql - _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localDbExists); + _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); - _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localDbExists); + _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); } else @@ -171,14 +171,15 @@ namespace Umbraco.Web.PublishedCache.NuCache var path = GetLocalFilesPath(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); - var localContentDbExists = File.Exists(localContentDbPath); - var localMediaDbExists = File.Exists(localMediaDbPath); - _localDbExists = localContentDbExists && localMediaDbExists; - // if both local databases exist then GetTree will open them, else new databases will be created - _localContentDb = BTree.GetTree(localContentDbPath, _localDbExists); - _localMediaDb = BTree.GetTree(localMediaDbPath, _localDbExists); + + _localContentDbExists = File.Exists(localContentDbPath); + _localMediaDbExists = File.Exists(localMediaDbPath); - _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", localContentDbExists, localMediaDbExists); + // if both local databases exist then GetTree will open them, else new databases will be created + _localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists); + _localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists); + + _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists); } /// @@ -211,11 +212,15 @@ namespace Umbraco.Web.PublishedCache.NuCache try { - if (_localDbExists) + if (_localContentDbExists) { okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true)); if (!okContent) - _logger.Warn("Loading content from local db raised warnings, will reload from database."); + _logger.Warn("Loading content from local db raised warnings, will reload from database."); + } + + if (_localMediaDbExists) + { okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true)); if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); From 1f481315bc990a2980f9bacebc300829c6cced15 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Dec 2019 17:18:15 +1100 Subject: [PATCH 152/173] Allow for switching out MainDom which will need to be done in the UmbracoApplication --- src/Umbraco.Core/IMainDom.cs | 5 +- src/Umbraco.Core/MainDom.cs | 96 ++++++++++++------------- src/Umbraco.Core/Runtime/CoreRuntime.cs | 43 ++++++++--- src/Umbraco.Web/Runtime/WebRuntime.cs | 13 +++- src/Umbraco.Web/UmbracoApplication.cs | 4 +- 5 files changed, 96 insertions(+), 65 deletions(-) diff --git a/src/Umbraco.Core/IMainDom.cs b/src/Umbraco.Core/IMainDom.cs index 3a8cd13ff1..31b2e2eee0 100644 --- a/src/Umbraco.Core/IMainDom.cs +++ b/src/Umbraco.Core/IMainDom.cs @@ -14,6 +14,9 @@ namespace Umbraco.Core /// /// Gets a value indicating whether the current domain is the main domain. /// + /// + /// When the first call is made to this there will generally be some logic executed to acquire a distributed lock lease. + /// bool IsMainDom { get; } /// @@ -35,4 +38,4 @@ namespace Umbraco.Core /// is guaranteed to execute before the AppDomain releases the main domain status. bool Register(Action install, Action release, int weight = 100); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 7adea7dc1a..892c020803 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core private readonly ILogger _logger; // our own lock for local consistency - private readonly object _locko = new object(); + private object _locko = new object(); // async lock representing the main domain lock private readonly SystemLock _systemLock; @@ -32,8 +32,9 @@ namespace Umbraco.Core // release the lock because a new domain wants to be the main domain private readonly EventWaitHandle _signal; + private bool _isInitialized; // indicates whether... - private volatile bool _isMainDom; // we are the main domain + private bool _isMainDom; // we are the main domain private volatile bool _signaled; // we have been signaled // actions to run before releasing the main domain @@ -156,62 +157,57 @@ namespace Umbraco.Core } // acquires the main domain - internal bool Acquire() + private bool Acquire() { - lock (_locko) // we don't want the hosting environment to interfere by signaling + // if signaled, too late to acquire, give up + // the handler is not installed so that would be the hosting environment + if (_signaled) { - // if signaled, too late to acquire, give up - // the handler is not installed so that would be the hosting environment - if (_signaled) - { - _logger.Info("Cannot acquire (signaled)."); - return false; - } - - _logger.Info("Acquiring."); - - // signal other instances that we want the lock, then wait one the lock, - // which may timeout, and this is accepted - see comments below - - // signal, then wait for the lock, then make sure the event is - // reset (maybe there was noone listening..) - _signal.Set(); - - // if more than 1 instance reach that point, one will get the lock - // and the other one will timeout, which is accepted - - //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. - try - { - _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); - } - finally - { - // we need to reset the event, because otherwise we would end up - // signaling ourselves and committing suicide immediately. - // only 1 instance can reach that point, but other instances may - // have started and be trying to get the lock - they will timeout, - // which is accepted - - _signal.Reset(); - } - _isMainDom = true; - - - //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread - - _signal.WaitOneAsync() - .ContinueWith(_ => OnSignal("signal")); - - _logger.Info("Acquired."); - return true; + _logger.Info("Cannot acquire (signaled)."); + return false; } + + _logger.Info("Acquiring."); + + // signal other instances that we want the lock, then wait one the lock, + // which may timeout, and this is accepted - see comments below + + // signal, then wait for the lock, then make sure the event is + // reset (maybe there was noone listening..) + _signal.Set(); + + // if more than 1 instance reach that point, one will get the lock + // and the other one will timeout, which is accepted + + //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. + try + { + _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); + } + finally + { + // we need to reset the event, because otherwise we would end up + // signaling ourselves and committing suicide immediately. + // only 1 instance can reach that point, but other instances may + // have started and be trying to get the lock - they will timeout, + // which is accepted + + _signal.Reset(); + } + + //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread + + _signal.WaitOneAsync() + .ContinueWith(_ => OnSignal("signal")); + + _logger.Info("Acquired."); + return true; } /// /// Gets a value indicating whether the current domain is the main domain. /// - public bool IsMainDom => _isMainDom; + public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire()); // IRegisteredObject void IRegisteredObject.Stop(bool immediate) diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 5839ba6cfd..50653edc7c 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -27,6 +27,17 @@ namespace Umbraco.Core.Runtime private IFactory _factory; private RuntimeState _state; + [Obsolete("Use the ctor with all parameters instead")] + public CoreRuntime() + { + } + + public CoreRuntime(ILogger logger, IMainDom mainDom) + { + MainDom = mainDom; + Logger = logger; + } + /// /// Gets the logger. /// @@ -45,14 +56,22 @@ namespace Umbraco.Core.Runtime /// public IRuntimeState State => _state; + public IMainDom MainDom { get; private set; } + /// public virtual IFactory Boot(IRegister register) { // create and register the essential services // ie the bare minimum required to boot +#pragma warning disable CS0618 // Type or member is obsolete // loggers - var logger = Logger = GetLogger(); + // TODO: Removes this in netcore, this is purely just backwards compat ugliness + var logger = GetLogger(); + if (logger != Logger) + Logger = logger; +#pragma warning restore CS0618 // Type or member is obsolete + var profiler = Profiler = GetProfiler(); var profilingLogger = ProfilingLogger = new ProfilingLogger(logger, profiler); @@ -125,12 +144,16 @@ namespace Umbraco.Core.Runtime Level = RuntimeLevel.Boot }; - // main dom - var mainDom = new MainDom(Logger); + // TODO: remove this in netcore, this is purely backwards compat hacks with the empty ctor + if (MainDom == null) + { + MainDom = new MainDom(Logger); + } + // create the composition composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs); - composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state); + composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, appCaches, databaseFactory, typeLoader, _state); // run handlers RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory); @@ -140,7 +163,7 @@ namespace Umbraco.Core.Runtime Compose(composition); // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - AcquireMainDom(mainDom); + AcquireMainDom(MainDom); // determine our runtime level DetermineRuntimeLevel(databaseFactory, ProfilingLogger); @@ -220,13 +243,13 @@ namespace Umbraco.Core.Runtime IOHelper.SetRootDirectory(path); } - private bool AcquireMainDom(MainDom mainDom) + private bool AcquireMainDom(IMainDom mainDom) { using (var timer = ProfilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) { try { - return mainDom.Acquire(); + return mainDom.IsMainDom; } catch { @@ -303,11 +326,9 @@ namespace Umbraco.Core.Runtime protected virtual IEnumerable GetComposerTypes(TypeLoader typeLoader) => typeLoader.GetTypes(); - /// - /// Gets a logger. - /// + [Obsolete("Don't use this method, the logger should be injected into the " + nameof(CoreRuntime))] protected virtual ILogger GetLogger() - => SerilogLogger.CreateWithDefaultConfiguration(); + => Logger ?? SerilogLogger.CreateWithDefaultConfiguration(); // TODO: Remove this in netcore, this purely just backwards compat ugliness /// /// Gets a profiler. diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 13cd717fd1..ffcd2343ed 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -1,4 +1,6 @@ -using System.Web; +using System; +using System.Web; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; @@ -17,11 +19,18 @@ namespace Umbraco.Web.Runtime private readonly UmbracoApplicationBase _umbracoApplication; private IProfiler _webProfiler; + [Obsolete("Use the ctor with all parameters instead")] + public WebRuntime(UmbracoApplicationBase umbracoApplication) + : this(umbracoApplication, null, null) + { + } + /// /// Initializes a new instance of the class. /// /// - public WebRuntime(UmbracoApplicationBase umbracoApplication) + public WebRuntime(UmbracoApplicationBase umbracoApplication, ILogger logger, IMainDom mainDom) + : base(logger, mainDom) { _umbracoApplication = umbracoApplication; } diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 191fb9dcd6..94403bc1be 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Web; using Umbraco.Core; +using Umbraco.Core.Logging.Serilog; using Umbraco.Web.Runtime; namespace Umbraco.Web @@ -12,7 +13,8 @@ namespace Umbraco.Web { protected override IRuntime GetRuntime() { - return new WebRuntime(this); + var logger = SerilogLogger.CreateWithDefaultConfiguration(); + return new WebRuntime(this, logger, new MainDom(logger)); } /// From 0c3d3b229ea3db907812755b3fc937c8396a69a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 4 Dec 2019 14:42:06 +0100 Subject: [PATCH 153/173] Checking nodeType on activeNode to ensure that nodes originate from the same treeAlias --- .../directives/components/tree/umbtreeitem.directive.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js index 975b10d678..0a6eeb8835 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js @@ -90,7 +90,9 @@ angular.module("umbraco.directives") css.push("umb-tree-item--deleted"); } - if (actionNode) { + // checking the nodeType to ensure that this node and actionNode is from the same treeAlias + if (actionNode && actionNode.nodeType === node.nodeType) { + if (actionNode.id === node.id && String(node.id) !== "-1") { css.push("active"); } From 15f75ce756019d21f26d73547fe760e730452b22 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Dec 2019 18:07:43 +1100 Subject: [PATCH 154/173] FIxes test and fixes boolean issue with bg task runner shutdown (issue exists in v7 too) --- .../Scheduling/BackgroundTaskRunnerTests.cs | 43 +++++++----------- .../Scheduling/BackgroundTaskRunner.cs | 44 +++++++++++-------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 3664717af7..4d264c830e 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -183,25 +183,19 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(false); // -immediate = -force, -wait + await runner.StopInternal(false); // -immediate = -force, -wait (max 2000 ms delay before +immediate) + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event - Assert.IsFalse(terminated); // but not terminated yet + Assert.IsTrue(terminated); // and that event - // all this before we await because -wait Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner - Assert.IsTrue(runner.IsRunning); // still running the task - - await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.IsFalse(runner.IsRunning); - Assert.IsTrue(stopped); - - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.IsTrue(terminated); // has raised that event + Assert.IsFalse(runner.IsRunning); // done running Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run } @@ -222,23 +216,20 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(true); // +immediate = +force, +wait + await runner.StopInternal(true); // +immediate = +force, +wait (no delay) + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event Assert.IsTrue(terminated); // and that event - Assert.IsTrue(stopped); // and that one - - // and all this before we await because +wait + Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner Assert.IsFalse(runner.IsRunning); // done running - await runner.StoppedAwaitable; // runner stops, within test's timeout - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run } } @@ -564,7 +555,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(1000); Assert.IsTrue(runner.IsRunning); // still waiting for the task to release Assert.IsFalse(task.HasRun); - task.Release(); + task.Release(); // unlatch var runnerTask = runner.CurrentThreadingTask; // may be null if things go fast enough if (runnerTask != null) await runnerTask; // wait for current task to complete @@ -574,7 +565,7 @@ namespace Umbraco.Tests.Scheduling } [Test] - public async Task LatchedTaskStops() + public async Task LatchedTaskStops_Runs_On_Shutdown() { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 4158594109..ebb9162f29 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -338,7 +338,7 @@ namespace Umbraco.Web.Scheduling if (_isRunning == false) return; // done already } - var hasTasks = _tasks.Count > 0; + var hasTasks = TaskCount > 0; if (!force && hasTasks) _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); @@ -432,7 +432,7 @@ namespace Umbraco.Web.Scheduling lock (_locker) { // deal with race condition - if (_shutdownToken.IsCancellationRequested == false && _tasks.Count > 0) continue; + if (_shutdownToken.IsCancellationRequested == false && TaskCount > 0) continue; // if we really have nothing to do, stop _logger.Debug("{LogPrefix} Stopping", _logPrefix); @@ -457,7 +457,7 @@ namespace Umbraco.Web.Scheduling // if KeepAlive is false then don't block, exit if there is // no task in the buffer - yes, there is a race condition, which // we'll take care of - if (_options.KeepAlive == false && _tasks.Count == 0) + if (_options.KeepAlive == false && TaskCount == 0) return null; try @@ -514,8 +514,12 @@ namespace Umbraco.Web.Scheduling if (task == latched.Latch) return bgTask; + // we are shutting down if the _tasks.Complete(); was called or the shutdown token was cancelled + var isShuttingDown = _shutdownToken.IsCancellationRequested || task == _tasks.Completion; + // if shutting down, return the task only if it runs on shutdown - if (_shutdownToken.IsCancellationRequested == false && latched.RunsOnShutdown) return bgTask; + if (isShuttingDown && latched.RunsOnShutdown) + return bgTask; // else, either it does not run on shutdown or it's been cancelled, dispose latched.Dispose(); @@ -668,17 +672,7 @@ namespace Umbraco.Web.Scheduling #endregion - /// - /// Requests a registered object to un-register. - /// - /// true to indicate the registered object should un-register from the hosting - /// environment before returning; otherwise, false. - /// - /// "When the application manager needs to stop a registered object, it will call the Stop method." - /// The application manager will call the Stop method to ask a registered object to un-register. During - /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. - /// - public void Stop(bool immediate) + internal Task StopInternal(bool immediate) { // the first time the hosting environment requests that the runner terminates, // raise the Terminating event - that could be used to prevent any process that @@ -701,18 +695,30 @@ namespace Umbraco.Web.Scheduling // with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop. if (!immediate) { - Task.Run(StopInitial, CancellationToken.None); + return Task.Run(StopInitial, CancellationToken.None); } else { - lock(_locker) + lock (_locker) { - if (_terminated) return; - Task.Run(StopImmediate, CancellationToken.None); + if (_terminated) return Task.FromResult(0); + return Task.Run(StopImmediate, CancellationToken.None); } } } + /// + /// Requests a registered object to un-register. + /// + /// true to indicate the registered object should un-register from the hosting + /// environment before returning; otherwise, false. + /// + /// "When the application manager needs to stop a registered object, it will call the Stop method." + /// The application manager will call the Stop method to ask a registered object to un-register. During + /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. + /// + public void Stop(bool immediate) => StopInternal(immediate); + /// /// Called when immediate == false for IRegisteredObject.Stop(bool immediate) /// From d8c1c8fd13ddcb815c9ce721223640033cdc5cc3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Dec 2019 19:18:15 +1100 Subject: [PATCH 155/173] fixes test --- src/Umbraco.Core/MainDom.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 892c020803..e2049c0190 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -53,11 +53,9 @@ namespace Umbraco.Core _logger = logger; - var appId = string.Empty; // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail - if (HostingEnvironment.ApplicationID != null) - appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty); - + var appId = HostingEnvironment.ApplicationID?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty; + // combining with the physical path because if running on eg IIS Express, // two sites could have the same appId even though they are different. // @@ -67,7 +65,7 @@ namespace Umbraco.Core // we *cannot* use the process ID here because when an AppPool restarts it is // a new process for the same application path - var appPath = HostingEnvironment.ApplicationPhysicalPath.ToLowerInvariant(); + var appPath = HostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty; var hash = (appId + ":::" + appPath).GenerateHash(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; From 6f9062c0cfcb1292fe40b594dc43b4644f622e83 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Dec 2019 19:19:09 +1100 Subject: [PATCH 156/173] fixes test (cherry picked) --- src/Umbraco.Core/MainDom.cs | 8 ++-- .../Scheduling/BackgroundTaskRunnerTests.cs | 43 +++++++----------- .../Scheduling/BackgroundTaskRunner.cs | 44 +++++++++++-------- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 7adea7dc1a..a21afd6ffb 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -52,11 +52,9 @@ namespace Umbraco.Core _logger = logger; - var appId = string.Empty; // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail - if (HostingEnvironment.ApplicationID != null) - appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty); - + var appId = HostingEnvironment.ApplicationID?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty; + // combining with the physical path because if running on eg IIS Express, // two sites could have the same appId even though they are different. // @@ -66,7 +64,7 @@ namespace Umbraco.Core // we *cannot* use the process ID here because when an AppPool restarts it is // a new process for the same application path - var appPath = HostingEnvironment.ApplicationPhysicalPath.ToLowerInvariant(); + var appPath = HostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty; var hash = (appId + ":::" + appPath).GenerateHash(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 3664717af7..4d264c830e 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -183,25 +183,19 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(false); // -immediate = -force, -wait + await runner.StopInternal(false); // -immediate = -force, -wait (max 2000 ms delay before +immediate) + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event - Assert.IsFalse(terminated); // but not terminated yet + Assert.IsTrue(terminated); // and that event - // all this before we await because -wait Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner - Assert.IsTrue(runner.IsRunning); // still running the task - - await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.IsFalse(runner.IsRunning); - Assert.IsTrue(stopped); - - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.IsTrue(terminated); // has raised that event + Assert.IsFalse(runner.IsRunning); // done running Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run } @@ -222,23 +216,20 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(true); // +immediate = +force, +wait + await runner.StopInternal(true); // +immediate = +force, +wait (no delay) + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event Assert.IsTrue(terminated); // and that event - Assert.IsTrue(stopped); // and that one - - // and all this before we await because +wait + Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner Assert.IsFalse(runner.IsRunning); // done running - await runner.StoppedAwaitable; // runner stops, within test's timeout - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run } } @@ -564,7 +555,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(1000); Assert.IsTrue(runner.IsRunning); // still waiting for the task to release Assert.IsFalse(task.HasRun); - task.Release(); + task.Release(); // unlatch var runnerTask = runner.CurrentThreadingTask; // may be null if things go fast enough if (runnerTask != null) await runnerTask; // wait for current task to complete @@ -574,7 +565,7 @@ namespace Umbraco.Tests.Scheduling } [Test] - public async Task LatchedTaskStops() + public async Task LatchedTaskStops_Runs_On_Shutdown() { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 4158594109..ebb9162f29 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -338,7 +338,7 @@ namespace Umbraco.Web.Scheduling if (_isRunning == false) return; // done already } - var hasTasks = _tasks.Count > 0; + var hasTasks = TaskCount > 0; if (!force && hasTasks) _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); @@ -432,7 +432,7 @@ namespace Umbraco.Web.Scheduling lock (_locker) { // deal with race condition - if (_shutdownToken.IsCancellationRequested == false && _tasks.Count > 0) continue; + if (_shutdownToken.IsCancellationRequested == false && TaskCount > 0) continue; // if we really have nothing to do, stop _logger.Debug("{LogPrefix} Stopping", _logPrefix); @@ -457,7 +457,7 @@ namespace Umbraco.Web.Scheduling // if KeepAlive is false then don't block, exit if there is // no task in the buffer - yes, there is a race condition, which // we'll take care of - if (_options.KeepAlive == false && _tasks.Count == 0) + if (_options.KeepAlive == false && TaskCount == 0) return null; try @@ -514,8 +514,12 @@ namespace Umbraco.Web.Scheduling if (task == latched.Latch) return bgTask; + // we are shutting down if the _tasks.Complete(); was called or the shutdown token was cancelled + var isShuttingDown = _shutdownToken.IsCancellationRequested || task == _tasks.Completion; + // if shutting down, return the task only if it runs on shutdown - if (_shutdownToken.IsCancellationRequested == false && latched.RunsOnShutdown) return bgTask; + if (isShuttingDown && latched.RunsOnShutdown) + return bgTask; // else, either it does not run on shutdown or it's been cancelled, dispose latched.Dispose(); @@ -668,17 +672,7 @@ namespace Umbraco.Web.Scheduling #endregion - /// - /// Requests a registered object to un-register. - /// - /// true to indicate the registered object should un-register from the hosting - /// environment before returning; otherwise, false. - /// - /// "When the application manager needs to stop a registered object, it will call the Stop method." - /// The application manager will call the Stop method to ask a registered object to un-register. During - /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. - /// - public void Stop(bool immediate) + internal Task StopInternal(bool immediate) { // the first time the hosting environment requests that the runner terminates, // raise the Terminating event - that could be used to prevent any process that @@ -701,18 +695,30 @@ namespace Umbraco.Web.Scheduling // with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop. if (!immediate) { - Task.Run(StopInitial, CancellationToken.None); + return Task.Run(StopInitial, CancellationToken.None); } else { - lock(_locker) + lock (_locker) { - if (_terminated) return; - Task.Run(StopImmediate, CancellationToken.None); + if (_terminated) return Task.FromResult(0); + return Task.Run(StopImmediate, CancellationToken.None); } } } + /// + /// Requests a registered object to un-register. + /// + /// true to indicate the registered object should un-register from the hosting + /// environment before returning; otherwise, false. + /// + /// "When the application manager needs to stop a registered object, it will call the Stop method." + /// The application manager will call the Stop method to ask a registered object to un-register. During + /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. + /// + public void Stop(bool immediate) => StopInternal(immediate); + /// /// Called when immediate == false for IRegisteredObject.Stop(bool immediate) /// From 3f78331145fee09e763e409b42e307ee542cab6e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Dec 2019 23:26:49 +1100 Subject: [PATCH 157/173] Adds lots of notes, fixes tests to ensure we catch OperationCanceledException where necessary, adds logging to tests, fixes a boolean op --- .../Scheduling/BackgroundTaskRunnerTests.cs | 63 ++++++++++++------- .../Scheduling/BackgroundTaskRunner.cs | 33 +++++++--- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 4d264c830e..ea42cd2ea9 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Scheduling; namespace Umbraco.Tests.Scheduling @@ -21,7 +22,7 @@ namespace Umbraco.Tests.Scheduling [OneTimeSetUp] public void InitializeFixture() { - _logger = new DebugDiagnosticsLogger(); + _logger = new ConsoleLogger(); } [Test] @@ -102,12 +103,12 @@ namespace Umbraco.Tests.Scheduling { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { - MyTask t; + MyTask t1, t2, t3; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(t1 = new MyTask(5000)); + runner.Add(t2 = new MyTask()); + runner.Add(t3 = new MyTask()); Assert.IsTrue(runner.IsRunning); // is running tasks // shutdown -force => run all queued tasks @@ -115,7 +116,7 @@ namespace Umbraco.Tests.Scheduling Assert.IsTrue(runner.IsRunning); // is running tasks await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run + Assert.AreNotEqual(DateTime.MinValue, t3.Ended); // t3 has run } } @@ -124,20 +125,25 @@ namespace Umbraco.Tests.Scheduling { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { - MyTask t; + MyTask t1, t2, t3; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(t1 = new MyTask(5000)); + runner.Add(t2 = new MyTask()); + runner.Add(t3 = new MyTask()); Assert.IsTrue(runner.IsRunning); // is running tasks + Thread.Sleep(1000); // since we are forcing shutdown, we need to give it a chance to start, else it will be canceled before the queue is started + // shutdown +force => tries to cancel the current task, ignores queued tasks runner.Shutdown(true, false); // +force -wait Assert.IsTrue(runner.IsRunning); // is running that long task it cannot cancel - await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run + await runner.StoppedAwaitable; // runner stops, within test's timeout (no cancelation token used, no need to catch OperationCanceledException) + + Assert.AreNotEqual(DateTime.MinValue, t1.Ended); // t1 *has* run + Assert.AreEqual(DateTime.MinValue, t2.Ended); // t2 has *not* run + Assert.AreEqual(DateTime.MinValue, t3.Ended); // t3 has *not* run } } @@ -163,7 +169,15 @@ namespace Umbraco.Tests.Scheduling // shutdown +force => tries to cancel the current task, ignores queued tasks runner.Shutdown(true, false); // +force -wait - await runner.StoppedAwaitable; // runner stops, within test's timeout + try + { + await runner.StoppedAwaitable; // runner stops, within test's timeout ... maybe + } + catch (OperationCanceledException) + { + // catch exception, this can occur because we are +force shutting down which will + // cancel a pending task if the queue hasn't completed in time + } } } @@ -188,7 +202,8 @@ namespace Umbraco.Tests.Scheduling runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - await runner.StopInternal(false); // -immediate = -force, -wait (max 2000 ms delay before +immediate) + runner.Stop(false); // -immediate = -force, -wait (max 2000 ms delay before +immediate) + await runner.TerminatedAwaitable; Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event @@ -221,7 +236,8 @@ namespace Umbraco.Tests.Scheduling runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - await runner.StopInternal(true); // +immediate = +force, +wait (no delay) + runner.Stop(true); // +immediate = +force, +wait (no delay) + await runner.TerminatedAwaitable; Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event @@ -255,8 +271,7 @@ namespace Umbraco.Tests.Scheduling }, _logger)) { Assert.IsTrue(runner.IsRunning); // because AutoStart is true - runner.Stop(false); // keepalive = must be stopped - await runner.StoppedAwaitable; // runner stops, within test's timeout + await runner.StopInternal(false); // keepalive = must be stopped } } @@ -282,13 +297,9 @@ namespace Umbraco.Tests.Scheduling // dispose will stop it } - await runner.StoppedAwaitable; // runner stops, within test's timeout - //await runner.TerminatedAwaitable; // NO! see note below + await runner.StoppedAwaitable; Assert.Throws(() => runner.Add(new MyTask())); - // but do NOT await on TerminatedAwaitable - disposing just shuts the runner down - // so that we don't have a runaway task in tests, etc - but it does NOT terminate - // the runner - it really is NOT a nice way to end a runner - it's there for tests } [Test] @@ -575,7 +586,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(5000); Assert.IsTrue(runner.IsRunning); // still waiting for the task to release Assert.IsFalse(task.HasRun); - runner.Shutdown(false, false); + runner.Shutdown(false, false); // -force, -wait await runner.StoppedAwaitable; // wait for the entire runner operation to complete Assert.IsTrue(task.HasRun); } @@ -871,7 +882,9 @@ namespace Umbraco.Tests.Scheduling public override void PerformRun() { + Console.WriteLine($"Sleeping {_milliseconds}..."); Thread.Sleep(_milliseconds); + Console.WriteLine("Wake up!"); } } @@ -988,7 +1001,9 @@ namespace Umbraco.Tests.Scheduling public DateTime Ended { get; set; } public virtual void Dispose() - { } + { + + } } } } diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index ebb9162f29..c0475b1f79 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -199,7 +199,7 @@ namespace Umbraco.Web.Scheduling { lock (_locker) { - var task = _runningTask ?? Task.FromResult(0); + var task = _runningTask ?? Task.CompletedTask; return new ThreadingTaskImmutable(task); } } @@ -211,8 +211,9 @@ namespace Umbraco.Web.Scheduling /// An awaitable object. /// /// Used to wait until the runner has terminated. - /// This is for unit tests and should not be used otherwise. In most cases when the runner - /// has terminated, the application domain is going down and it is not the right time to do things. + /// + /// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown. + /// /// internal ThreadingTaskImmutable TerminatedAwaitable { @@ -347,14 +348,18 @@ namespace Umbraco.Web.Scheduling // will stop waiting on the queue or on a latch _tasks.Complete(); - if (!hasTasks || force) + if (force) { // we must bring everything down, now lock (_locker) { // was Complete() enough? - if (_isRunning == false) return; + // if _tasks.Complete() ended up triggering code to stop the runner and reset + // the _isRunning flag, then there's no need to initiate a cancel on the cancelation token. + if (_isRunning == false) + return; } + // try to cancel running async tasks (cannot do much about sync tasks) // break latched tasks // stop processing the queue @@ -586,17 +591,18 @@ namespace Umbraco.Web.Scheduling // triggers when the hosting environment requests that the runner terminates internal event TypedEventHandler, EventArgs> Terminating; - // triggers when the runner has terminated (no task can be added, no task is running) + // triggers when the hosting environment has terminated (no task can be added, no task is running) internal event TypedEventHandler, EventArgs> Terminated; private void OnEvent(TypedEventHandler, EventArgs> handler, string name) { - if (handler == null) return; OnEvent(handler, name, EventArgs.Empty); } private void OnEvent(TypedEventHandler, TArgs> handler, string name, TArgs e) { + _logger.Debug("{LogPrefix} OnEvent {EventName}", _logPrefix, name); + if (handler == null) return; try @@ -672,6 +678,15 @@ namespace Umbraco.Web.Scheduling #endregion + #region IRegisteredObject.Stop + + /// + /// Used by IRegisteredObject.Stop and shutdown on threadpool threads to not block shutdown times. + /// + /// + /// + /// An awaitable Task that is used to handle the shutdown. + /// internal Task StopInternal(bool immediate) { // the first time the hosting environment requests that the runner terminates, @@ -701,7 +716,7 @@ namespace Umbraco.Web.Scheduling { lock (_locker) { - if (_terminated) return Task.FromResult(0); + if (_terminated) return Task.CompletedTask; return Task.Run(StopImmediate, CancellationToken.None); } } @@ -810,5 +825,7 @@ namespace Umbraco.Web.Scheduling terminatedSource.TrySetResult(0); } + + #endregion } } From 2948d506eacfc30ba83757b81fa82a831e7a9773 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 5 Dec 2019 12:38:57 +0000 Subject: [PATCH 158/173] V8: Bye hardcoded colors lets use Variables (#7246) * Bye bye #fff let's use the variable @white instead * Update to use @black * Update canvas-designer file with variables where possible - ALOT was not found (need to see if we have similar matches already) * Update all hex colour variables or add a TODO comment * Empty LESS files so lets get rid of them * Moves the color CSS classes out into own less file to ensure variables contains variables only * Adds in gulp-sourcemaps to allow us to have LESS sourcemaps - https://www.npmjs.com/package/gulp-less#source-maps * A VERY BAD first attempt at doing DARK mode, but plumbing done. Now a game of tweaking variables * Revert "A VERY BAD first attempt at doing DARK mode, but plumbing done. Now a game of tweaking variables" This reverts commit 5ac8080c8b314cd65b1bd735fea88569991736b2. * Update hard-coded hex values to color variables * Fix couple typos with LESS variables gray not grey * Amend build script so we remove the /umbraco/assets/css/maps folder from the build ouput of the ZIP & Nuget Packages * Removing more hard coded colour values --- build/build.ps1 | 8 +- .../gulp/util/processLess.js | 15 +- src/Umbraco.Web.UI.Client/package-lock.json | 130 +++++++++++++ src/Umbraco.Web.UI.Client/package.json | 1 + src/Umbraco.Web.UI.Client/src/less/belle.less | 5 +- .../src/less/canvas-designer.less | 177 +++++++++--------- .../src/less/colors.less | 81 ++++++++ .../less/components/buttons/umb-toggle.less | 6 +- .../src/less/components/card.less | 6 +- .../subheader/umb-editor-sub-header.less | 12 +- .../components/prevalues/multivalues.less | 2 +- .../less/components/tree/umb-tree-item.less | 18 +- .../src/less/components/umb-avatar.less | 4 +- .../src/less/components/umb-form-check.less | 2 +- .../src/less/components/umb-grid.less | 12 +- .../src/less/components/umb-iconpicker.less | 2 +- .../less/components/umb-insert-code-box.less | 4 +- .../less/components/umb-layout-selector.less | 4 +- .../components/umb-list-view-settings.less | 2 +- .../src/less/components/umb-logviewer.less | 6 +- .../src/less/components/umb-media-grid.less | 8 +- .../less/components/umb-nested-content.less | 22 +-- .../less/components/umb-property-actions.less | 14 +- .../src/less/components/umb-range-slider.less | 2 +- .../components/users/umb-user-details.less | 4 +- .../src/less/dashboards/getstarted.less | 4 +- .../src/less/dashboards/nucache.less | 2 +- .../src/less/dashboards/umbraco-forms.less | 2 +- .../src/less/footer.less | 2 - .../src/less/gridview.less | 10 +- .../src/less/installer.less | 5 +- .../src/less/legacydialog.less | 8 +- .../src/less/mixins.less | 28 +-- .../src/less/modals.less | 2 +- .../src/less/pages/document-type-editor.less | 0 .../src/less/pages/nonodes.less | 1 + .../src/less/property-editors.less | 24 +-- .../src/less/rte-content.less | 1 + src/Umbraco.Web.UI.Client/src/less/rte.less | 6 +- .../src/less/variables.less | 105 +---------- 40 files changed, 441 insertions(+), 306 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/colors.less delete mode 100644 src/Umbraco.Web.UI.Client/src/less/footer.less delete mode 100644 src/Umbraco.Web.UI.Client/src/less/pages/document-type-editor.less diff --git a/build/build.ps1 b/build/build.ps1 index 892654d3cd..b40be8e12f 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -313,6 +313,10 @@ $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\js", "*", "$tmp\WebApp\umbraco\js") $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\lib", "*", "$tmp\WebApp\umbraco\lib") $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\views", "*", "$tmp\WebApp\umbraco\views") + + # copied too much - delete the LESS map directory + # that was copied over into "$tmp\WebApp\umbraco\assets\css\maps" + Remove-Item "$tmp\WebApp\umbraco\assets\css\maps" -Force -Recurse -ErrorAction SilentlyContinue }) $ubuild.DefineMethod("PackageZip", @@ -456,10 +460,10 @@ $ubuild.DefineMethod("PrepareAngularDocs", { Write-Host "Prepare Angular Documentation" - + $src = "$($this.SolutionRoot)\src" $out = $this.BuildOutput - + "Moving to Umbraco.Web.UI.Docs folder" cd ..\src\Umbraco.Web.UI.Docs diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js index 94150043c1..1566c7ac79 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js @@ -6,25 +6,28 @@ var postcss = require('gulp-postcss'); var less = require('gulp-less'); var autoprefixer = require('autoprefixer'); var cssnano = require('cssnano'); -var cleanCss = require("gulp-clean-css"); +var cleanCss = require('gulp-clean-css'); var rename = require('gulp-rename'); +var sourcemaps = require('gulp-sourcemaps'); module.exports = function(files, out) { - + var processors = [ autoprefixer, cssnano({zindex: false}) ]; - + console.log("LESS: ", files, " -> ", config.root + config.targets.css + out) - + var task = gulp.src(files) + .pipe(sourcemaps.init()) .pipe(less()) .pipe(cleanCss()) .pipe(postcss(processors)) .pipe(rename(out)) + .pipe(sourcemaps.write('./maps')) .pipe(gulp.dest(config.root + config.targets.css)); - + return task; - + }; diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index a2f55b76b1..d69ef62f16 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -862,6 +862,43 @@ } } }, + "@gulp-sourcemaps/identity-map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", + "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "dev": true, + "requires": { + "acorn": "^5.0.3", + "css": "^2.2.1", + "normalize-path": "^2.1.1", + "source-map": "^0.6.0", + "through2": "^2.0.3" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -3148,6 +3185,26 @@ "which": "^1.2.9" } }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -3370,6 +3427,34 @@ "ms": "^2.1.1" } }, + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "requires": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -3637,6 +3722,12 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -6660,6 +6751,39 @@ "through2": "^2.0.1" } }, + "gulp-sourcemaps": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "1.X", + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "5.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "1.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom-string": "1.X", + "through2": "2.X" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "gulp-util": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", @@ -14832,6 +14956,12 @@ "strip-bom": "^2.0.0" } }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "dev": true + }, "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 44ecb4026f..2b92302684 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -66,6 +66,7 @@ "gulp-postcss": "8.0.0", "gulp-rename": "1.4.0", "gulp-sort": "2.0.0", + "gulp-sourcemaps": "^2.6.5", "gulp-watch": "5.0.1", "gulp-wrap": "0.15.0", "gulp-wrap-js": "0.4.1", diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 391fafb3fa..f6490fc79b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -2,6 +2,7 @@ // Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "colors.less"; // Colors from variables but as specific CSS classes @import "mixins.less"; // CSS Reset @@ -74,9 +75,8 @@ @import "sections.less"; @import "helveticons.less"; @import "main.less"; -@import "listview.less"; +@import "listview.less"; @import "gridview.less"; -@import "footer.less"; @import "filter-toggle.less"; @import "forms/umb-validation-label.less"; @@ -206,7 +206,6 @@ @import "utilities/_cursor.less"; //page specific styles -@import "pages/document-type-editor.less"; @import "pages/login.less"; @import "pages/welcome-dashboard.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index b36c73a61a..ddb45517b6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -1,5 +1,6 @@ @import "helveticons.less"; +@import "variables.less"; /******* font-face *******/ @@ -23,7 +24,7 @@ body { font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; - color: #343434; + color: @grayDark; transition: all 0.2s ease-in-out; padding-left:80px; } @@ -31,8 +32,8 @@ body { h4 { margin: 0; font-size: 16px; - background-color: #535353; - color: #b3b3b3; + background-color: @gray-3; + color: @gray-7; text-transform: uppercase; position: initial; display: block; @@ -53,7 +54,7 @@ ul { } a, a:hover{ - color:#333; + color: @grayDark; text-decoration:none; } @@ -80,7 +81,7 @@ a, a:hover{ display: block; height: 100%; width: 100%; - background:#fff center center url(../img/loader.gif) no-repeat; + background:@white center center url(../img/loader.gif) no-repeat; } /****************************/ @@ -98,12 +99,12 @@ a, a:hover{ margin-bottom: 0; font-size: 14px; line-height: 20px; - color: #000000; + color: @black; text-align: center; vertical-align: middle; cursor: pointer; - background: #f2f2f2; - border: 1px solid #cccccc; + background: @gray-10; + border: 1px solid @gray-8; border-radius: 2px; box-shadow: none; } @@ -132,12 +133,12 @@ a, a:hover{ width: 0; height: 0; vertical-align: top; - border-top: 4px solid #FFFFFF; + border-top: 4px solid @white; border-right: 4px solid transparent; border-left: 4px solid transparent; content: ""; border-top: 0; - border-bottom: 4px solid #ffffff; + border-bottom: 4px solid @white; margin-top: 8px; margin-left: 0; } @@ -155,8 +156,8 @@ a, a:hover{ margin: -96px 10px 0 0; margin-bottom: 1px; list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; + background-color: @white; + border: 1px solid @gray-8; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); @@ -170,19 +171,19 @@ a, a:hover{ clear: both; font-weight: normal; line-height: 20px; - color: black; + color:@black; white-space: nowrap; cursor:pointer; } -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-menu > li > button:hover, -.dropdown-menu > li > button:focus, -.dropdown-submenu:hover > a, +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-menu > li > button:hover, +.dropdown-menu > li > button:focus, +.dropdown-submenu:hover > a, .dropdown-submenu:focus > a { - color: #000000; - background: #e4e0dd; + color: @black; + background: @brownLight; } /****************************/ @@ -211,9 +212,9 @@ a, a:hover{ margin: auto; margin-top: 5px; font-size: 12px; - color: #ffffff; + color: @white; text-shadow: none; - background-color: #46a546; + background-color: @greenIcon; border: none; border-color: transparent; border-radius: 5px 0 0 5px; @@ -229,7 +230,7 @@ a, a:hover{ width: 5px !important; height: 5px !important; margin: 5px 1px 7px 0; - background: #d9d9d9; + background: @grayLight; border-radius: 20px; } @@ -244,7 +245,7 @@ a, a:hover{ font-family: "Lato", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 16px; - background: #1b264f; + background: @blueExtraDark; transition: all 0.2s ease-in-out; z-index: 9999; } @@ -252,7 +253,7 @@ a, a:hover{ .avatar { text-align:center; padding: 27px 0 29px 0; - border-bottom: 1px solid #2E2246; + border-bottom: 1px solid @purple-d1; } .help { @@ -264,14 +265,14 @@ a, a:hover{ margin: 0; font-size: 30px; text-align: center; - color: #D8D7D9; + color: @gray-8; opacity: 0.4; transition: all .3s linear; } ul.sections { display: block; - background: #1b264f; + background: @blueExtraDark; position:absolute; top: 90px; width: 80px; @@ -286,20 +287,20 @@ ul.sections { &::-webkit-scrollbar { width: 0px; - background: transparent; + background: transparent; } } ul.sections li { display: block; - border-left: 4px #1b264f solid; + border-left: 4px @blueExtraDark solid; transition: all .3s linear; cursor: pointer; } .fix-left-menu ul.sections li a span, .fix-left-menu ul.sections li a i { - color: #fff; + color: @white; opacity: .7; transition: all .3s linear; } @@ -312,11 +313,11 @@ ul.sections li a { margin: 0 0 0 -4px; text-align: center; text-decoration: none; - border-bottom: 1px solid #2E2246; + border-bottom: 1px solid @purple-d1; &:hover { span, i { opacity: 1; - color:#fff; + color:@white; } } } @@ -334,15 +335,15 @@ ul.sections li a span { } ul.sections li.current { - border-left: 4px #f5c1bc solid; + border-left: 4px @pinkLight solid; } ul.sections li.current a i { - color: #f5c1bc; + color: @pinkLight; } ul.sections li.current { - border-left: 4px #f5c1bc solid; + border-left: 4px @pinkLight solid; } ul.sections li:hover a i, @@ -371,7 +372,7 @@ ul.sections li:hover a span { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 16px; - background: #ffffff; + background: @white; transition: all 0.2s ease-in-out; width: 250px; height: 100%; @@ -383,8 +384,8 @@ ul.sections li:hover a span { .main-panel .header { padding: 28px 20px 32px 20px; font-weight: bold; - background: #f8f8f8; - border-bottom: 1px solid #d9d9d9; + background: @grayLighter; + border-bottom: 1px solid @grayLight; } .main-panel .header h3 { @@ -401,7 +402,7 @@ ul.sections li:hover a span { } .main-panel .header h3 i:hover { - color:#333; + color: @grayDark; } .main-panel.selected { @@ -433,7 +434,7 @@ ul.sections li:hover a span { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 16px; - background: #ffffff; + background: @white; box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); transition: all 0.2s ease-in-out; } @@ -451,7 +452,7 @@ ul.sections li:hover a span { display: block; padding: 5px 10px; margin: 0; - border: 0px solid #6B6B6B; + border: 0px solid @gray-4; font-weight: normal; font-size: 12px; } @@ -462,7 +463,7 @@ ul.sections li:hover a span { } .samples > li:hover, .samples > li.hover { - background: #f8f8f8; + background: @grayLighter; } .samples > li ul { @@ -482,13 +483,13 @@ ul.sections li:hover a span { h4.panel-title { cursor:pointer; - background-color: #f8f8f8; - color: #767676; + background-color: @grayLighter; + color: @grayMed; } .editor-category{ margin: 5px 10px; - border: 1px solid #D9D9D9; + border: 1px solid @grayLight; } .canvasdesigner-panel-container { @@ -511,7 +512,7 @@ h4.panel-title { float: left; margin-right: 10px; font-size: 12px; - color: #d9d9d9; + color: @grayLight; } .div-field { @@ -531,9 +532,9 @@ h4.panel-title { margin-top: 4px; clear: both; font-size: 18px; - color: #CDCDCD; + color: @gray-8; cursor: pointer; - border: 1px solid #CDCDCD; + border: 1px solid @gray-8; text-align: center; position: relative; } @@ -547,18 +548,18 @@ h4.panel-title { position: absolute; margin: 5px 0 0 -15px; cursor: pointer; - color:#CDCDCD; + color: @gray-8; right: 0; top: 0; } .fontFamilyPickerPreview:hover { - border: 1px solid #979797; - color:#979797; + border: 1px solid @grayIcon; + color: @grayIcon; } .fontFamilyPickerPreview:hover .fontPickerDelete { - color:#979797; + color: @grayIcon; } .canvasdesigner-fontfamilypicker { @@ -590,7 +591,7 @@ h4.panel-title { .canvasdesigner .ui-widget-content { background: rgba(0, 0, 0, 0.27) !important; - border: 0 solid #fff !important; + border: 0 solid @white !important; border-radius: 1px !important; } @@ -601,21 +602,21 @@ h4.panel-title { .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { - border: 1px solid #535353 !important; - background: #535353 !important; + border: 1px solid @gray-3 !important; + background: @gray-3 !important; background-color:none !important; outline:none; } .canvasdesigner .ui-slider .ui-slider-handle:hover, .canvasdesigner .ui-slider .ui-slider-handle:focus { - color: #d9d9d9 !important; + color: @grayLight !important; } .canvasdesigner .ui-slider .ui-slider-handle span { position: absolute !important; margin: -15px 0 0 -8px !important; - color: #535353 !important; + color: @gray-3 !important; font-size: 9px !important; text-align: -webkit-center !important; display: block !important; @@ -629,7 +630,7 @@ h4.panel-title { margin-top: -9px; margin-right: 1px; font-size: 12px; - color: #d9d9d9; + color: @grayLight; text-align: right; background-color: transparent; border: none; @@ -649,11 +650,11 @@ h4.panel-title { border: none; height: 26px; border-radius: 1px; - border: 1px solid #CDCDCD; + border: 1px solid @gray-8; } .canvasdesigner .sp-replacer:hover { - border: 1px solid #979797; + border: 1px solid @grayIcon; } .canvasdesigner .panel-body { @@ -678,7 +679,7 @@ h4.panel-title { .canvasdesigner .color-picker-preview { height: 26px; - border: 1px solid #CDCDCD; + border: 1px solid @gray-8; border-radius: 1px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); cursor: pointer; @@ -764,10 +765,10 @@ h4.panel-title { .border { margin: 75px auto; - background-color: #ffffff; + background-color: @white; border-radius: 10px; opacity: 1.0; - box-shadow: 0 0 0 29px #E9E9EB, 0 0 0 30px #D8D7D9; + box-shadow: 0 0 0 29px @gray-9, 0 0 0 30px @gray-8; transition: all 0.5s ease-in-out; } @@ -794,13 +795,13 @@ iframe { .imagePickerPreview { height: 20px; text-align: center; - background-color: #fff; + background-color: @white; padding: 6px 0 0 0; cursor: pointer; background-size: cover; - border-radius:1px; - border: 1px solid #CDCDCD; - color: #CDCDCD; + border-radius: 1px; + border: 1px solid @gray-8; + color: @gray-8; } .sp-clear-display { @@ -808,16 +809,16 @@ iframe { } .imagePickerPreview:hover { - border: 1px solid #979797; + border: 1px solid @grayIcon; } .imagePickerPreview:hover i { - color: #979797; + color: @grayIcon; } .imagePickerPreview i { font-size:24px; - color: #CDCDCD; + color: @gray-8; } .canvasdesignerImagePicker { @@ -852,7 +853,7 @@ iframe { cursor: pointer; background-position: center center; background-size: cover; - border: 2px solid #F3F2F2; + border: 2px solid @gray-10; } .canvasdesignerImagePicker .media-preview .folder { @@ -861,7 +862,7 @@ iframe { left: 0; width: 100%; font-size: 40px; - color: gainsboro; + color: @grayLight; text-align: center; } @@ -875,7 +876,7 @@ iframe { .canvasdesignerImagePicker .media-preview:hover, .media-preview.selected { - border: 2px solid #84B8F0; + border: 2px solid @blueLight; } .canvasdesignerImagePicker .modal-dialog { @@ -928,14 +929,14 @@ iframe { .bodyCanvasdesignerImagePicker .breadcrumb a.disabled, .bodyCanvasdesignerImagePicker .breadcrumb a.disabled:hover { - color: grey; + color: @gray; text-decoration: none; cursor: default; } .canvasdesignerImagePicker h3 { font-size: 18px; - color: #555555; + color: @gray; } /*************************************************/ @@ -951,11 +952,11 @@ iframe { width: 30px; border-radius: 1px; border: none; - display:inline-block; - background-color:#535353; - cursor:pointer; + display: inline-block; + background-color: @gray-3; + cursor: pointer; margin-right: 6px; - position:relative; + position: relative; } .box-preview li:last-child{ @@ -963,33 +964,33 @@ iframe { } .box-preview li.selected { - border-color:#53a93f !important; + border-color: @greenIcon !important; } .box-preview li.border-all { - border: 6px solid #b3b3b3; + border: 6px solid @gray-7; height: 18px; width: 18px; - margin-left:0px + margin-left: 0px } .box-preview li.border-left { - border-left: 6px solid #b3b3b3; + border-left: 6px solid @gray-7; width: 24px; } .box-preview li.border-right { - border-right: 6px solid #b3b3b3; + border-right: 6px solid @gray-7; width: 24px; } .box-preview li.border-top { - border-top: 6px solid #b3b3b3; + border-top: 6px solid @gray-7; height: 24px; } .box-preview li.border-bottom { - border-bottom: 6px solid #b3b3b3; + border-bottom: 6px solid @gray-7; height: 24px; } @@ -1006,13 +1007,13 @@ iframe { .radius-top-left, .radius-top-right, .radius-bottom-left, .radius-bottom-right { display: block; position: absolute; - background-color: #b3b3b3; + background-color: @gray-7; width: 7px; height: 7px; } .box-preview li.selected span { - background-color:#53a93f; + background-color: @greenIcon; } .radius-top-left{ diff --git a/src/Umbraco.Web.UI.Client/src/less/colors.less b/src/Umbraco.Web.UI.Client/src/less/colors.less new file mode 100644 index 0000000000..4cb70cdf5e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/colors.less @@ -0,0 +1,81 @@ +.red{color: @red;} +.blue{color: @blue;} +.black{color: @black;} +.turquoise{color: @turquoise;} +.turquoise-d1{color: @turquoise-d1;} + +.text-warning { + color: @orange; +} +.text-error { + color: @red; +} +.text-success { + color: @green; +} + + +//icon colors for tree icons +.color-red, .color-red i{color: @red-d1 !important;} +.color-blue, .color-blue i{color: @turquoise-d1 !important;} +.color-orange, .color-orange i{color: @orange !important;} +.color-green, .color-green i{color: @green-d1 !important;} +.color-yellow, .color-yellow i{color: @yellowIcon !important;} + +/* Colors based on https://zavoloklom.github.io/material-design-color-palette/colors.html */ +.btn-color-black {background-color: @black;} +.color-black i { color: @black;} + +.btn-color-blue-grey {background-color: @blueGrey;} +.color-blue-grey, .color-blue-grey i { color: @blueGrey !important;} + +.btn-color-grey{background-color: @grayIcon;} +.color-grey, .color-grey i { color: @grayIcon !important; } + +.btn-color-brown{background-color: @brownIcon;} +.color-brown, .color-brown i { color: @brownIcon !important; } + +.btn-color-blue{background-color: @blueIcon;} +.color-blue, .color-blue i { color: @blueIcon !important; } + +.btn-color-light-blue{background-color: @lightBlueIcon;} +.color-light-blue, .color-light-blue i {color: @lightBlueIcon !important;} + +.btn-color-cyan{background-color: @cyanIcon;} +.color-cyan, .color-cyan i { color: @cyanIcon !important; } + +.btn-color-green{background-color: @greenIcon;} +.color-green, .color-green i { color: @greenIcon !important; } + +.btn-color-light-green{background-color: @lightGreenIcon;} +.color-light-green, .color-light-green i {color: @lightGreenIcon !important; } + +.btn-color-lime{background-color: @limeIcon;} +.color-lime, .color-lime i { color: @limeIcon !important; } + +.btn-color-yellow{background-color: @yellowIcon;} +.color-yellow, .color-yellow i { color: @yellowIcon !important; } + +.btn-color-amber{background-color: @amberIcon;} +.color-amber, .color-amber i { color: @amberIcon !important; } + +.btn-color-orange{background-color: @orangeIcon;} +.color-orange, .color-orange i { color: @orangeIcon !important; } + +.btn-color-deep-orange{background-color: @deepOrangeIcon;} +.color-deep-orange, .color-deep-orange i { color: @deepOrangeIcon !important; } + +.btn-color-red{background-color: @redIcon;} +.color-red, .color-red i { color: @redIcon !important; } + +.btn-color-pink{background-color: @pinkIcon;} +.color-pink, .color-pink i { color: @pinkIcon !important; } + +.btn-color-purple{background-color: @purpleIcon;} +.color-purple, .color-purple i { color: @purpleIcon !important; } + +.btn-color-deep-purple{background-color: @deepPurpleIcon;} +.color-deep-purple, .color-deep-purple i { color: @deepPurpleIcon !important; } + +.btn-color-indigo{background-color: @indigoIcon;} +.color-indigo, .color-indigo i { color: @indigoIcon !important; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index a621370d02..456601a7bd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -57,7 +57,7 @@ .umb-toggle.umb-toggle--checked & { transform: translateX(20px); - background-color: white; + background-color: @white; } } @@ -75,7 +75,7 @@ .umb-toggle__icon--left { left: 5px; - color: white; + color:@white; transition: opacity 120ms; opacity: 0; // .umb-toggle:hover & { @@ -85,7 +85,7 @@ opacity: 1; } .umb-toggle.umb-toggle--checked:hover & { - color: white; + color:@white; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less index 8324698685..ed80359833 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/card.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -5,7 +5,7 @@ .umb-card{ position: relative; padding: 5px 10px 5px 10px; - background: white; + background: @white; width: 100%; .title{padding: 12px; color: @gray-3; border-bottom: 1px solid @gray-8; font-weight: 400; font-size: 16px; text-transform: none; margin: 0 -10px 10px -10px;} @@ -86,7 +86,7 @@ margin: 0 auto; list-style: none; width: 100%; - + display: flex; flex-flow: row wrap; justify-content: flex-start; @@ -119,7 +119,7 @@ padding-top: 100%; border-radius: 3px; transition: background-color 120ms; - + > span { position: absolute; top: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 44cd86a189..1217441f4e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -17,8 +17,8 @@ } } .umb-editor-sub-header--white { - background-color: white; - border-color: white; + background-color: @white; + border-color: @white; } .umb-editor-sub-header.--state-selection { @@ -33,11 +33,11 @@ transition: box-shadow 240ms; position:sticky; z-index: 30; - + &.umb-sticky-bar--active { box-shadow: 0 6px 3px -3px rgba(0,0,0,.16); } - + .umb-dashboard__content & { top:-20px; // umb-dashboard__content has 20px padding - offset here prevents sticky position from firing when page loads } @@ -45,8 +45,8 @@ .umb-sticky-sentinel { pointer-events: none; - z-index: 5050; - + z-index: 5050; + &.-top { height:1px; transform:translateY(-10px); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less index 9947c793c2..7036d60a63 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less @@ -44,7 +44,7 @@ display: flex; padding: 6px; margin: 10px 0px !important; - background: #F3F3F5; + background: @gray-10; cursor: move; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less index 8945d15ec6..df01477880 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less @@ -3,11 +3,11 @@ min-width: 100%; width: auto; margin-top:1px; - + .umb-tree-item__label { user-select: none; } - + &:hover .umb-tree-item__arrow { visibility: visible; cursor: pointer @@ -36,7 +36,7 @@ overflow: hidden; margin-right: 6px; } - + // Loading Animation // ------------------------ .umb-tree-item__loader { @@ -46,7 +46,7 @@ } .umb-tree-item__label { - padding: 7px 0 5px; + padding: 7px 0 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -71,7 +71,7 @@ left: 0; right: 0; bottom: 0; - border: 2px solid fade(white, 80%); + border: 2px solid fade(@white, 80%); } &:hover { @@ -86,12 +86,12 @@ .umb-tree-item.current > .umb-tree-item__inner { background: @ui-active; color:@ui-active-type; - - // override small icon color. TODO => check usage + + // override small icon color. TODO => check usage &:before { color: @blue; } - + .umb-options { &:hover i { @@ -113,5 +113,5 @@ .umb-tree-item.current-not-active > .umb-tree-item__inner { background: @ui-active-blur; - color:@ui-active-type; + color:@ui-active-type; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less index 21f4a7bda8..c6b9dc7261 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less @@ -103,7 +103,7 @@ a.umb-avatar-btn:hover { text-decoration: none; } a.umb-avatar-btn .umb-avatar { - border: 2px dashed #A2A1A6; + border: 2px dashed @gray-6; } a.umb-avatar-btn .umb-avatar span { font-size: 50px; @@ -114,4 +114,4 @@ a.umb-avatar-btn .umb-avatar span { font-size: 50px; } -/*border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: #F3F3F5; border: 2px dashed #A2A1A6; color: #A2A1A6;*/ +/*border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: @gray-10; border: 2px dashed @gray-6; color: @gray-6;*/ diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less index 7f19c4933c..e0dee5f266 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less @@ -80,7 +80,7 @@ outline: 2px solid @inputBorderTabFocus; } .tabbing-active &.umb-form-check--checkbox &__input:checked:focus ~ .umb-form-check__state .umb-form-check__check { - border-color: white; + border-color: @white; } // add spacing between when flexed/inline, equal to the width of the input diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 277c2bcbe8..479074fee9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -437,7 +437,7 @@ .umb-grid .umb-row.-active-child { background-color: @gray-10; - + .umb-row-title-bar { cursor: inherit; } @@ -445,7 +445,7 @@ .umb-row-title { color: @gray-3; } - + } @@ -582,10 +582,10 @@ .umb-grid .iconBox.selected { -webkit-appearance: none; - background-image: linear-gradient(to bottom,#e6e6e6,#bfbfbf); + background-image: linear-gradient(to bottom,@gray-9,@gray-7); background-repeat: repeat-x; zoom: 1; - border-color: #bfbfbf #bfbfbf #999; + border-color: @gray-7 @gray-7 @gray-6; border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05); border-radius: 3px; @@ -638,9 +638,9 @@ .umb-grid .mce-toolbar { border-bottom: 1px solid @gray-7; - background-color: white; + background-color: @white; display: none; - + left: 0; right: 0; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less index 34070256ce..e8a62f739d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less @@ -51,7 +51,7 @@ // Color swatch .button { border: none; - color: white; + color: @white; padding: 5px; text-align: center; text-decoration: none; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less index a87e7084fb..f3b53f4def 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less @@ -11,7 +11,7 @@ cursor: pointer; } -.umb-insert-code-box:hover, +.umb-insert-code-box:hover, .umb-insert-code-box.-selected { background-color: @ui-option-hover; color: @ui-action-type-hover; @@ -32,7 +32,7 @@ .umb-insert-code-box__check { width: 18px; height: 18px; - background: @gray-10;x + background: @gray-10; border-radius: 50%; display: flex; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less index cdc6cfcb63..9ebd6d6e5d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less @@ -23,10 +23,10 @@ .umb-layout-selector__dropdown { position: absolute; padding: 5px; - background: #333; + background: @grayDark; z-index: 999; display: flex; - background: #fff; + background: @white; flex-wrap: wrap; flex-direction: column; transform: translate(-50%,0); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less index f6dfed63c1..ba46c68a57 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less @@ -52,7 +52,7 @@ tbody tr { background: @gray-10; - border-bottom: 1px solid #fff; + border-bottom: 1px solid @white; } th { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less index 76223589e4..8beff55b7c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less @@ -83,7 +83,7 @@ top: 0; line-height: 32px; right: 140px; - color: #fdb45c; + color: @yellow-d1; cursor: pointer; } @@ -92,7 +92,7 @@ top: 0; line-height: 32px; right: 120px; - color: #bbbabf; + color: @gray-7; cursor: pointer; } @@ -133,7 +133,7 @@ } .exception { - border-left: 4px solid #D42054; + border-left: 4px solid @red; padding: 0 10px 10px 10px; box-shadow: rgba(0,0,0,0.07) 2px 2px 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 50244c2079..05d91de9f7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -54,7 +54,7 @@ color: @ui-selected-type; } } -.umb-media-grid__item.-selected, +.umb-media-grid__item.-selected, .umb-media-grid__item.-selectable:hover { &::before { content: ""; @@ -130,10 +130,10 @@ overflow: hidden; color: @black; white-space: nowrap; - border-top:1px solid fade(black, 4%); + border-top:1px solid fade(@black, 4%); background: fade(@white, 92%); transition: opacity 150ms; - + &:hover { text-decoration: underline; } @@ -181,7 +181,7 @@ align-items: center; color: @black; transition: opacity 150ms; - + &:hover { color: @ui-action-discreet-type-hover; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index bf0dd9d109..699496f5d3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -82,7 +82,7 @@ padding: 15px 5px; color:@ui-option-type; border-radius: 3px 3px 0 0; - + &:hover { color:@ui-option-type-hover; } @@ -104,7 +104,7 @@ padding-left: 30px; } } - + } .umb-nested-content__icons { @@ -226,19 +226,19 @@ display: none !important; } -.umb-nested-content__doctypepicker table input, +.umb-nested-content__doctypepicker table input, .umb-nested-content__doctypepicker table select { width: 100%; padding-right: 0; } -.umb-nested-content__doctypepicker table td.icon-navigation, +.umb-nested-content__doctypepicker table td.icon-navigation, .umb-nested-content__doctypepicker i.umb-nested-content__help-icon { vertical-align: middle; color: @gray-7; } -.umb-nested-content__doctypepicker table td.icon-navigation:hover, +.umb-nested-content__doctypepicker table td.icon-navigation:hover, .umb-nested-content__doctypepicker i.umb-nested-content__help-icon:hover { color: @gray-2; } @@ -248,11 +248,11 @@ } .umb-nested-content__placeholder { - padding: 4px 6px; - border: 1px dashed #d8d7d9; + padding: 4px 6px; + border: 1px dashed @gray-8; background: 0 0; cursor: pointer; - color: #1b264f; + color: @blueExtraDark; -webkit-animation: fadeIn .5s; animation: fadeIn .5s; text-align: center; @@ -265,8 +265,8 @@ } .umb-nested-content__placeholder:hover { - color: #2152a3; - border-color: #2152a3; + color: @blueMid; + border-color: @blueMid; text-decoration: none; } @@ -288,7 +288,7 @@ // the attribute selector ensures the change only applies to the linkpicker overlay .form-horizontal .umb-nested-content--narrow [ng-controller*="Umbraco.Overlays.LinkPickerController"] .controls-row { margin-left:0!important; - + .umb-textarea, .umb-textstring { width:100%; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less index 3ce284870e..3f0b981ac6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less @@ -39,7 +39,7 @@ top: -15px; border-radius: 3px 3px 0 0; - + border-top-left-radius: 3px; border-top-right-radius: 3px; @@ -49,8 +49,8 @@ .box-shadow(0 5px 20px rgba(0,0,0,.3)); - background-color: white; - + background-color: @white; + } .umb-property .umb-property-actions { @@ -71,12 +71,12 @@ } .umb-property-actions__menu { - + position: absolute; z-index: 1000; display: block; - + float: left; min-width: 160px; list-style: none; @@ -85,11 +85,11 @@ border-top-left-radius: 0; margin-top:1px; - + } .umb-contextmenu-item > button { - + z-index:2;// need to stay on top of menu-toggle-open shadow. } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less index 1461d0f223..6ae92ffa4e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -1,7 +1,7 @@ .umb-range-slider.noUi-target { - background: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); + background: linear-gradient(to bottom, @grayLighter 0%, @grayLighter 100%); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); border-radius: 20px; height: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less index 7caec3c78e..a612af65ef 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less @@ -1,7 +1,7 @@ .umb-user-details-avatar { margin-bottom: 20px; padding-bottom: 20px; - border-bottom: 1px solid #d8d7d9; + border-bottom: 1px solid @gray-8; } div.umb-user-details-actions > div { @@ -81,7 +81,7 @@ a.umb-user-details-details__back-link { margin-bottom: 30px; margin-right: 0; } - + .umb-user-details-details__sidebar { flex: 1 1 auto; width: 100%; diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less index 807d0e863f..d64ffef098 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less @@ -22,8 +22,8 @@ text-align: center; display: flex; align-items: center; - border: 1px solid #d8d7d9; - background-color: #fff; + border: 1px solid @gray-8; + background-color: @white; margin: 0 0 0.5em; @media (min-width: 500px) { diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less index 4ebe1d47b0..91922300fb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less @@ -4,7 +4,7 @@ } .top-border { - border-top: 2px solid #f3f3f5; + border-top: 2px solid @gray-10; } .no-left-padding { diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less index b51bfeffa9..21ec047d41 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less @@ -106,7 +106,7 @@ .step-text { font-size: 16px; line-height: 1.5; - color: #4c4c4c; + color: @gray; margin-bottom: 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/footer.less b/src/Umbraco.Web.UI.Client/src/less/footer.less deleted file mode 100644 index 93fdb6ab5e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/footer.less +++ /dev/null @@ -1,2 +0,0 @@ -// Footer -// ------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/less/gridview.less b/src/Umbraco.Web.UI.Client/src/less/gridview.less index bb684cd69b..238feead90 100644 --- a/src/Umbraco.Web.UI.Client/src/less/gridview.less +++ b/src/Umbraco.Web.UI.Client/src/less/gridview.less @@ -365,12 +365,12 @@ .usky-grid .iconBox.selected { -webkit-appearance: none; - background-image: -webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf)); - background-image: -webkit-linear-gradient(top,#e6e6e6,#bfbfbf); - background-image: linear-gradient(to bottom,#e6e6e6,#bfbfbf); + background-image: -webkit-gradient(linear,0 0,0 100%,from(@gray-9),to(@gray-7)); + background-image: -webkit-linear-gradient(top,@gray-9,@gray-7); + background-image: linear-gradient(to bottom,@gray-9,@gray-7); background-repeat: repeat-x; zoom: 1; - border-color: #bfbfbf #bfbfbf #999; + border-color: @gray-7 @gray-7 @gray-6; border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05); border-radius: 3px; @@ -379,7 +379,7 @@ .usky-grid .iconBox i { font-size:16px !important; - color: #5F5F5F; + color: @gray-4; display:block; } diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less index 865f015ffa..e964ed3c6f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/installer.less +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -1,6 +1,7 @@ // Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "colors.less"; @import "mixins.less"; @import "buttons.less"; @import "forms.less"; @@ -133,8 +134,8 @@ legend { } input.ng-dirty.ng-invalid { - border-color: #b94a48; - color: #b94a48; + border-color: @pink; + color: @pink; } .disabled { diff --git a/src/Umbraco.Web.UI.Client/src/less/legacydialog.less b/src/Umbraco.Web.UI.Client/src/less/legacydialog.less index ca920d22c2..f1835a682c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/legacydialog.less +++ b/src/Umbraco.Web.UI.Client/src/less/legacydialog.less @@ -16,8 +16,8 @@ margin-top: 10px; height: 100%; overflow: auto; - border-top: 1px solid #ccc; - border-top: 1px solid #ccc; + border-top: 1px solid @gray-8; + border-top: 1px solid @gray-8; padding: 5px; } @@ -30,11 +30,11 @@ .umb-dialog .diff table th { padding: 5px; width: 25%; - border-bottom: 1px solid #ccc; + border-bottom: 1px solid @gray-8; } .umb-dialog .diff table td { - border-bottom: 1px solid #ccc; + border-bottom: 1px solid @gray-8; padding: 3px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index e49755338b..21b9c5c550 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -332,7 +332,7 @@ } // Gradient Bar Colors for buttons and alerts -.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff) { +.gradientBar(@primaryColor, @secondaryColor, @textColor: @white) { color: @textColor; #gradient > .vertical(@primaryColor, @secondaryColor); border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); @@ -341,7 +341,7 @@ // Gradients #gradient { - .horizontal(@startColor: #555, @endColor: #333) { + .horizontal(@startColor: @gray, @endColor: @grayDark) { background-color: @endColor; background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ @@ -350,7 +350,7 @@ background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 background-repeat: repeat-x; } - .vertical(@startColor: #555, @endColor: #333) { + .vertical(@startColor: @gray, @endColor: @grayDark) { background-color: mix(@startColor, @endColor, 60%); background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ @@ -359,7 +359,7 @@ background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 background-repeat: repeat-x; } - .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { + .directional(@startColor: @gray, @endColor: @grayDark, @deg: 45deg) { background-color: @endColor; background-repeat: repeat-x; background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ @@ -367,7 +367,7 @@ background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 } - .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + .horizontal-three-colors(@startColor: @lightBlueIcon, @midColor: @purple-l1, @colorStop: 50%, @endColor: @pink) { background-color: mix(@midColor, @endColor, 80%); background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); @@ -377,7 +377,7 @@ background-repeat: no-repeat; } - .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + .vertical-three-colors(@startColor: @lightBlueIcon, @midColor: @deepPurpleIcon, @colorStop: 50%, @endColor: @pink) { background-color: mix(@midColor, @endColor, 80%); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); @@ -386,7 +386,7 @@ background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); background-repeat: no-repeat; } - .radial(@innerColor: #555, @outerColor: #333) { + .radial(@innerColor: @gray, @outerColor: @grayDark) { background-color: @outerColor; background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); @@ -394,7 +394,7 @@ background-image: -o-radial-gradient(circle, @innerColor, @outerColor); background-repeat: no-repeat; } - .striped(@color: #555, @angle: 45deg) { + .striped(@color: @gray, @angle: 45deg) { background-color: @color; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); @@ -404,7 +404,7 @@ } } -.checkeredBackground(@backgroundColor: #eee, @fillColor: #000, @fillOpacity: 0.25) { +.checkeredBackground(@backgroundColor: @gray-9, @fillColor: @black, @fillOpacity: 0.25) { background-image: url('data:image/svg+xml;charset=utf-8,\ \ \ @@ -435,14 +435,14 @@ // Button backgrounds // ------------------ -.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: #fff, @textColorHover: @textColor) { - +.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor) { + color: @textColor; border-color: @startColor @startColor darken(@startColor, 15%); border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); - + background-color: @startColor; - + .caret { border-top-color: @textColor; border-bottom-color: @textColor; @@ -453,7 +453,7 @@ color: @textColorHover; background-color: @hoverColor; } - + &.disabled, &[disabled] { color: @white; background-color: @sand-1; diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index fa23e08983..925f845c4c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -85,7 +85,7 @@ bottom: 0px; position: absolute; padding: 0px; - background: #fff; + background: @white; } .umb-dialog .umb-btn-toolbar .umb-control-group{ diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/document-type-editor.less b/src/Umbraco.Web.UI.Client/src/less/pages/document-type-editor.less deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less b/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less index 868a358d21..7c493ab18e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less @@ -1,5 +1,6 @@ @import "../fonts.less"; // Loading fonts @import "../variables.less"; // Loading variables +@import "../colors.less"; // Loading colors that were in variables abbr, address, diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 823daedf22..8301b6a753 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -1,13 +1,13 @@ // // Container styles // -------------------------------------------------- -.umb-property-editor { +.umb-property-editor { width: 100%; } .umb-property-editor-tiny { width: 60px; - + &.umb-editor-push { width:30%; min-width:0; @@ -30,7 +30,7 @@ } .umb-codeeditor{ - width: 99%; + width: 99%; } // displays property inline with preceeding @@ -139,7 +139,7 @@ margin-bottom: 0; vertical-align: middle; padding: 6px 10px; - background: #f7f7f7; + background: @gray-11; flex: 0 0 auto; } @@ -169,8 +169,8 @@ border: 1px solid @white; padding: 6px 10px; font-family: monospace; - border: 1px solid #dfdfe1; - background: #f7f7f7; + border: 1px solid @gray-8; + background: @gray-11; margin: 0 15px 0 3px; border-radius: 3px; } @@ -253,7 +253,7 @@ color: @ui-action-discreet-type-hover; border-color: @ui-action-discreet-type-hover; } - + &:focus { outline: none; .tabbing-active &:after { @@ -358,11 +358,11 @@ max-width: 100%; } .umb-mediapicker { - + .umb-sortable-thumbnails li { border:none; } - + } .umb-mediapicker .umb-sortable-thumbnails li { @@ -623,7 +623,7 @@ .imagecropper .umb-cropper__container { position: relative; margin-bottom: 10px; - max-width: 100%; + max-width: 100%; border: 1px solid @gray-10; @media (min-width: 769px) { @@ -822,7 +822,7 @@ background: @blueExtraDark; position: relative; user-select: all; - + .icon-trash { position: relative; cursor: pointer; @@ -843,7 +843,7 @@ border: none; background: @white; } - + .twitter-typeahead { margin: 10px; margin-top: 16px; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte-content.less b/src/Umbraco.Web.UI.Client/src/less/rte-content.less index 5fd7bbf1c3..3fe4e52f92 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte-content.less @@ -1,4 +1,5 @@ @import "variables.less"; +@import "colors.less"; .mce-content-body .umb-macro-holder { border: 3px dotted @pinkLight; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 445ed7eb4a..d6d38f540a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -3,7 +3,7 @@ .umb-rte { position: relative; - + .umb-property-editor--limit-width(); } @@ -116,7 +116,7 @@ } } -.umb-rte .mce-btn.mce-active, .umb-rte .mce-btn.mce-active:active, +.umb-rte .mce-btn.mce-active, .umb-rte .mce-btn.mce-active:active, .umb-rte .mce-btn.mce-active:hover, .umb-rte .mce-btn.mce-active:focus { background: @gray-9; border-color: transparent; @@ -158,7 +158,7 @@ } .umb-grid .umb-rte { - border: 1px solid #d8d7d9; + border: 1px solid @gray-8; max-width: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index e8f6d4ee58..6071c4a5ef 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -76,11 +76,11 @@ @gray-10: #F3F3F5; @gray-11: #F6F6F7; -@sand-1: hsl(22, 18%, 84%);// added 2019 -@sand-2: hsl(22, 34%, 88%);// added 2019 -@sand-5: hsl(22, 31%, 93%);// added 2019 -@sand-6: hsl(22, 29%, 95%);// added 2019 -@sand-7: hsl(22, 26%, 97%);// added 2019 +@sand-1: #DED4CF;// added 2019 +@sand-2: #EBDED6;// added 2019 +@sand-5: #F3ECE8;// added 2019 +@sand-6: #F6F1EF;// added 2019 +@sand-7: #F9F7F5;// added 2019 // Additional Icon Colours @@ -124,7 +124,7 @@ //@u-greyLight: #f2ebe6;// added 2019 @u-white: #f9f7f4;// added 2019 -@u-black: black;// added 2019 +@u-black: @black;// added 2019 // UI colors @@ -132,7 +132,7 @@ @ui-option-type: @blueExtraDark; @ui-option-type-hover: @blueMid; -@ui-option: white; +@ui-option: @white; @ui-option-hover: @sand-7; @ui-option-disabled-type: @gray-6; @@ -161,14 +161,14 @@ @ui-light-active-type-hover: @blueMid; -@ui-action: white; +@ui-action: @white; @ui-action-hover: @sand-7; @ui-action-type: @blueExtraDark; @ui-action-type-hover: @blueMid; @ui-action-border: @blueExtraDark; @ui-action-border-hover: @blueMid; -@ui-action-discreet: white; +@ui-action-discreet: @white; @ui-action-discreet-hover: @sand-7; @ui-action-discreet-type: @blueExtraDark; @ui-action-discreet-type-hover: @blueMid; @@ -194,96 +194,11 @@ - -.red{color: @red;} -.blue{color: @blue;} -.black{color: @black;} -.turquoise{color: @turquoise;} -.turquoise-d1{color: @turquoise-d1;} - -.text-warning { - color: @orange; -} -.text-error { - color: @red; -} -.text-success { - color: @green; -} - - -//icon colors for tree icons -.color-red, .color-red i{color: @red-d1 !important;} -.color-blue, .color-blue i{color: @turquoise-d1 !important;} -.color-orange, .color-orange i{color: @orange !important;} -.color-green, .color-green i{color: @green-d1 !important;} -.color-yellow, .color-yellow i{color: @yellowIcon !important;} - -/* Colors based on https://zavoloklom.github.io/material-design-color-palette/colors.html */ -.btn-color-black {background-color: @black;} -.color-black i { color: @black;} - -.btn-color-blue-grey {background-color: @blueGrey;} -.color-blue-grey, .color-blue-grey i { color: @blueGrey !important;} - -.btn-color-grey{background-color: @grayIcon;} -.color-grey, .color-grey i { color: @grayIcon !important; } - -.btn-color-brown{background-color: @brownIcon;} -.color-brown, .color-brown i { color: @brownIcon !important; } - -.btn-color-blue{background-color: @blueIcon;} -.color-blue, .color-blue i { color: @blueIcon !important; } - -.btn-color-light-blue{background-color: @lightBlueIcon;} -.color-light-blue, .color-light-blue i {color: @lightBlueIcon !important;} - -.btn-color-cyan{background-color: @cyanIcon;} -.color-cyan, .color-cyan i { color: @cyanIcon !important; } - -.btn-color-green{background-color: @greenIcon;} -.color-green, .color-green i { color: @greenIcon !important; } - -.btn-color-light-green{background-color: @lightGreenIcon;} -.color-light-green, .color-light-green i {color: @lightGreenIcon !important; } - -.btn-color-lime{background-color: @limeIcon;} -.color-lime, .color-lime i { color: @limeIcon !important; } - -.btn-color-yellow{background-color: @yellowIcon;} -.color-yellow, .color-yellow i { color: @yellowIcon !important; } - -.btn-color-amber{background-color: @amberIcon;} -.color-amber, .color-amber i { color: @amberIcon !important; } - -.btn-color-orange{background-color: @orangeIcon;} -.color-orange, .color-orange i { color: @orangeIcon !important; } - -.btn-color-deep-orange{background-color: @deepOrangeIcon;} -.color-deep-orange, .color-deep-orange i { color: @deepOrangeIcon !important; } - -.btn-color-red{background-color: @redIcon;} -.color-red, .color-red i { color: @redIcon !important; } - -.btn-color-pink{background-color: @pinkIcon;} -.color-pink, .color-pink i { color: @pinkIcon !important; } - -.btn-color-purple{background-color: @purpleIcon;} -.color-purple, .color-purple i { color: @purpleIcon !important; } - -.btn-color-deep-purple{background-color: @deepPurpleIcon;} -.color-deep-purple, .color-deep-purple i { color: @deepPurpleIcon !important; } - -.btn-color-indigo{background-color: @indigoIcon;} -.color-indigo, .color-indigo i { color: @indigoIcon !important; } - - - // Scaffolding // ------------------------- @appHeaderHeight: 55px; @bodyBackground: @gray-10; -@textColor: #000; +@textColor: @black; @editorHeaderHeight: 70px; @editorFooterHeight: 50px; From 3e480229491662aed5f7830068bbdecfe394514b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Dec 2019 13:19:14 +1100 Subject: [PATCH 159/173] Fixes test --- .../Scheduling/BackgroundTaskRunnerTests.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index ea42cd2ea9..aacf48a636 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -297,7 +297,17 @@ namespace Umbraco.Tests.Scheduling // dispose will stop it } - await runner.StoppedAwaitable; + try + { + await runner.StoppedAwaitable; + } + catch (OperationCanceledException) + { + // swallow this exception, it can be expected to throw since when disposing we are calling Shutdown +force + // which depending on a timing operation may cancel the cancelation token + } + + Assert.Throws(() => runner.Add(new MyTask())); } From 37513a5161c721cc6894f02ae6e0db1e54116a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 6 Dec 2019 09:34:53 +0100 Subject: [PATCH 160/173] basic setup for differentiating compile modes in gulp + only compile sourcemaps in dev mode --- src/Umbraco.Web.UI.Client/gulp/config.js | 8 +++++++ src/Umbraco.Web.UI.Client/gulp/modes.js | 13 ++++++++++ .../gulp/util/processLess.js | 24 ++++++++++++------- src/Umbraco.Web.UI.Client/gulpfile.js | 19 ++++----------- 4 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/gulp/modes.js diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index ee63d0085e..59e8bf6c05 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -1,6 +1,14 @@ 'use strict'; module.exports = { + compile: { + build: { + sourcemaps: false + }, + dev: { + sourcemaps: true + } + }, sources: { // less files used by backoffice and preview diff --git a/src/Umbraco.Web.UI.Client/gulp/modes.js b/src/Umbraco.Web.UI.Client/gulp/modes.js new file mode 100644 index 0000000000..dc2947f2cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/gulp/modes.js @@ -0,0 +1,13 @@ +'use strict'; + +var config = require('./config'); +var gulp = require('gulp'); + +function setDevelopmentMode(cb) { + + config.compile.current = config.compile.dev; + + return cb(); +}; + +module.exports = { setDevelopmentMode: setDevelopmentMode }; diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js index 1566c7ac79..e33fc0389b 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js @@ -19,14 +19,22 @@ module.exports = function(files, out) { console.log("LESS: ", files, " -> ", config.root + config.targets.css + out) - var task = gulp.src(files) - .pipe(sourcemaps.init()) - .pipe(less()) - .pipe(cleanCss()) - .pipe(postcss(processors)) - .pipe(rename(out)) - .pipe(sourcemaps.write('./maps')) - .pipe(gulp.dest(config.root + config.targets.css)); + var task = gulp.src(files); + + if(config.compile.current.sourcemaps === true) { + task = task.pipe(sourcemaps.init()); + } + + task = task.pipe(less()); + task = task.pipe(cleanCss()); + task = task.pipe(postcss(processors)); + task = task.pipe(rename(out)); + + if(config.compile.current.sourcemaps === true) { + task = task.pipe(sourcemaps.write('./maps')); + } + + task = task.pipe(gulp.dest(config.root + config.targets.css)); return task; diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 1e4dc591ca..705c54bf04 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -12,6 +12,8 @@ const { src, dest, series, parallel, lastRun } = require('gulp'); +const config = require('./gulp/config'); +const { setDevelopmentMode } = require('./gulp/modes'); const { dependencies } = require('./gulp/tasks/dependencies'); const { js } = require('./gulp/tasks/js'); const { less } = require('./gulp/tasks/less'); @@ -19,25 +21,14 @@ const { testE2e, testUnit } = require('./gulp/tasks/test'); const { views } = require('./gulp/tasks/views'); const { watchTask } = require('./gulp/tasks/watchTask'); -// Load local overwrites, can be used to overwrite paths in your local setup. -var fs = require('fs'); -var onlyScripts = require('./gulp/util/scriptFilter'); -try { - if (fs.existsSync('./gulp/overwrites/')) { - var overwrites = fs.readdirSync('./gulp/overwrites/').filter(onlyScripts); - overwrites.forEach(function(overwrite) { - require('./gulp/overwrites/' + overwrite); - }); - } -} catch (err) { - console.error(err) - } +// set default current compile mode: +config.compile.current = config.compile.build; // *********************************************************** // These Exports are the new way of defining Tasks in Gulp 4.x // *********************************************************** exports.build = series(parallel(dependencies, js, less, views), testUnit); -exports.dev = series(parallel(dependencies, js, less, views), watchTask); +exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // exports.runTests = series(js, testUnit); From 66801f09abf238296b21d4c291f8ca775ef2e85f Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 6 Dec 2019 09:27:35 +0000 Subject: [PATCH 161/173] Remove the cleanup code of LESS Source Map files from the ZIP & Nupkg builds - this is done with Niels PR as only gulp dev creates LESS SourceMaps --- build/build.ps1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index b40be8e12f..ea07e4516f 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -313,10 +313,6 @@ $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\js", "*", "$tmp\WebApp\umbraco\js") $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\lib", "*", "$tmp\WebApp\umbraco\lib") $this.CopyFiles("$src\Umbraco.Web.UI\umbraco\views", "*", "$tmp\WebApp\umbraco\views") - - # copied too much - delete the LESS map directory - # that was copied over into "$tmp\WebApp\umbraco\assets\css\maps" - Remove-Item "$tmp\WebApp\umbraco\assets\css\maps" -Force -Recurse -ErrorAction SilentlyContinue }) $ubuild.DefineMethod("PackageZip", From 6c0cb723c66ca63ccc3d1d63d2013f8d9336063b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Dec 2019 09:40:41 +0100 Subject: [PATCH 162/173] new preview UI prototype --- .../src/less/canvas-designer.less | 1044 ++--------------- .../src/preview/preview.controller.js | 30 +- .../Umbraco/Views/Preview/Index.cshtml | 154 +-- 3 files changed, 216 insertions(+), 1012 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index ddb45517b6..ce535d796a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -14,730 +14,156 @@ /****************************/ body { + position: absolute; overflow: hidden; height: 100%; - width: 100%; - width: calc(~"100% - 80px"); // 80px is the fixed left menu for toggling the different browser sizes - position: absolute; + height: calc(~"100% - 40px"); + width: 100%; // 80px is the fixed left menu for toggling the different browser sizes padding: 0; margin: 0; - font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 20px; - color: @grayDark; - transition: all 0.2s ease-in-out; - padding-left:80px; + padding-top:40px; + background-color: @brownGrayExtraLight; } -h4 { - margin: 0; - font-size: 16px; - background-color: @gray-3; - color: @gray-7; - text-transform: uppercase; - position: initial; - display: block; - padding: 5px 10px; - font-size: 12px; - font-weight: normal; -} - -h5 { - margin: 0 0 6px 0; - font-size: 12px; -} - -ul { - list-style:none; - margin:0; - padding:0; -} - -a, a:hover{ - color: @grayDark; - text-decoration:none; -} - - /****************************/ /* General class */ /****************************/ -.right { - float:right; - display:inline-block; -} - -.left { - float:left; - display:inline-block; -} - -.leftOpen { - padding-left: 330px -} - -.wait { - display: block; - height: 100%; - width: 100%; - background:@white center center url(../img/loader.gif) no-repeat; -} - -/****************************/ -/* Group button */ -/****************************/ - -.btn-group { - float: right; - margin-right: 10px; -} - -.btn { - display: inline-block; - padding: 4px 12px; - margin-bottom: 0; - font-size: 14px; - line-height: 20px; - color: @black; - text-align: center; - vertical-align: middle; - cursor: pointer; - background: @gray-10; - border: 1px solid @gray-8; - border-radius: 2px; - box-shadow: none; -} - -.btn-group > .btn + .dropdown-toggle { - box-shadow: none; -} - -.btn-group > .btn + .btn { - margin-left: -6px; -} - -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; - box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group > .btn:last-child, .btn-group > .dropdown-toggle { - border-top-right-radius: 2px; - border-bottom-right-radius: 2px; -} - -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid @white; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; - border-top: 0; - border-bottom: 4px solid @white; - margin-top: 8px; - margin-left: 0; -} - -.dropdown-menu { +.menu-bar { position: absolute; - display: block; - top: auto; - right: 0; - z-index: 1000; - display: block; - float: left; - min-width: 160px; - padding: 5px 0; - margin: -96px 10px 0 0; - margin-bottom: 1px; - list-style: none; - background-color: @white; - border: 1px solid @gray-8; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 6px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - background-clip: padding-box; -} - -.dropdown-menu > li > a, -.dropdown-menu > li > button { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 20px; - color:@black; - white-space: nowrap; - cursor:pointer; -} - -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-menu > li > button:hover, -.dropdown-menu > li > button:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - color: @black; - background: @brownLight; -} - -/****************************/ -/* Speech bubble */ -/****************************/ - -#speechbubble { - position: absolute; - right: 0; - bottom: 40px; + top:0; left: 0; - z-index: 9999; - display: none; - padding: 0; - margin: auto; - margin-left: 300px; - text-align: left; - background: none; - border: none; - border-bottom: none; -} + right: 0; + background-color: @blueExtraDark; + color:@white; -#speechbubble p { - position: relative; - padding: 8px 30px 8px 20px; - margin: auto; - margin-top: 5px; - font-size: 12px; - color: @white; - text-shadow: none; - background-color: @greenIcon; - border: none; - border-color: transparent; - border-radius: 5px 0 0 5px; -} - - -/****************************/ -/* Main section menu */ -/****************************/ - -.more-options i { - display: inline-block; - width: 5px !important; - height: 5px !important; - margin: 5px 1px 7px 0; - background: @grayLight; - border-radius: 20px; -} - -.fix-left-menu { - position: fixed; - top: 0; - left: 0; - width: 80px; - height: 100%; - padding: 0; - margin-left: -80px; font-family: "Lato", Helvetica, Arial, sans-serif; - font-size: 13px; + font-size: 12px; line-height: 16px; - background: @blueExtraDark; - transition: all 0.2s ease-in-out; - z-index: 9999; + } -.avatar { - text-align:center; - padding: 27px 0 29px 0; - border-bottom: 1px solid @purple-d1; -} +.menu-bar--right-part { + float: right; + display: flex; + flex: row; -.help { - position: absolute; - bottom: 0; - left: 0; - display: block; - width: 100%; - margin: 0; - font-size: 30px; - text-align: center; - color: @gray-8; - opacity: 0.4; - transition: all .3s linear; -} - -ul.sections { - display: block; - background: @blueExtraDark; - position:absolute; - top: 90px; - width: 80px; - transition: all 0.2s ease-in-out; - list-style:none; - margin:0; - padding:0; - margin-left: -80px; - overflow: auto; - overflow-x: hidden; - height: calc(100% - 91px); - - &::-webkit-scrollbar { - width: 0px; - background: transparent; + > div, > button { + border-left: 1px solid rgba(255, 255, 255, .25); } } -ul.sections li { - display: block; - border-left: 4px @blueExtraDark solid; - transition: all .3s linear; +.menu-bar--title { + display: inline-block; + padding: 11px 15px; + font-weight: bold; +} + +.menu-bar--button { + display: inline-block; + padding: 11px 15px; + border:none; + background-color: @blueExtraDark; + + font: inherit; + color: inherit; cursor: pointer; -} -.fix-left-menu ul.sections li a span, -.fix-left-menu ul.sections li a i { - color: @white; - opacity: .7; - transition: all .3s linear; -} + transition: color 120ms linear, background-color 120ms linear; + + .icon { + margin-right: 10px; + font-size: 14px; + vertical-align: middle; + } + + span { + vertical-align: middle; + } + + > svg { + display: inline-block; + width: 12px; + height: 12px; + fill: #fff; + margin-right: 10px; + vertical-align: middle; + transition: fill 120ms linear; + } -ul.sections li a { - display: block; - width: 100%; - height: 100%; - padding: 20px 4px 15px 0; - margin: 0 0 0 -4px; - text-align: center; - text-decoration: none; - border-bottom: 1px solid @purple-d1; &:hover { - span, i { - opacity: 1; - color:@white; + background-color: lighten(@blueExtraDark, 4%); + color: @pinkLight; + > svg { + fill: @pinkLight; } } } -ul.sections li a i { - font-size: 30px; - opacity: 0.8; -} +.preview-menu-option { -ul.sections li a span { - display: block; - font-size: 10px; - line-height: 1.4em; - opacity: 0.8; -} - -ul.sections li.current { - border-left: 4px @pinkLight solid; -} - -ul.sections li.current a i { - color: @pinkLight; -} - -ul.sections li.current { - border-left: 4px @pinkLight solid; -} - -ul.sections li:hover a i, -ul.sections li:hover a span { - opacity: 1; -} - -.fix-left-menu:hover .help { - opacity: 1; -} - -.fix-left-menu.selected, -.sections.selected { - margin-left:0px; -} - -/*************************************************/ -/* Main panel */ -/*************************************************/ - -.main-panel { - position: fixed; - top: 0; - left: 0; - margin-left: -330px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 16px; - background: @white; - transition: all 0.2s ease-in-out; - width: 250px; - height: 100%; - padding: 0; - z-index: 999; - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -} - -.main-panel .header { - padding: 28px 20px 32px 20px; - font-weight: bold; - background: @grayLighter; - border-bottom: 1px solid @grayLight; -} - -.main-panel .header h3 { - color: rgba(179, 179, 179, 0.49); - font-size: 24px; - margin:0; -} - -.main-panel .header h3 i { - position: absolute; - right: 20px; - cursor:pointer; - transition: all 0.2s ease-in-out; -} - -.main-panel .header h3 i:hover { - color: @grayDark; -} - -.main-panel.selected { - margin-left: 80px; - position: absolute; - right: 0; - bottom: 0; - left: 0; - overflow: auto; -} - -.main-panel .content { - padding:20px 0; -} - -/*************************************************/ -/* float-panel */ -/*************************************************/ - -.float-panel { - position: fixed; - top: 0; - z-index: 99; - width: 250px; - height: 100%; - padding: 0; - padding: 20px; - margin-left: -480px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 16px; - background: @white; - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - transition: all 0.2s ease-in-out; -} - -.float-panel.selected{ - margin-left: 0px; -} - -/*************************************************/ -/* sample palette color */ -/*************************************************/ - -.samples h4 { - position: initial; - display: block; - padding: 5px 10px; - margin: 0; - border: 0px solid @gray-4; - font-weight: normal; - font-size: 12px; -} - -.samples > li { - padding: 6px 20px; - cursor: pointer; -} - -.samples > li:hover, .samples > li.hover { - background: @grayLighter; -} - -.samples > li ul { - display:table; - width:100%; -} - -.samples > li ul > li { - display: table-cell; - height: 15px; - padding: 0; -} - -/*************************************************/ -/* canvas designer panel */ -/*************************************************/ - -h4.panel-title { - cursor:pointer; - background-color: @grayLighter; - color: @grayMed; -} - -.editor-category{ - margin: 5px 10px; - border: 1px solid @grayLight; -} - -.canvasdesigner-panel-container { - padding: 10px 10px; -} - -.canvasdesigner-panel-property { - clear: both; - overflow: hidden; - margin: 0 0 10px 0; -} - -.canvasdesigner .box-slider { - padding: 0px 0px 6px 0px; - overflow: hidden; - clear: both; -} - -.field-title { - float: left; - margin-right: 10px; - font-size: 12px; - color: @grayLight; -} - -.div-field { - margin-bottom: 10px; - overflow: hidden; - clear: both; -} - -/*************************************************/ -/* font family picker */ -/*************************************************/ - -.fontFamilyPickerPreview { - float: left; - width: 90%; - padding: 8px; - margin-top: 4px; - clear: both; - font-size: 18px; - color: @gray-8; - cursor: pointer; - border: 1px solid @gray-8; - text-align: center; - position: relative; -} - -.fontFamilyPickerPreview span { - font-size: 32px; - line-height: 32px; -} - -.fontPickerDelete { - position: absolute; - margin: 5px 0 0 -15px; - cursor: pointer; - color: @gray-8; - right: 0; - top: 0; -} - -.fontFamilyPickerPreview:hover { - border: 1px solid @grayIcon; - color: @grayIcon; -} - -.fontFamilyPickerPreview:hover .fontPickerDelete { - color: @grayIcon; -} - -.canvasdesigner-fontfamilypicker { - margin-bottom:30px; -} - -.canvasdesigner-fontfamilypicker select { - font-size: 12px; - padding: 2px; -} - -.canvasdesigner-fontfamilypicker select.font-list { - width:170px; -} - -.canvasdesigner-fontfamilypicker select.variant-list { - width:60px; -} - -.canvasdesigner-fontfamilypicker { - font-size: 42px; - line-height: 50px; - margin-bottom:40px; -} - -/*************************************************/ -/* slider */ -/*************************************************/ - -.canvasdesigner .ui-widget-content { - background: rgba(0, 0, 0, 0.27) !important; - border: 0 solid @white !important; - border-radius: 1px !important; -} - -.canvasdesigner .ui-slider-horizontal { - margin: 14px 11px 0 11px; -} - -.ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, -.ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { - border: 1px solid @gray-3 !important; - background: @gray-3 !important; - background-color:none !important; - outline:none; -} - -.canvasdesigner .ui-slider .ui-slider-handle:hover, -.canvasdesigner .ui-slider .ui-slider-handle:focus { - color: @grayLight !important; -} - -.canvasdesigner .ui-slider .ui-slider-handle span { - position: absolute !important; - margin: -15px 0 0 -8px !important; - color: @gray-3 !important; - font-size: 9px !important; - text-align: -webkit-center !important; - display: block !important; - width: 30px !important; -} - -.canvasdesigner .slider-input { - float: right; - width: 25px; - padding: 0; - margin-top: -9px; - margin-right: 1px; - font-size: 12px; - color: @grayLight; - text-align: right; - background-color: transparent; - border: none; - -} - -@-moz-document url-prefix() { - .canvasdesigner .slider-input { - margin-top: -6px; - } -} - -.canvasdesigner .sp-replacer { - padding: 0; - margin: 0; - display: block; - border: none; - height: 26px; - border-radius: 1px; - border: 1px solid @gray-8; -} - -.canvasdesigner .sp-replacer:hover { - border: 1px solid @grayIcon; -} - -.canvasdesigner .panel-body { - border-top: none !important; -} - -.canvasdesigner select { - font-size: 12px; -} - -.canvasdesigner .sp-dd { - display: none; -} - -.canvasdesigner .sp-preview { - width: 100%; - height: 100%; - margin-right: 0; - border: none; - display: block; -} - -.canvasdesigner .color-picker-preview { - height: 26px; - border: 1px solid @gray-8; - border-radius: 1px; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); - cursor: pointer; -} - -.canvasdesigner .float-panel .sp-container.sp-flat { - background-color: transparent; - border: none; - padding: 10px; - width: 100%; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-picker-container { - padding: 0; - margin: 0px; - margin-bottom: 10px; - border: none; - width: 100%; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-palette-container { - padding: 0; - height: 32px; - width: 100%; - float: left; - margin: 0; - border: none; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-button-container { - display: none; -} - -.colorPickerDelete { - position: absolute; - margin: -23px 0 0 0; - cursor: pointer; -} - -.borderStyleSelect -{ display: inline-block; - float: right; - width: 75px; - height: 28px; + + .dropdown-menu { + display:none; + + position: absolute; + float: left; + min-width: 320px; + + background-color: @blueExtraDark; + + > button { + display: block; + text-align: left; + + &.--active { + &::after { + content: ''; + position: absolute; + left:0; + width: 4px; + top: 0; + bottom: 0; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + background-color: @pinkLight; + } + } + } + } + + &:hover, &:focus, &:focus-within { + .dropdown-menu { + display:block; + } + } + } + + + /*************************************************/ /* IFrame size */ /*************************************************/ -.desktop { +#demo-iframe-wrapper { + transition: all 240ms cubic-bezier(0.165, 0.84, 0.44, 1); +} + +.fullsize { width: 100%; height: 100%; margin: 0 auto; overflow: hidden; } +.desktop { + width: 1920px; + height: 1080px; +} + .laptop { width: 1366px; height: 768px; @@ -763,13 +189,13 @@ h4.panel-title { height: 360px; } -.border { +.shadow { margin: 75px auto; background-color: @white; - border-radius: 10px; + border-radius: 3px; + overflow: hidden; opacity: 1.0; - box-shadow: 0 0 0 29px @gray-9, 0 0 0 30px @gray-8; - transition: all 0.5s ease-in-out; + box-shadow: 0 5px 20px 0 rgba(0,0,0,.26); } iframe { @@ -787,251 +213,3 @@ iframe { .flip:before { transform: rotate(90deg); } - -/*************************************************/ -/* Image picker */ -/*************************************************/ - -.imagePickerPreview { - height: 20px; - text-align: center; - background-color: @white; - padding: 6px 0 0 0; - cursor: pointer; - background-size: cover; - border-radius: 1px; - border: 1px solid @gray-8; - color: @gray-8; -} - -.sp-clear-display { - background-image: none !important; -} - -.imagePickerPreview:hover { - border: 1px solid @grayIcon; -} - -.imagePickerPreview:hover i { - color: @grayIcon; -} - -.imagePickerPreview i { - font-size:24px; - color: @gray-8; -} - -.canvasdesignerImagePicker { - padding: 0; - margin-top: 10px; - overflow: auto; - text-align: left; - list-style: none; -} - -.canvasdesignerImagePicker ul { - padding: 0; - margin: 0; - margin-left: 20px; - list-style: none; -} - -.canvasdesignerImagePicker li { - display: inline-block; - margin-bottom: 10px; -} - -.canvasdesignerImagePicker ul.media-items li { - margin-right: 20px; -} - -.canvasdesignerImagePicker .media-preview { - position: relative; - display: inline-block; - width: 91px; - height: 78px; - cursor: pointer; - background-position: center center; - background-size: cover; - border: 2px solid @gray-10; -} - -.canvasdesignerImagePicker .media-preview .folder { - position: absolute; - top: 30px; - left: 0; - width: 100%; - font-size: 40px; - color: @grayLight; - text-align: center; -} - -.canvasdesignerImagePicker .media-preview .folder-name { - margin-top: -5px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 0; - color: grey; -} - -.canvasdesignerImagePicker .media-preview:hover, -.media-preview.selected { - border: 2px solid @blueLight; -} - -.canvasdesignerImagePicker .modal-dialog { - width: 618px; - font-weight: normal; -} - -.bodyCanvasdesignerImagePicker .breadcrumb { - margin-top: 4px; - margin-bottom: 10px; - font-size: 16px; - text-align: left; -} - -.bodyCanvasdesignerImagePicker .breadcrumb > li { - display: inline; -} - -.breadcrumb{ - font-size: 12px; -} - -.bodyCanvasdesignerImagePicker .breadcrumb > li a { - cursor: pointer; -} - -.bodyCanvasdesignerImagePicker .breadcrumb input { - padding: 0; - margin: -4px -4px; -} - -.bodyCanvasdesignerImagePicker .fileinput-button { - position: absolute; - top: 10px; - right: 10px; - font-size: 19px; -} - -.bodyCanvasdesignerImagePicker input.input-media { - position: absolute; - top: 0; - right: 0; - margin: 0; - font-size: 23px; - cursor: pointer; - opacity: 0; - transform: translate(-300px, 0) scale(4); - direction: ltr; -} - -.bodyCanvasdesignerImagePicker .breadcrumb a.disabled, -.bodyCanvasdesignerImagePicker .breadcrumb a.disabled:hover { - color: @gray; - text-decoration: none; - cursor: default; -} - -.canvasdesignerImagePicker h3 { - font-size: 18px; - color: @gray; -} - -/*************************************************/ -/* Border editor */ -/*************************************************/ - -.box-preview { - display:inline-block; -} - -.box-preview li { - height: 30px; - width: 30px; - border-radius: 1px; - border: none; - display: inline-block; - background-color: @gray-3; - cursor: pointer; - margin-right: 6px; - position: relative; -} - -.box-preview li:last-child{ - margin-right: 0px; -} - -.box-preview li.selected { - border-color: @greenIcon !important; -} - -.box-preview li.border-all { - border: 6px solid @gray-7; - height: 18px; - width: 18px; - margin-left: 0px -} - -.box-preview li.border-left { - border-left: 6px solid @gray-7; - width: 24px; -} - -.box-preview li.border-right { - border-right: 6px solid @gray-7; - width: 24px; -} - -.box-preview li.border-top { - border-top: 6px solid @gray-7; - height: 24px; -} - -.box-preview li.border-bottom { - border-bottom: 6px solid @gray-7; - height: 24px; -} - -.bordereditor .color-picker-preview { - display: inline-block; - width: 120px; - float: left; -} - -/*************************************************/ -/* Radius editor */ -/*************************************************/ - -.radius-top-left, .radius-top-right, .radius-bottom-left, .radius-bottom-right { - display: block; - position: absolute; - background-color: @gray-7; - width: 7px; - height: 7px; -} - -.box-preview li.selected span { - background-color: @greenIcon; -} - -.radius-top-left{ - top:0; - left:0; -} - -.radius-top-right{ - top:0; - right:0; -} - -.radius-bottom-left{ - bottom:0; - left:0; -} - -.radius-bottom-right{ - bottom:0; - right:0 -} diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index c4cb821818..3bad5ee110 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -7,6 +7,10 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi .controller("previewController", function ($scope, $window, $location) { + + console.log($scope); + + //gets a real query string value function getParameterByName(name, url) { if (!url) url = $window.location.href; @@ -75,12 +79,13 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.valueAreLoaded = false; $scope.devices = [ - { name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" }, - { name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop", title: "Laptop" }, - { name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad", title: "Tablet portrait" }, - { name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip", title: "Tablet landscape" }, - { name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone", title: "Smartphone portrait" }, - { name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip", title: "Smartphone landscape" } + { name: "fullsize", css: "fullsize", icon: "icon-application-window-alt", title: "Browser" }, + { name: "desktop", css: "desktop shadow", icon: "icon-display", title: "Desktop" }, + { name: "laptop - 1366px", css: "laptop shadow", icon: "icon-laptop", title: "Laptop" }, + { name: "iPad portrait - 768px", css: "iPad-portrait shadow", icon: "icon-ipad", title: "Tablet portrait" }, + { name: "iPad landscape - 1024px", css: "iPad-landscape shadow", icon: "icon-ipad flip", title: "Tablet landscape" }, + { name: "smartphone portrait - 480px", css: "smartphone-portrait shadow", icon: "icon-iphone", title: "Smartphone portrait" }, + { name: "smartphone landscape - 320px", css: "smartphone-landscape shadow", icon: "icon-iphone flip", title: "Smartphone landscape" } ]; $scope.previewDevice = $scope.devices[0]; @@ -189,4 +194,15 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi .config(function ($locationProvider) { $locationProvider.html5Mode(false); //turn html5 mode off $locationProvider.hashPrefix(''); - }); + }) + + .controller('previewDropdownMenuController', + function ($element, $scope, angularHelper) { + + var vm = this; + + vm.onItemClicked = function (item) { + console.log("clicked", item) + }; + } + ) diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index 65f77dbf85..fb973e4b21 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -1,72 +1,82 @@ -@using Umbraco.Core -@using ClientDependency.Core -@using ClientDependency.Core.Mvc -@using Umbraco.Core.IO -@using Umbraco.Web -@using Umbraco.Core.Configuration - -@inherits System.Web.Mvc.WebViewPage -@{ - var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant(); - - Html - .RequiresCss("assets/css/canvasdesigner.css", "Umbraco"); -} - - - - - Umbraco Preview - - - - @Html.RenderCssHere( - new BasicPath("Umbraco", IOHelper.ResolveUrl(SystemDirectories.Umbraco))) - - - -
      - - @if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false) - { - @Html.Partial(Model.PreviewExtendedHeaderView) - } - -
      - -
      -
      -
      -
      - -
      -
        -
      • - -
      • - - @if (Model.Languages != null && Model.Languages.Count() > 1) - { - foreach (var previewLink in Model.Languages) - { -
      • - @previewLink.CultureName -
      • - } - } - -
      • - -
      • -
      -
      - -
      - - - - - - +@using Umbraco.Core +@using ClientDependency.Core +@using ClientDependency.Core.Mvc +@using Umbraco.Core.IO +@using Umbraco.Web +@using Umbraco.Core.Configuration + +@inherits System.Web.Mvc.WebViewPage +@{ + var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant(); + + Html.RequiresCss("assets/css/canvasdesigner.css", "Umbraco"); +} + + + + + Umbraco Preview + + + + @Html.RenderCssHere( + new BasicPath("Umbraco", IOHelper.ResolveUrl(SystemDirectories.Umbraco))) + + + +
      + + @if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false) + { + @Html.Partial(Model.PreviewExtendedHeaderView) + } + +
      + +
      +
      + + +
      + + + + + + From 86c6fd5873ce2aa20120cd558f0085f2d26c9382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Dec 2019 14:07:09 +0100 Subject: [PATCH 163/173] dropdown menus for preview --- .../src/less/canvas-designer.less | 43 +++++--- .../src/preview/preview.controller.js | 98 +++++++++++++++---- .../Umbraco/Views/Preview/Index.cshtml | 20 ++-- 3 files changed, 122 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index ce535d796a..8f4f02089d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -1,6 +1,7 @@ @import "helveticons.less"; @import "variables.less"; +@import "application/umb-outline.less"; /******* font-face *******/ @@ -43,7 +44,7 @@ body { } -.menu-bar--right-part { +.menu-bar__right-part { float: right; display: flex; flex: row; @@ -53,13 +54,13 @@ body { } } -.menu-bar--title { +.menu-bar__title { display: inline-block; padding: 11px 15px; font-weight: bold; } -.menu-bar--button { +.menu-bar__button { display: inline-block; padding: 11px 15px; border:none; @@ -73,7 +74,7 @@ body { .icon { margin-right: 10px; - font-size: 14px; + font-size: 18px; vertical-align: middle; } @@ -83,8 +84,8 @@ body { > svg { display: inline-block; - width: 12px; - height: 12px; + width: 14px; + height: 14px; fill: #fff; margin-right: 10px; vertical-align: middle; @@ -93,6 +94,8 @@ body { &:hover { background-color: lighten(@blueExtraDark, 4%); + } + &.--active { color: @pinkLight; > svg { fill: @pinkLight; @@ -104,25 +107,34 @@ body { display: inline-block; + > .menu-bar__button { + position: relative; + } + .dropdown-menu { display:none; position: absolute; float: left; - min-width: 320px; + min-width: 200px; + + border-radius: 0 3px 3px 3px; + overflow: hidden; background-color: @blueExtraDark; > button { - display: block; + position: relative; + display: list-item; text-align: left; + width: 100%; &.--active { - &::after { + &::before { content: ''; position: absolute; left:0; - width: 4px; + width: 3px; top: 0; bottom: 0; border-top-right-radius: 3px; @@ -133,9 +145,16 @@ body { } } - &:hover, &:focus, &:focus-within { + &.--open { + z-index:1; + box-shadow: 0 5px 10px 0 rgba(0,0,0,.26); + > .menu-bar__button { + z-index: @zindexDropdown + 1; + } .dropdown-menu { display:block; + z-index: @zindexDropdown; + box-shadow: 0 5px 10px 0 rgba(0,0,0,.26); } } @@ -190,7 +209,7 @@ body { } .shadow { - margin: 75px auto; + margin: 10px auto; background-color: @white; border-radius: 3px; overflow: hidden; diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 3bad5ee110..40cfc18ab7 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -7,8 +7,29 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi .controller("previewController", function ($scope, $window, $location) { + $scope.currentCulture = {iso:'', title:'...', icon:'icon-loading'} + var cultures = []; - console.log($scope); + $scope.tabbingActive = false; + // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. + // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 + function handleFirstTab(evt) { + if (evt.keyCode === 9) { + $scope.tabbingActive = true; + $scope.$digest(); + window.removeEventListener('keydown', handleFirstTab); + window.addEventListener('mousedown', disableTabbingActive); + } + } + + function disableTabbingActive(evt) { + $scope.tabbingActive = false; + $scope.$digest(); + window.removeEventListener('mousedown', disableTabbingActive); + window.addEventListener("keydown", handleFirstTab); + } + + window.addEventListener("keydown", handleFirstTab); //gets a real query string value @@ -89,6 +110,45 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi ]; $scope.previewDevice = $scope.devices[0]; + $scope.sizeOpen = false; + $scope.cultureOpen = false; + + $scope.toggleSizeOpen = function() { + $scope.sizeOpen = toggleMenu($scope.sizeOpen); + } + $scope.toggleCultureOpen = function() { + $scope.cultureOpen = toggleMenu($scope.cultureOpen); + } + + function toggleMenu(isCurrentlyOpen) { + if (isCurrentlyOpen === false) { + closeOthers(); + return true; + } else { + return false; + } + } + function closeOthers() { + $scope.sizeOpen = false; + $scope.cultureOpen = false; + } + + $scope.windowClickHandler = function() { + closeOthers(); + } + function windowBlurHandler() { + closeOthers(); + $scope.$digest(); + } + + var win = angular.element($window); + + win.on("blur", windowBlurHandler); + + $scope.$on("$destroy", function () { + win.off("blur", handleBlwindowBlurHandlerur ); + }); + function setPageUrl(){ $scope.pageId = $location.search().id || getParameterByName("id"); @@ -128,6 +188,8 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.onFrameLoaded = function (iframe) { $scope.frameLoaded = true; configureSignalR(iframe); + + $scope.currentCultureIso = $location.search().culture; }; /*****************************************************************************/ @@ -141,17 +203,30 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi /*****************************************************************************/ /* Change culture */ /*****************************************************************************/ - $scope.changeCulture = function (culture) { - if($location.search().culture !== culture){ + $scope.changeCulture = function (iso) { + if($location.search().culture !== iso) { $scope.frameLoaded = false; - $location.search("culture", culture); + $scope.currentCultureIso = iso; + $location.search("culture", iso); setPageUrl(); } }; - + /* $scope.isCurrentCulture = function(culture) { return $location.search().culture === culture; } + */ + $scope.registerCulture = function(iso, title, icon) { + var cultureObject = {iso: iso, title: title, icon: icon}; + cultures.push(cultureObject); + } + + $scope.$watch("currentCultureIso", function(oldIso, newIso) { + $scope.currentCulture = cultures.find(function(culture) { + return culture.iso === $scope.currentCultureIso; + }) + }); + }) @@ -194,15 +269,4 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi .config(function ($locationProvider) { $locationProvider.html5Mode(false); //turn html5 mode off $locationProvider.hashPrefix(''); - }) - - .controller('previewDropdownMenuController', - function ($element, $scope, angularHelper) { - - var vm = this; - - vm.onItemClicked = function (item) { - console.log("clicked", item) - }; - } - ) + }); diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index fb973e4b21..e14a459d9d 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -23,7 +23,7 @@ new BasicPath("Umbraco", IOHelper.ResolveUrl(SystemDirectories.Umbraco))) - +
      @if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false) @@ -37,29 +37,29 @@
      - -
      - - + } From 0d0f8e6beb704a18fa8a460c588d471f6d3d65a6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Dec 2019 11:08:15 +1100 Subject: [PATCH 172/173] adds debug timing --- src/Umbraco.Core/Runtime/CoreRuntime.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index e9e37c871d..5ceb89d7fb 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -170,7 +170,13 @@ namespace Umbraco.Core.Runtime // get composers, and compose var composerTypes = ResolveComposerTypes(typeLoader); - var enableDisableAttributes = typeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute)); + + IEnumerable enableDisableAttributes; + using (ProfilingLogger.DebugDuration("Scanning enable/disable composer attributes")) + { + enableDisableAttributes = typeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute)); + } + var composers = new Composers(composition, composerTypes, enableDisableAttributes, ProfilingLogger); composers.Compose(); From 63429dadb7657049576c5ab7b7397dc46216324e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Dec 2019 15:01:33 +1100 Subject: [PATCH 173/173] Adds notes, changes values to constants --- .../PropertyEditors/IDataEditorWithMediaPath.cs | 12 ++++++++++++ .../TestHelpers/Entities/MockedContentTypes.cs | 16 ++++++++-------- .../Routing/DefaultMediaUrlProvider.cs | 2 ++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs index b16dbbaa25..e8af1b0ac3 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditorWithMediaPath.cs @@ -1,7 +1,19 @@ namespace Umbraco.Core.PropertyEditors { + /// + /// Must be implemented by property editors that store media and return media paths + /// + /// + /// Currently there are only 2x core editors that do this: upload and image cropper. + /// It would be possible for developers to know implement their own media property editors whereas previously this was not possible. + /// public interface IDataEditorWithMediaPath { + /// + /// Returns the media path for the value stored for a property + /// + /// + /// string GetMediaPath(object value); } } diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 41786a5fd7..c55467431d 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -420,10 +420,10 @@ namespace Umbraco.Tests.TestHelpers.Entities var contentCollection = new PropertyTypeCollection(false); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.UploadField, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); @@ -449,10 +449,10 @@ namespace Umbraco.Tests.TestHelpers.Entities var contentCollection = new PropertyTypeCollection(false); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.ImageCropper, ValueStorageType.Ntext) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = 1043 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -92 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); diff --git a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs index dacd314f94..89abde0576 100644 --- a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs @@ -27,6 +27,8 @@ namespace Umbraco.Web.Routing string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); + + // get the raw source value since this is what is used by IDataEditorWithMediaPath for processing var value = prop?.GetSourceValue(culture); if (value == null) {