Merge branch 'temp8' of https://github.com/umbraco/Umbraco-CMS into temp8

This commit is contained in:
Shannon
2018-10-01 11:26:40 +02:00
18 changed files with 372 additions and 160 deletions

View File

@@ -40,7 +40,6 @@
<!-- these files are copied by install.ps1 -->
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="$BuildTmp$\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />

View File

@@ -6,7 +6,7 @@
/// </summary>
/// <remarks>Every strongly-typed property set class should inherit from <c>PublishedElementModel</c>
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
public class PublishedElementModel : PublishedElementWrapped
public abstract class PublishedElementModel : PublishedElementWrapped
{
/// <inheritdoc />
/// <summary>

View File

@@ -41,10 +41,17 @@
//We fetch all ancestors of the node to generate the footer breadcrumb navigation
if (!$scope.page.isNew) {
if (content.parentId && content.parentId !== -1) {
entityResource.getAncestors(content.id, "document")
entityResource.getAncestors(content.id, "document", $scope.culture)
.then(function (anc) {
$scope.ancestors = anc;
});
$scope.$watch('culture',
function(value, oldValue) {
entityResource.getAncestors(content.id, "document", value)
.then(function (anc) {
$scope.ancestors = anc;
});
});
}
}

View File

@@ -0,0 +1,27 @@
(function () {
/**
* @ngdoc directive
* @name umbraco.directives.directive:multi
* @restrict A
* @description Used on input fields when you want to validate multiple fields at once.
**/
function multi($parse, $rootScope) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModelCtrl) {
var validate = $parse(attrs.multi)(scope);
ngModelCtrl.$viewChangeListeners.push(function () {
// ngModelCtrl.$setValidity('multi', validate());
$rootScope.$broadcast('multi:valueChanged');
});
var deregisterListener = scope.$on('multi:valueChanged', function (event) {
ngModelCtrl.$setValidity('multi', validate());
});
scope.$on('$destroy', deregisterListener); // optional, only required for $rootScope.$on
}
};
}
angular.module('umbraco.directives.validation').directive('multi', ['$parse', '$rootScope', multi]);
})();

View File

@@ -242,6 +242,44 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
{ id: id, culture: culture })),
'Failed to publish content with id ' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#getCultureAndDomains
* @methodOf umbraco.resources.contentResource
*
* @description
* Gets the culture and hostnames for a content item with the given Id
*
* ##usage
* <pre>
* contentResource.getCultureAndDomains(1234)
* .then(function(data) {
* alert(data.Domains, data.Language);
* });
* </pre>
* @param {Int} id the ID of the node to get the culture and domains for.
* @returns {Promise} resourcePromise object.
*
*/
getCultureAndDomains: function (id) {
if (!id) {
throw "id cannot be null";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetCultureAndDomains", { id: id })),
'Failed to retreive culture and hostnames for ' + id);
},
saveLanguageAndDomains: function (model) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"PostSaveLanguageAndDomains"),
model));
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#emptyRecycleBin
@@ -334,26 +372,26 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetById",
{ id: id })),
'Failed to retrieve data for content id ' + id)
.then(function(result) {
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetById",
{ id: id })),
'Failed to retrieve data for content id ' + id)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));
});
},
getBlueprintById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetBlueprintById",
[{ id: id }])),
'Failed to retrieve data for content id ' + id)
.then(function(result) {
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetBlueprintById",
[{ id: id }])),
'Failed to retrieve data for content id ' + id)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));
});
},
@@ -410,15 +448,15 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
});
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetByIds",
idQuery)),
'Failed to retrieve data for content with multiple ids')
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetByIds",
idQuery)),
'Failed to retrieve data for content with multiple ids')
.then(function (result) {
//each item needs to be re-formatted
_.each(result, function(r) {
_.each(result, function (r) {
umbDataFormatter.formatContentGetData(r)
});
return $q.when(result);
@@ -461,13 +499,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
getScaffold: function (parentId, alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }, { parentId: parentId }])),
'Failed to retrieve data for empty content item type ' + alias)
.then(function(result) {
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }, { parentId: parentId }])),
'Failed to retrieve data for empty content item type ' + alias)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));
});
},
@@ -475,13 +513,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
getBlueprintScaffold: function (parentId, blueprintId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetEmpty",
[{ blueprintId: blueprintId }, { parentId: parentId }])),
'Failed to retrieve blueprint for id ' + blueprintId)
.then(function(result) {
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetEmpty",
[{ blueprintId: blueprintId }, { parentId: parentId }])),
'Failed to retrieve blueprint for id ' + blueprintId)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));
});
},

