Merge remote-tracking branch 'origin/netcore/dev' into netcore/netcore

# Conflicts:
#	build/NuSpecs/UmbracoCms.Web.nuspec
#	src/SolutionInfo.cs
#	src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
#	src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs
#	src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts
#	src/Umbraco.Tests.AcceptanceTest/package.json
#	src/Umbraco.Tests/TestHelpers/TestObjects.cs
#	src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs
#	src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs
#	src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
#	src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
#	src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs
#	src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js
#	src/Umbraco.Web.UI.Client/src/installer/steps/database.html
#	src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less
#	src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less
#	src/Umbraco.Web.UI.Client/src/less/forms.less
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html
#	src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html
#	src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-actions.html
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html
#	src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html
#	src/Umbraco.Web.UI.Client/src/views/dictionary/list.html
#	src/Umbraco.Web.UI.Client/src/views/logViewer/search.html
#	src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/layoutconfig.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js
#	src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml
#	src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml
#	src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml
#	src/Umbraco.Web.UI/config/umbracoSettings.config
#	src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
#	src/Umbraco.Web/Editors/DictionaryController.cs
#	src/Umbraco.Web/Editors/LogController.cs
#	src/Umbraco.Web/Editors/MediaTypeController.cs
#	src/Umbraco.Web/Editors/MemberGroupController.cs
This commit is contained in:
Bjarke Berg
2020-09-09 19:13:37 +02:00
200 changed files with 5507 additions and 3661 deletions

View File

