Merge remote-tracking branch 'origin/v8/dev' into netcore/dev
# Conflicts: # src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs # src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs # src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
||||
@@ -69,6 +69,13 @@ namespace Umbraco.Core.Services
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is a list view content item in the path.
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(params int[] ids);
|
||||
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId);
|
||||
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
|
||||
EntityContainer GetContainer(int containerId);
|
||||
|
||||
@@ -27,6 +27,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is a list view content item in the path.
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(params int[] ids);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true or false depending on whether content nodes have been created based on the provided content type id.
|
||||
/// </summary>
|
||||
|
||||
@@ -1312,14 +1312,16 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
|
||||
return test;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
|
||||
/// </summary>
|
||||
/// <param name="contentPath"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc />
|
||||
public bool HasContainerInPath(string contentPath)
|
||||
{
|
||||
var ids = contentPath.Split(',').Select(int.Parse);
|
||||
var ids = contentPath.Split(',').Select(int.Parse).ToArray();
|
||||
return HasContainerInPath(ids);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasContainerInPath(params int[] ids)
|
||||
{
|
||||
var sql = new Sql($@"SELECT COUNT(*) FROM cmsContentType
|
||||
INNER JOIN {Constants.DatabaseSchema.Tables.Content} ON cmsContentType.nodeId={Constants.DatabaseSchema.Tables.Content}.contentTypeId
|
||||
WHERE {Constants.DatabaseSchema.Tables.Content}.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true });
|
||||
|
||||
@@ -16,35 +16,46 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetAllReferences(IPropertyCollection properties, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
var trackedRelations = new List<UmbracoEntityReference>();
|
||||
var trackedRelations = new HashSet<UmbracoEntityReference>();
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue;
|
||||
|
||||
//TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here
|
||||
if (!p.PropertyType.VariesByNothing()) continue;
|
||||
var val = p.GetValue(); // get the invariant value
|
||||
//TODO: We will need to change this once we support tracking via variants/segments
|
||||
// for now, we are tracking values from ALL variants
|
||||
|
||||
var valueEditor = editor.GetValueEditor();
|
||||
if (valueEditor is IDataValueReference reference)
|
||||
foreach(var propertyVal in p.Values)
|
||||
{
|
||||
var refs = reference.GetReferences(val);
|
||||
trackedRelations.AddRange(refs);
|
||||
var val = propertyVal.EditedValue;
|
||||
|
||||
var valueEditor = editor.GetValueEditor();
|
||||
if (valueEditor is IDataValueReference reference)
|
||||
{
|
||||
var refs = reference.GetReferences(val);
|
||||
foreach(var r in refs)
|
||||
trackedRelations.Add(r);
|
||||
}
|
||||
|
||||
// Loop over collection that may be add to existing property editors
|
||||
// implementation of GetReferences in IDataValueReference.
|
||||
// Allows developers to add support for references by a
|
||||
// package /property editor that did not implement IDataValueReference themselves
|
||||
foreach (var item in this)
|
||||
{
|
||||
// Check if this value reference is for this datatype/editor
|
||||
// Then call it's GetReferences method - to see if the value stored
|
||||
// in the dataeditor/property has referecnes to media/content items
|
||||
if (item.IsForEditor(editor))
|
||||
trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val));
|
||||
// Loop over collection that may be add to existing property editors
|
||||
// implementation of GetReferences in IDataValueReference.
|
||||
// Allows developers to add support for references by a
|
||||
// package /property editor that did not implement IDataValueReference themselves
|
||||
foreach (var item in this)
|
||||
{
|
||||
// Check if this value reference is for this datatype/editor
|
||||
// Then call it's GetReferences method - to see if the value stored
|
||||
// in the dataeditor/property has referecnes to media/content items
|
||||
if (item.IsForEditor(editor))
|
||||
{
|
||||
foreach(var r in item.GetDataValueReference().GetReferences(val))
|
||||
trackedRelations.Add(r);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return trackedRelations;
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
using System;
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Web.Templates;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
[DefaultPropertyValueConverter]
|
||||
public class MarkdownEditorValueConverter : PropertyValueConverterBase
|
||||
{
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
private readonly HtmlUrlParser _urlParser;
|
||||
|
||||
public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser)
|
||||
{
|
||||
_localLinkParser = localLinkParser;
|
||||
_urlParser = urlParser;
|
||||
}
|
||||
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
=> Constants.PropertyEditors.Aliases.MarkdownEditor.Equals(propertyType.EditorAlias);
|
||||
|
||||
@@ -15,20 +26,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
|
||||
// PropertyCacheLevel.Content is ok here because that converter does not parse {locallink} nor executes macros
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Element;
|
||||
=> PropertyCacheLevel.Snapshot;
|
||||
|
||||
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// in xml a string is: string
|
||||
// in the database a string is: string
|
||||
// default value is: null
|
||||
return source;
|
||||
if (source == null) return null;
|
||||
var sourceString = source.ToString();
|
||||
|
||||
// ensures string is parsed for {localLink} and urls are resolved correctly
|
||||
sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview);
|
||||
sourceString = _urlParser.EnsureUrls(sourceString);
|
||||
|
||||
return sourceString;
|
||||
}
|
||||
|
||||
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||
{
|
||||
// convert markup to HTML for frontend rendering.
|
||||
// source should come from ConvertSource and be a string (or null) already
|
||||
return new HtmlEncodedString(inter == null ? string.Empty : (string) inter);
|
||||
var mark = new Markdown();
|
||||
return new HtmlEncodedString(inter == null ? string.Empty : mark.Transform((string)inter));
|
||||
}
|
||||
|
||||
public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||
|
||||
@@ -318,6 +318,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasContainerInPath(params int[] ids)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
// can use same repo for both content and media
|
||||
return Repository.HasContainerInPath(ids);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TItem> GetDescendants(int id, bool andSelf)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using static Umbraco.Core.Models.Property;
|
||||
|
||||
namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
[TestFixture]
|
||||
public class DataValueReferenceFactoryCollectionTests
|
||||
{
|
||||
IDataTypeService DataTypeService { get; } = Mock.Of<IDataTypeService>();
|
||||
private IIOHelper IOHelper { get; } = TestHelper.IOHelper;
|
||||
ILocalizedTextService LocalizedTextService { get; } = Mock.Of<ILocalizedTextService>();
|
||||
ILocalizationService LocalizationService { get; } = Mock.Of<ILocalizationService>();
|
||||
IShortStringHelper ShortStringHelper { get; } = Mock.Of<IShortStringHelper>();
|
||||
|
||||
[Test]
|
||||
public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield());
|
||||
|
||||
|
||||
// label does not implement IDataValueReference
|
||||
var labelEditor = new LabelPropertyEditor(
|
||||
Mock.Of<ILogger>(),
|
||||
IOHelper,
|
||||
DataTypeService,
|
||||
LocalizedTextService,
|
||||
LocalizationService,
|
||||
ShortStringHelper
|
||||
);
|
||||
var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(labelEditor.Yield()));
|
||||
var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(ShortStringHelper, new DataType(labelEditor))
|
||||
{
|
||||
Variations = ContentVariation.CultureAndSegment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi3
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllReferences_All_Variants_With_IDataValueReference_Editor()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
|
||||
// mediaPicker does implement IDataValueReference
|
||||
var mediaPicker = new MediaPickerPropertyEditor(
|
||||
Mock.Of<ILogger>(),
|
||||
DataTypeService,
|
||||
LocalizationService,
|
||||
IOHelper,
|
||||
ShortStringHelper,
|
||||
LocalizedTextService
|
||||
);
|
||||
var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield()));
|
||||
var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker))
|
||||
{
|
||||
Variations = ContentVariation.CultureAndSegment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi3
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllReferences_Invariant_With_IDataValueReference_Editor()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
|
||||
// mediaPicker does implement IDataValueReference
|
||||
var mediaPicker = new MediaPickerPropertyEditor(
|
||||
Mock.Of<ILogger>(),
|
||||
DataTypeService,
|
||||
LocalizationService,
|
||||
IOHelper,
|
||||
ShortStringHelper,
|
||||
LocalizedTextService
|
||||
);
|
||||
var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield()));
|
||||
var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker))
|
||||
{
|
||||
Variations = ContentVariation.Nothing | ContentVariation.Segment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
// Ignored (has culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
// Ignored (has culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi4
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi1, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
private class TestDataValueReferenceFactory : IDataValueReferenceFactory
|
||||
{
|
||||
public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference();
|
||||
|
||||
public bool IsForEditor(IDataEditor dataEditor) => dataEditor.Alias == Constants.PropertyEditors.Aliases.Label;
|
||||
|
||||
private class TestMediaDataValueReference : IDataValueReference
|
||||
{
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
// This is the same as the media picker, it will just try to parse the value directly as a UDI
|
||||
|
||||
var asString = value is string str ? str : value?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(asString)) yield break;
|
||||
|
||||
if (UdiParser.TryParse(asString, out var udi))
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,7 @@
|
||||
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
|
||||
<Compile Include="UmbracoExamine\ExamineExtensions.cs" />
|
||||
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionTests.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentLanguageVariantTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 581 KiB |
@@ -9,6 +9,7 @@
|
||||
ng-model="vm.model"
|
||||
ng-change="vm.onChange()"
|
||||
ng-keydown="vm.onKeyDown($event)"
|
||||
ng-blur="vm.onBlur($event)"
|
||||
prevent-enter-submit
|
||||
no-dirty-check>
|
||||
</ng-form>
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
bindings: {
|
||||
model: "=",
|
||||
onStartTyping: "&?",
|
||||
onSearch: "&?"
|
||||
onSearch: "&?",
|
||||
onBlur: "&?"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en
|
||||
|
||||
_.each($scope.model.value, function (item){
|
||||
// we must reload the "document" link URLs to match the current editor culture
|
||||
if (item.udi.indexOf("/document/") > 0) {
|
||||
if (item.udi && item.udi.indexOf("/document/") > 0) {
|
||||
item.url = null;
|
||||
entityResource.getUrlByUdi(item.udi).then(function (data) {
|
||||
item.url = data;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
vm.userStates = [];
|
||||
vm.selection = [];
|
||||
vm.newUser = {};
|
||||
vm.usersOptions = {filter:null};
|
||||
vm.usersOptions = {};
|
||||
vm.userSortData = [
|
||||
{ label: "Name (A-Z)", key: "Name", direction: "Ascending" },
|
||||
{ label: "Name (Z-A)", key: "Name", direction: "Descending" },
|
||||
@@ -112,6 +112,7 @@
|
||||
vm.selectAll = selectAll;
|
||||
vm.areAllSelected = areAllSelected;
|
||||
vm.searchUsers = searchUsers;
|
||||
vm.onBlurSearch = onBlurSearch;
|
||||
vm.getFilterName = getFilterName;
|
||||
vm.setUserStatesFilter = setUserStatesFilter;
|
||||
vm.setUserGroupFilter = setUserGroupFilter;
|
||||
@@ -150,10 +151,12 @@
|
||||
function initViewOptions() {
|
||||
|
||||
// Start with default view options.
|
||||
vm.usersOptions.filter = "";
|
||||
vm.usersOptions.orderBy = "Name";
|
||||
vm.usersOptions.orderDirection = "Ascending";
|
||||
|
||||
// Update from querystring if available.
|
||||
initViewOptionFromQueryString("filter");
|
||||
initViewOptionFromQueryString("orderBy");
|
||||
initViewOptionFromQueryString("orderDirection");
|
||||
initViewOptionFromQueryString("pageNumber");
|
||||
@@ -451,7 +454,8 @@
|
||||
|
||||
var search = _.debounce(function () {
|
||||
$scope.$apply(function () {
|
||||
changePageNumber(1);
|
||||
vm.usersOptions.pageNumber = 1;
|
||||
getUsers();
|
||||
});
|
||||
}, 500);
|
||||
|
||||
@@ -459,6 +463,10 @@
|
||||
search();
|
||||
}
|
||||
|
||||
function onBlurSearch() {
|
||||
updateLocation("filter", vm.usersOptions.filter);
|
||||
}
|
||||
|
||||
function getFilterName(array) {
|
||||
var name = vm.labels.all;
|
||||
var found = false;
|
||||
@@ -547,6 +555,7 @@
|
||||
}
|
||||
|
||||
function updateLocation(key, value) {
|
||||
$location.search("filter", vm.usersOptions.filter);// update filter, but first when something else requests a url update.
|
||||
$location.search(key, value);
|
||||
}
|
||||
|
||||
@@ -657,7 +666,8 @@
|
||||
function usersOptionsAsQueryString() {
|
||||
var qs = "?orderBy=" + vm.usersOptions.orderBy +
|
||||
"&orderDirection=" + vm.usersOptions.orderDirection +
|
||||
"&pageNumber=" + vm.usersOptions.pageNumber;
|
||||
"&pageNumber=" + vm.usersOptions.pageNumber +
|
||||
"&filter=" + vm.usersOptions.filter;
|
||||
|
||||
qs += addUsersOptionsFilterCollectionToQueryString("userStates", vm.usersOptions.userStates);
|
||||
qs += addUsersOptionsFilterCollectionToQueryString("userGroups", vm.usersOptions.userGroups);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</umb-editor-sub-header-section>
|
||||
<umb-editor-sub-header-section>
|
||||
|
||||
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-start-typing="vm.searchUsers()">
|
||||
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-blur="vm.onBlurSearch()">
|
||||
</umb-mini-search>
|
||||
|
||||
</umb-editor-sub-header-section>
|
||||
|
||||
@@ -1671,7 +1671,7 @@ namespace Umbraco.Web.Editors
|
||||
[HttpPost]
|
||||
public DomainSave PostSaveLanguageAndDomains(DomainSave model)
|
||||
{
|
||||
foreach(var domain in model.Domains)
|
||||
foreach (var domain in model.Domains)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -2188,7 +2188,10 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
private ContentItemDisplay MapToDisplay(IContent content)
|
||||
{
|
||||
var display = Mapper.Map<ContentItemDisplay>(content);
|
||||
var display = Mapper.Map<ContentItemDisplay>(content, context =>
|
||||
{
|
||||
context.Items["CurrentUser"] = Security.CurrentUser;
|
||||
});
|
||||
display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false;
|
||||
return display;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Routing;
|
||||
@@ -30,6 +31,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
private readonly UriUtility _uriUtility;
|
||||
@@ -38,9 +40,10 @@ namespace Umbraco.Web.Models.Mapping
|
||||
private readonly ContentBasicSavedStateMapper<ContentPropertyBasic> _basicStateMapper;
|
||||
private readonly ContentVariantMapper _contentVariantMapper;
|
||||
|
||||
|
||||
public ContentMapDefinition(CommonMapper commonMapper, ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService, IContentService contentService, IContentTypeService contentTypeService,
|
||||
IFileService fileService, IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, ILocalizationService localizationService, ILogger logger,
|
||||
IUserService userService, IVariationContextAccessor variationContextAccessor, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider)
|
||||
IUserService userService, IVariationContextAccessor variationContextAccessor, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider, IEntityService entityService)
|
||||
{
|
||||
_commonMapper = commonMapper;
|
||||
_cultureDictionary = cultureDictionary;
|
||||
@@ -53,6 +56,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
_localizationService = localizationService;
|
||||
_logger = logger;
|
||||
_userService = userService;
|
||||
_entityService = entityService;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_uriUtility = uriUtility;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
@@ -90,7 +94,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Icon = source.ContentType.Icon;
|
||||
target.Id = source.Id;
|
||||
target.IsBlueprint = source.Blueprint;
|
||||
target.IsChildOfListView = DetermineIsChildOfListView(source);
|
||||
target.IsChildOfListView = DetermineIsChildOfListView(source, context);
|
||||
target.IsContainer = source.ContentType.IsContainer;
|
||||
target.IsElement = source.ContentType.IsElement;
|
||||
target.Key = source.Key;
|
||||
@@ -221,13 +225,66 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})";
|
||||
}
|
||||
|
||||
private bool DetermineIsChildOfListView(IContent source)
|
||||
/// <summary>
|
||||
/// Checks if the content item is a descendant of a list view
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>
|
||||
/// Returns true if the content item is a descendant of a list view and where the content is
|
||||
/// not a current user's start node.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// We must check if it's the current user's start node because in that case we will actually be
|
||||
/// rendering the tree node underneath the list view to visually show context. In this case we return
|
||||
/// false because the item is technically not being rendered as part of a list view but instead as a
|
||||
/// real tree node. If we didn't perform this check then tree syncing wouldn't work correctly.
|
||||
/// </remarks>
|
||||
private bool DetermineIsChildOfListView(IContent source, MapperContext context)
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var userStartNodes = Array.Empty<int>();
|
||||
|
||||
// In cases where a user's start node is below a list view, we will actually render
|
||||
// out the tree to that start node and in that case for that start node, we want to return
|
||||
// false here.
|
||||
if (context.HasItems && context.Items.TryGetValue("CurrentUser", out var usr) && usr is IUser currentUser)
|
||||
{
|
||||
userStartNodes = currentUser.CalculateContentStartNodeIds(_entityService);
|
||||
if (!userStartNodes.Contains(Constants.System.Root))
|
||||
{
|
||||
// return false if this is the user's actual start node, the node will be rendered in the tree
|
||||
// regardless of if it's a list view or not
|
||||
if (userStartNodes.Contains(source.Id))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var parent = _contentService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
|
||||
if (parent == null)
|
||||
return false;
|
||||
|
||||
var pathParts = parent.Path.Split(',').Select(x => int.TryParse(x, out var i) ? i : 0).ToList();
|
||||
|
||||
// reduce the path parts so we exclude top level content items that
|
||||
// are higher up than a user's start nodes
|
||||
foreach (var n in userStartNodes)
|
||||
{
|
||||
var index = pathParts.IndexOf(n);
|
||||
if (index != -1)
|
||||
{
|
||||
// now trim all top level start nodes to the found index
|
||||
for (var i = 0; i < index; i++)
|
||||
{
|
||||
pathParts.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(pathParts.ToArray());
|
||||
}
|
||||
|
||||
|
||||
private DateTime? GetScheduledDate(IContent source, ContentScheduleAction action, MapperContext context)
|
||||
{
|
||||
var culture = context.GetCulture() ?? string.Empty;
|
||||
|
||||
@@ -381,7 +381,12 @@ namespace Umbraco.Web.Trees
|
||||
var startNodes = Services.EntityService.GetAll(UmbracoObjectType, UserStartNodes);
|
||||
//if any of these start nodes' parent is current, then we need to render children normally so we need to switch some logic and tell
|
||||
// the UI that this node does have children and that it isn't a container
|
||||
if (startNodes.Any(x => x.ParentId == e.Id))
|
||||
|
||||
if (startNodes.Any(x =>
|
||||
{
|
||||
var pathParts = x.Path.Split(',');
|
||||
return pathParts.Contains(e.Id.ToInvariantString());
|
||||
}))
|
||||
{
|
||||
renderChildren = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user