View File

@@ -288,17 +288,19 @@ function entityResource($q, $http, umbRequestHelper) {
* Gets ancestor entities for a given item
*
*
* @param {string} type Object type name
* @param {string} type Object type name
* @param {string} culture Culture
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getAncestors: function (id, type) {
getAncestors: function (id, type, culture) {
if (culture === undefined) culture = "";
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetAncestors",
[{id: id}, {type: type}])),
[{ id: id }, { type: type }, { culture: culture }])),
'Failed to retrieve ancestor data for id ' + id);
},

View File

@@ -1,24 +1,70 @@
<div ng-controller="Umbraco.Editors.Content.AssignDomainController as vm">
<div class="umb-dialog-body">
<umb-pane>
<p>UI magic to be done...</p>
</umb-pane>
</div>
<div ng-controller="Umbraco.Editors.Content.AssignDomainController as vm" ng-cloak>
<div class="umb-dialog-body">
<umb-pane>
<h5 class="umb-pane-title"><localize key="assignDomain_setLanguage">Culture</localize></h5>
<label for="assignDomain_language" class="control-label"><localize key="general_language"></localize></label>
<select class="umb-property-editor umb-dropdown" name="language" id="assignDomain_language" ng-model="vm.language" ng-options="lang.name for lang in vm.languages">
<option value="">{{vm.inherit}}</option>
</select>
</umb-pane>
<umb-pane>
<h5 class="umb-pane-title"><localize key="assignDomain_setDomains">Domains</localize></h5>
<form name="vm.domainForm" novalidate="" ng-submit="vm.save()" class="control-group umb-control-group">
<small class="help-inline">
<localize key="assignDomain_domainHelp"></localize>
</small>
<div class="umb-el-wrap hidelabel">
<table class="table domains" ng-if="vm.domains.length > 0">
<thead>
<tr>
<th><localize key="assignDomain_domain"></localize></th>
<th><localize key="assignDomain_language"></localize></th>
<th />
</tr>
</thead>
<tbody>
<tr ng-repeat="domain in vm.domains">
<td>
<input type="text" ng-model="domain.Name" multi="vm.validateDomain" ng-pattern="vm.domainPattern" name="domain_name_{{$index}}" />
<span ng-if="vm.domainForm.$submitted" ng-messages="vm.domainForm['domain_name_' + $index].$error">
<span class="help-inline" ng-message="multi"><localize key="assignDomain_duplicateDomain"></localize></span>
<span class="help-inline" ng-message="pattern"><localize key="assignDomain_invalidDomain"></localize></span>
</span>
</td>
<td>
<select name="domain_language_{{$index}}" class="language" ng-model="domain.Lang" ng-options="lang.name for lang in vm.languages"></select>
</td>
<td>
<umb-button icon="icon-trash"
action="vm.removeDomain($index)"
type="button"
button-style="danger">
</umb-button>
</td>
</tr>
</tbody>
</table>
</div>
<umb-button label-key="assignDomain_addNew"
action="vm.addDomain()"
type="button">
</umb-button>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<umb-button label-key="general_cancel"
action="vm.closeDialog()"
type="button"
button-style="link">
</umb-button>
<umb-button
label-key="general_cancel"
action="vm.closeDialog()"
type="button"
button-style="link">
</umb-button>
<umb-button
label-key="buttons_save"
type="button"
button-style="success">
</umb-button>
<umb-button label-key="buttons_save"
type="submit"
button-style="success">
</umb-button>
</div>
</form>
</umb-pane>
</div>
</div>

View File