@@ -14,6 +14,14 @@ namespace Umbraco.Core
{
public static class ContentExtensions
{
/// <summary>
/// Returns all properties based on the editorAlias
/// </summary>
/// <param name="content"></param>
/// <param name="editorAlias"></param>
/// <returns></returns>
public static IEnumerable<IProperty> GetPropertiesByEditor(this IContentBase content, string editorAlias)
=> content.Properties.Where(x => x.PropertyType.PropertyEditorAlias == editorAlias);
internal static bool IsMoving(this IContentBase entity)
{
@@ -28,29 +36,6 @@ namespace Umbraco.Core
return isMoving;
}
/// <summary>
/// Removes characters that are not valid XML characters from all entity properties
/// of type string. See: http://stackoverflow.com/a/961504/5018
/// </summary>
/// <returns></returns>
/// <remarks>
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
/// </remarks>
/// <param name="entity"></param>
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
{
entity.Name = entity.Name.ToValidXmlString();
foreach (var property in entity.Properties)
{
foreach (var propertyValue in property.Values)
{
if (propertyValue.EditedValue is string editString)
propertyValue.EditedValue = editString.ToValidXmlString();
if (propertyValue.PublishedValue is string publishedString)
propertyValue.PublishedValue = publishedString.ToValidXmlString();
}
}
}
#region IContent

View File

@@ -18,10 +18,35 @@ namespace Umbraco.Core.Models.Blocks
_propertyEditorAlias = propertyEditorAlias;
}
public BlockEditorData ConvertFrom(JToken json)
{
var value = json.ToObject<BlockValue>();
return Convert(value);
}
public bool TryDeserialize(string json, out BlockEditorData blockEditorData)
{
try
{
var value = JsonConvert.DeserializeObject<BlockValue>(json);
blockEditorData = Convert(value);
return true;
}
catch (System.Exception)
{
blockEditorData = null;
return false;
}
}
public BlockEditorData Deserialize(string json)
{
var value = JsonConvert.DeserializeObject<BlockValue>(json);
return Convert(value);
}
private BlockEditorData Convert(BlockValue value)
{
if (value.Layout == null)
return BlockEditorData.Empty;

View File

@@ -12,7 +12,7 @@ namespace Umbraco.Core.Models.Blocks
[JsonConverter(typeof(UdiJsonConverter))]
public Udi ContentUdi { get; set; }
[JsonProperty("settingsUdi")]
[JsonProperty("settingsUdi", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(UdiJsonConverter))]
public Udi SettingsUdi { get; set; }
}

View File

@@ -1,64 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Models.Blocks
{
/// <summary>
/// The strongly typed model for the Block List editor
/// The strongly typed model for the Block List editor.
/// </summary>
/// <seealso cref="System.Collections.ObjectModel.ReadOnlyCollection{Umbraco.Core.Models.Blocks.BlockListItem}" />
[DataContract(Name = "blockList", Namespace = "")]
public class BlockListModel : IReadOnlyList<BlockListItem>
public class BlockListModel : ReadOnlyCollection<BlockListItem>
{
private readonly IReadOnlyList<BlockListItem> _layout = new List<BlockListItem>();
/// <summary>
/// Gets the empty <see cref="BlockListModel" />.
/// </summary>
/// <value>
/// The empty <see cref="BlockListModel" />.
/// </value>
public static BlockListModel Empty { get; } = new BlockListModel();
/// <summary>
/// Prevents a default instance of the <see cref="BlockListModel" /> class from being created.
/// </summary>
private BlockListModel()
{
}
public BlockListModel(IEnumerable<BlockListItem> layout)
{
_layout = layout.ToList();
}
public int Count => _layout.Count;
: this(new List<BlockListItem>())
{ }
/// <summary>
/// Get the block by index
/// Initializes a new instance of the <see cref="BlockListModel" /> class.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public BlockListItem this[int index] => _layout[index];
/// <param name="list">The list to wrap.</param>
public BlockListModel(IList<BlockListItem> list)
: base(list)
{ }
/// <summary>
/// Get the block by content Guid
/// Gets the <see cref="BlockListItem" /> with the specified content key.
/// </summary>
/// <param name="contentKey"></param>
/// <returns></returns>
public BlockListItem this[Guid contentKey] => _layout.FirstOrDefault(x => x.Content.Key == contentKey);
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentKey">The content key.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content key.
/// </returns>
public BlockListItem this[Guid contentKey] => this.FirstOrDefault(x => x.Content.Key == contentKey);
/// <summary>
/// Get the block by content element Udi
/// Gets the <see cref="BlockListItem" /> with the specified content UDI.
/// </summary>
/// <param name="contentUdi"></param>
/// <returns></returns>
public BlockListItem this[Udi contentUdi]
{
get
{
if (!(contentUdi is GuidUdi guidUdi)) return null;
return _layout.FirstOrDefault(x => x.Content.Key == guidUdi.Guid);
}
}
public IEnumerator<BlockListItem> GetEnumerator() => _layout.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentUdi">The content UDI.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content UDI.
/// </returns>
public BlockListItem this[Udi contentUdi] => contentUdi is GuidUdi guidUdi ? this.FirstOrDefault(x => x.Content.Key == guidUdi.Guid) : null;
}
}

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System;
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Models.Blocks
{
@@ -12,26 +12,16 @@ namespace Umbraco.Core.Models.Blocks
}
public Udi ContentUdi { get; }
public Udi SettingsUdi { get; }
public override bool Equals(object obj)
{
return obj is ContentAndSettingsReference reference && Equals(reference);
}
public override bool Equals(object obj) => obj is ContentAndSettingsReference reference && Equals(reference);
public bool Equals(ContentAndSettingsReference other)
{
return EqualityComparer<Udi>.Default.Equals(ContentUdi, other.ContentUdi) &&
EqualityComparer<Udi>.Default.Equals(SettingsUdi, other.SettingsUdi);
}
public bool Equals(ContentAndSettingsReference other) => other != null
&& EqualityComparer<Udi>.Default.Equals(ContentUdi, other.ContentUdi)
&& EqualityComparer<Udi>.Default.Equals(SettingsUdi, other.SettingsUdi);
public override int GetHashCode()
{
var hashCode = 272556606;
hashCode = hashCode * -1521134295 + EqualityComparer<Udi>.Default.GetHashCode(ContentUdi);
hashCode = hashCode * -1521134295 + EqualityComparer<Udi>.Default.GetHashCode(SettingsUdi);
return hashCode;
}
public override int GetHashCode() => (ContentUdi, SettingsUdi).GetHashCode();
public static bool operator ==(ContentAndSettingsReference left, ContentAndSettingsReference right)
{

View File

@@ -7,6 +7,7 @@
public enum SpecialDbTypes
{
NTEXT,
NCHAR
NCHAR,
NVARCHARMAX
}
}

View File

@@ -79,6 +79,9 @@ namespace Umbraco.Core.Persistence
case SpecialDbTypes.NCHAR:
sqlDbType = SqlDbType.NChar;
break;
case SpecialDbTypes.NVARCHARMAX:
sqlDbType = SqlDbType.NVarChar;
break;
default:
throw new ArgumentOutOfRangeException();
}

View File

@@ -132,7 +132,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (objectTypes.Any())
{
sql = sql.Where("umbracoNode.nodeObjectType IN (@objectTypes)", objectTypes);
sql = sql.Where("umbracoNode.nodeObjectType IN (@objectTypes)", new { objectTypes = objectTypes });
}
return Database.Fetch<string>(sql);

View File

