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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
public enum SpecialDbTypes
|
||||
{
|
||||
NTEXT,
|
||||
NCHAR
|
||||
NCHAR,
|
||||
NVARCHARMAX
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,6 @@ namespace Umbraco.Web.PropertyEditors
|
||||
_contentTypes = new Lazy<Dictionary<string, IContentType>>(() =>
|
||||
_contentTypeService.GetAll().ToDictionary(c => c.Alias)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user