@@ -1,17 +1,126 @@
(function () {
"use strict";
function AssignDomainController($scope) {
function AssignDomainController($scope, localizationService, languageResource, contentResource) {
var vm = this;
vm.closeDialog = closeDialog;
function closeDialog() {
$scope.nav.hideDialog();
vm.addDomain = addDomain;
vm.removeDomain = removeDomain;
vm.save = save;
vm.validateDomain = validateDomain;
vm.languages = [];
vm.domains = [];
vm.language = null;
vm.domainPattern = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*|-)?$/gi; //TODO: This regex is not working as it should.
function activate() {
languageResource.getAll().then(function (langs) {
vm.languages = langs;
var defLang = langs.filter(function (l) {
return l.isDefault;
});
if (defLang.length > 0) {
vm.defaultLanguage = defLang[0];
}
else {
vm.defaultLanguage = langs[0];
}
getCultureAndDomains();
});
localizationService.localize("assignDomain_inherit").then(function (value) {
vm.inherit = value;
});
}
function getCultureAndDomains () {
contentResource.getCultureAndDomains($scope.currentNode.id)
.then(function (data) {
if (data.Language !== "undefined") {
var lang = vm.languages.filter(function (l) {
return matchLanguageById(l, data.Language.Id);
});
if (lang.length > 0) {
vm.language = lang[0];
}
}
vm.domains = data.Domains.map(function (d) {
var matchedLangs = vm.languages.filter(function (l) {
return matchLanguageById(l, d.Language);
});
return {
Name: d.Name,
Lang: matchedLangs.length > 0 ? matchedLangs[0] : vm.defaultLanguage
}
});
});
}
function matchLanguageById(language, id) {
return language.Id === id;
}
function closeDialog() {
$scope.nav.hideDialog();
}
function addDomain() {
vm.domains.push({
Name: '',
Lang: vm.defaultLanguage
});
}
function removeDomain(index) {
vm.domains.splice(index, 1);
}
function validateDomain() {
var valid = true, duplicateTest = {};
if (vm.domains.length > 1) {
vm.domains.map(function (d, index) {
if (d.Name in duplicateTest) {
valid = false;
}
else {
duplicateTest[d.Name] = index;
}
});
}
return valid;
}
function save() {
if (vm.domainForm.$valid) {
var data = {
NodeId: $scope.currentNode.id,
Domains: vm.domains.map(function (d) {
return {
Name: d.Name,
Lang: d.Lang.id
};
}),
Language: vm.language != null ? vm.language.id : 0
};
console.log(data);
contentResource.saveLanguageAndDomains(data).then(function () {
closeDialog();
}, function (e) {
console.log(e); //TODO: not sure how best to handle this case
});
}
else {
console.log('not valid');
}
}
activate();
}
angular.module("umbraco").controller("Umbraco.Editors.Content.AssignDomainController", AssignDomainController);
})();
})();

View File