@@ -1203,7 +1203,7 @@ AND umbracoNode.id <> @id",
{
// first clear dependencies
Database.Delete<TagRelationshipDto>("WHERE propertyTypeId = @Id", new { Id = propertyTypeId });
Database.Delete<PropertyDataDto>("WHERE propertytypeid = @Id", new { Id = propertyTypeId });
Database.Delete<PropertyDataDto>("WHERE propertyTypeId = @Id", new { Id = propertyTypeId });
// then delete the property type
Database.Delete<PropertyTypeDto>("WHERE contentTypeId = @Id AND id = @PropertyTypeId",

View File

@@ -198,7 +198,13 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return "NCHAR";
}
else if (dbTypes == SpecialDbTypes.NTEXT)
{
return "NTEXT";
}
else if (dbTypes == SpecialDbTypes.NVARCHARMAX)
{
return "NVARCHAR(MAX)";
}
return "NVARCHAR";
}

View File

@@ -4,14 +4,11 @@ using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// The configuration object for the Block List editor
/// </summary>
public class BlockListConfiguration
{
[ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html", Description = "Define the available blocks.")]
public BlockConfiguration[] Blocks { get; set; }
@@ -61,7 +58,7 @@ namespace Umbraco.Web.PropertyEditors
public int? Max { get; set; }
}
[ConfigurationField("useLiveEditing", "Live editing mode", "boolean", Description = "Live editing in editor overlays for live updated custom views.")]
[ConfigurationField("useLiveEditing", "Live editing mode", "boolean", Description = "Live editing in editor overlays for live updated custom views or labels using custom expression.")]
public bool UseLiveEditing { get; set; }
[ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view.")]
@@ -69,7 +66,5 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("maxPropertyWidth", "Property editor width", "textstring", Description = "optional css overwrite, example: 800px or 100%")]
public string MaxPropertyWidth { get; set; }
}
}

View File

@@ -1,11 +1,4 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.IO;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
@@ -14,6 +7,8 @@ namespace Umbraco.Web.PropertyEditors
{
public BlockListConfigurationEditor(IIOHelper ioHelper) : base(ioHelper)
{
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Utility class for dealing with <see cref="ContentService"/> Copying/Saving events for complex editors
/// </summary>
public class ComplexPropertyEditorContentEventHandler : IDisposable
{
private readonly string _editorAlias;
private readonly Func<string, bool, string> _formatPropertyValue;
private bool _disposedValue;
public ComplexPropertyEditorContentEventHandler(string editorAlias,
Func<string, bool, string> formatPropertyValue)
{
_editorAlias = editorAlias;
_formatPropertyValue = formatPropertyValue;
ContentService.Copying += ContentService_Copying;
ContentService.Saving += ContentService_Saving;
}
/// <summary>
/// <see cref="ContentService"/> Copying event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContentService_Copying(IContentService sender, CopyEventArgs<IContent> e)
{
var props = e.Copy.GetPropertiesByEditor(_editorAlias);
UpdatePropertyValues(props, false);
}
/// <summary>
/// <see cref="ContentService"/> Saving event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e)
{
foreach (var entity in e.SavedEntities)
{
var props = entity.GetPropertiesByEditor(_editorAlias);
UpdatePropertyValues(props, true);
}
}
private void UpdatePropertyValues(IEnumerable<IProperty> props, bool onlyMissingKeys)
{
foreach (var prop in props)
{
// A Property may have one or more values due to cultures
var propVals = prop.Values;
foreach (var cultureVal in propVals)
{
// Remove keys from published value & any nested properties
var updatedPublishedVal = _formatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys);
cultureVal.PublishedValue = updatedPublishedVal;
// Remove keys from edited/draft value & any nested properties
var updatedEditedVal = _formatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys);
cultureVal.EditedValue = updatedEditedVal;
}
}
}
/// <summary>
/// Unbinds from events
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
ContentService.Copying -= ContentService_Copying;
ContentService.Saving -= ContentService_Saving;
}
_disposedValue = true;
}
}
/// <summary>
/// Unbinds from events
/// </summary>
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -95,7 +95,6 @@ namespace Umbraco.Web.PropertyEditors
_contentTypes = new Lazy<Dictionary<string, IContentType>>(() =>
_contentTypeService.GetAll().ToDictionary(c => c.Alias)
);
}
/// <inheritdoc />

View File

@@ -120,7 +120,9 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
settingsData = null;
}
var layoutRef = new BlockListItem(contentGuidUdi, contentData, settingGuidUdi, settingsData);
var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsData?.GetType() ?? typeof(IPublishedElement));
var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData);
layout.Add(layoutRef);
}