@@ -279,7 +279,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
_.each($scope.model.config.contentTypes, function (contentType) {
contentResource.getScaffold(-20, contentType.ncAlias).then(function (scaffold) {
// remove all tabs except the specified tab
var tab = _.find(scaffold.tabs, function (tab) {
var tabs = scaffold.variants[0].tabs;
var tab = _.find(tabs, function (tab) {
return tab.id != 0 && (tab.alias.toLowerCase() == contentType.ncTabAlias.toLowerCase() || contentType.ncTabAlias == "");
});
scaffold.tabs = [];

View File

@@ -333,7 +333,6 @@
<Content Include="Umbraco\Developer\RelationTypes\RelationTypesWebService.asmx" />
<Content Include="Umbraco\Developer\RelationTypes\TreeMenu\ActionDeleteRelationType.js" />
<Content Include="Umbraco\Developer\RelationTypes\TreeMenu\ActionNewRelationType.js" />
<Content Include="Umbraco\Dialogs\AssignDomain2.aspx" />
<Content Include="Umbraco\Dialogs\ChangeDocType.aspx">
<SubType>ASPXCodeBehind</SubType>
</Content>

View File

@@ -1,79 +0,0 @@
<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="AssignDomain2.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.AssignDomain2" %>
<%@ Import Namespace="Umbraco.Web" %>
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %>
<asp:Content ContentPlaceHolderID="head" runat="server">
<umb:JsInclude runat="server" FilePath="Dialogs/AssignDomain2.js" PathNameAlias="UmbracoClient" />
<umb:JsInclude runat="server" FilePath="PunyCode/punycode.min.js" PathNameAlias="UmbracoClient" />
<umb:JsInclude runat="server" FilePath="/Umbraco/lib/jquery-validate/jquery.validate.min.js" PathNameAlias="UmbracoClient" />
<umb:CssInclude runat="server" FilePath="Dialogs/AssignDomain2.css" PathNameAlias="UmbracoClient" />
<script type="text/javascript">
(function ($) {
$(document).ready(function () {
var dialog = new Umbraco.Dialogs.AssignDomain2({
nodeId: <%=GetNodeId()%>,
restServiceLocation: '<%=GetRestServicePath() %>',
invalidDomain: '<%=Services.TextService.Localize("assignDomain/invalidDomain") %>',
duplicateDomain: '<%=Services.TextService.Localize("assignDomain/duplicateDomain") %>',
<asp:Literal runat="server" ID="data" />
});
dialog.init();
});
})(jQuery);
</script>
</asp:Content>
<asp:Content ContentPlaceHolderID="body" runat="server">
<div class="umb-dialog-body">
<cc1:Feedback ID="feedback" runat="server" />
<div id="komask"></div>
<cc1:Pane runat="server" ID="pane_language" cssClass="hide">
<cc1:PropertyPanel runat="server" ID="prop_language">
<select class="umb-property-editor umb-dropdown" name="language" data-bind="options: languages, optionsText: 'Code', optionsValue: 'Id', value: language, optionsCaption: '<%=Services.TextService.Localize("assignDomain/inherit") %> '"></select>
<!-- <small class="help-inline"><%=Services.TextService.Localize("assignDomain/setLanguageHelp") %></small>-->
</cc1:PropertyPanel>
</cc1:Pane>
<cc1:Pane runat="server" ID="pane_domains">
<small class="help-inline"><%=Services.TextService.Localize("assignDomain/domainHelp") %></small>
<cc1:PropertyPanel runat="server">
<table class="table domains" data-bind="visible: domains().length > 0">
<thead>
<tr>
<th><%=Services.TextService.Localize("assignDomain/domain") %></th>
<th><%=Services.TextService.Localize("assignDomain/language") %></th>
<th />
</tr>
</thead>
<tbody data-bind="foreach: domains">
<tr>
<td valign="top"><input class="domain duplicate" data-bind="value: Name, uniqueName: true" /><input type="hidden" value="" data-bind="uniqueName: true"/></td>
<td valign="top"><select class="language" data-bind="options: $parent.languages, optionsText: 'Code', optionsValue: 'Id', value: Lang, uniqueName: true"></select></td>
<td valign="top"><a href="#" class="btn btn-danger" data-bind="click: $parent.removeDomain"><i class="icon icon-trash"></i></a></td>
</tr>
</tbody>
</table>
</cc1:PropertyPanel>
<cc1:PropertyPanel runat="server">
<button class="btn" data-bind="click: addDomain"><%=Services.TextService.Localize("assignDomain/addNew") %></button>
</cc1:PropertyPanel>
</cc1:Pane>
</div>
<div runat="server" ID="p_buttons" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("general/cancel")%></a>
<button class="btn btn-primary" id="btnSave"><%=Services.TextService.Localize("buttons/save") %></button>
</div>
</asp:Content>

View File

@@ -56,6 +56,8 @@ namespace Umbraco.Web.Editors
private readonly PropertyEditorCollection _propertyEditors;
private readonly Lazy<IDictionary<string, ILanguage>> _allLangs;
public object Domains { get; private set; }
public ContentController(IPublishedSnapshotService publishedSnapshotService, PropertyEditorCollection propertyEditors)
{
if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService));
@@ -1146,6 +1148,18 @@ namespace Umbraco.Web.Editors
}
}
public ContentDomainsAndCulture GetCultureAndDomains(int id)
{
var nodeDomains = Services.DomainService.GetAssignedDomains(id, true).ToArray();
var wildcard = nodeDomains.FirstOrDefault(d => d.IsWildcard);
var domains = nodeDomains.Where(d => !d.IsWildcard).Select(d => new DomainDisplay(d.DomainName, d.LanguageId.GetValueOrDefault(0)));
return new ContentDomainsAndCulture
{
Domains = domains,
Language = wildcard == null || !wildcard.LanguageId.HasValue ? "undefined" : wildcard.LanguageId.ToString()
};
}
[HttpPost]
public DomainSave PostSaveLanguageAndDomains(DomainSave model)
{

View File

@@ -10,15 +10,18 @@ using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using Umbraco.Core.Models;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using System.Web.Http.Controllers;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Xml;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Search;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.Editors
{
@@ -211,7 +214,7 @@ namespace Umbraco.Web.Editors
}
}
var ancestors = GetAncestors(id, type);
var ancestors = GetResultForAncestors(id, type);
//if content, skip the first node for replicating NiceUrl defaults
if(type == UmbracoEntityTypes.Document) {
@@ -582,9 +585,10 @@ namespace Umbraco.Web.Editors
}
}
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type)
[HttpQueryStringFilter("queryStrings")]
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type, FormDataCollection queryStrings)
{
return GetResultForAncestors(id, type);
return GetResultForAncestors(id, type, queryStrings);
}
/// <summary>
@@ -626,7 +630,7 @@ namespace Umbraco.Web.Editors
}
}
private IEnumerable<EntityBasic> GetResultForAncestors(int id, UmbracoEntityTypes entityType)
private IEnumerable<EntityBasic> GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormDataCollection queryStrings = null)
{
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
@@ -666,12 +670,14 @@ namespace Umbraco.Web.Editors
ids = lids.ToArray();
}
var culture = queryStrings?.GetValue<string>("culture");
return ids.Length == 0
? Enumerable.Empty<EntityBasic>()
: Services.EntityService.GetAll(objectType.Value, ids)
.WhereNotNull()
.OrderBy(x => x.Level)
.Select(Mapper.Map<EntityBasic>);
.Select(x => Mapper.Map<EntityBasic>(x, opts => { opts.SetCulture(culture);}));
}
//now we need to convert the unknown ones
switch (entityType)

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Umbraco.Web.Models.ContentEditing
{
public class ContentDomainsAndCulture
{
public IEnumerable<DomainDisplay> Domains { get; set; }
public string Language { get; internal set; }
}
}

View File

@@ -25,6 +25,7 @@ namespace Umbraco.Web.Models.Mapping
var contentTypeUdiResolver = new ContentTypeUdiResolver();
CreateMap<EntitySlim, EntityBasic>()
.ForMember(dest => dest.Name, opt => opt.ResolveUsing<NameResolver>())
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => GetContentTypeIcon(src)))
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
@@ -179,5 +180,33 @@ namespace Umbraco.Web.Models.Mapping
CreateMap<IEnumerable<SearchResult>, IEnumerable<SearchResultItem>>()
.ConvertUsing(results => results.Select(Mapper.Map<SearchResultItem>).ToList());
}
/// <summary>
/// Resolves the name for a content item/content variant
/// </summary>
private class NameResolver : IValueResolver<EntitySlim, EntityBasic, string>
{
public string Resolve(EntitySlim source, EntityBasic destination, string destMember, ResolutionContext context)
{
if (!(source is DocumentEntitySlim doc))
return source.Name;
// invariant = only 1 name
if (!doc.Variations.VariesByCulture()) return source.Name;
// variant = depends on culture
var culture = context.Options.GetCulture();
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
if (culture == null)
//throw new InvalidOperationException("Missing culture in mapping options.");
// fixme we should throw, but this is used in various places that won't set a culture yet
return source.Name;
// if we don't have a name for a culture, it means the culture is not available, and
// hey we should probably not be mapping it, but it's too late, return a fallback name
return doc.CultureNames.TryGetValue(culture, out var name) && !name.IsNullOrWhiteSpace() ? name : $"(({source.Name}))";
}
}
}
}

View File

@@ -272,6 +272,9 @@ namespace Umbraco.Web.PropertyEditors
public IEnumerable<ValidationResult> Validate(object rawValue, string valueType, object dataTypeConfiguration)
{
if (rawValue == null)
yield break;
var value = JsonConvert.DeserializeObject<List<object>>(rawValue.ToString());
if (value == null)
yield break;

View File

@@ -55,19 +55,18 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
using (_proflog.DebugDuration<PublishedPropertyType>($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})"))
{
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return null;
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0)
return Enumerable.Empty<IPublishedElement>();
var configuration = propertyType.DataType.ConfigurationAs<NestedContentConfiguration>();
var contentTypes = configuration.ContentTypes;
var elements = contentTypes.Length > 1
? new List<IPublishedElement>()
: PublishedModelFactory.CreateModelList(contentTypes[0].Alias);
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return elements;
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0) return elements;
foreach (var sourceObject in objects)
{
var element = ConvertToElement(sourceObject, referenceCacheLevel, preview);

View File

@@ -163,6 +163,7 @@
<Compile Include="Editors\BackOfficeServerVariables.cs" />
<Compile Include="ImageProcessorLogger.cs" />
<Compile Include="Macros\MacroTagParser.cs" />
<Compile Include="Models\ContentEditing\ContentDomainsAndCulture.cs" />
<Compile Include="Models\ContentEditing\ContentSavedState.cs" />
<Compile Include="Models\Mapping\ContentSavedStateResolver.cs" />
<Compile Include="OwinExtensions.cs" />