Merge branch 'temp8' into temp8-3640
This commit is contained in:
@@ -160,5 +160,8 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) => (IContentType)DeepCloneWithResetIdentities(newAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,9 +504,9 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public IContentType DeepCloneWithResetIdentities(string alias)
|
||||
public ContentTypeBase DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (ContentType)DeepClone();
|
||||
var clone = (ContentTypeBase)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
|
||||
@@ -44,29 +44,7 @@ namespace Umbraco.Core.Models
|
||||
/// <inheritdoc />
|
||||
public override bool IsPublishing => IsPublishingConst;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public new IMediaType DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (MediaType)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
propertyGroup.ResetDirtyProperties(false);
|
||||
}
|
||||
foreach (var propertyType in clone.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
IMediaType IMediaType.DeepCloneWithResetIdentities(string newAlias) => (IMediaType)DeepCloneWithResetIdentities(newAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
//var originalb = (ContentTypeCompositionBase)original;
|
||||
// but we *know* it has to be a ContentTypeCompositionBase anyways
|
||||
var originalb = (ContentTypeCompositionBase) (object) original;
|
||||
var clone = (TItem) originalb.DeepCloneWithResetIdentities(alias);
|
||||
var clone = (TItem) (object) originalb.DeepCloneWithResetIdentities(alias);
|
||||
|
||||
clone.Name = name;
|
||||
|
||||
@@ -645,7 +645,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
//var copyingb = (ContentTypeCompositionBase) copying;
|
||||
// but we *know* it has to be a ContentTypeCompositionBase anyways
|
||||
var copyingb = (ContentTypeCompositionBase) (object)copying;
|
||||
copy = (TItem) copyingb.DeepCloneWithResetIdentities(alias);
|
||||
copy = (TItem) (object) copyingb.DeepCloneWithResetIdentities(alias);
|
||||
|
||||
copy.Name = copy.Name + " (copy)"; // might not be unique
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace Umbraco.Examine
|
||||
/// <summary>
|
||||
/// By default these are the member fields we index
|
||||
/// </summary>
|
||||
public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email" };
|
||||
public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email", UmbracoExamineIndex.NodeKeyFieldName };
|
||||
|
||||
private static readonly IEnumerable<string> ValidCategories = new[] { IndexTypes.Member };
|
||||
protected override IEnumerable<string> ValidIndexCategories => ValidCategories;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,46 +407,6 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsNull(doc2.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Media_Type_With_Hierarchy_Of_Media_Items_Moves_Orphaned_Media_To_Recycle_Bin()
|
||||
{
|
||||
IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1");
|
||||
ServiceContext.MediaTypeService.Save(contentType1);
|
||||
IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2");
|
||||
ServiceContext.MediaTypeService.Save(contentType2);
|
||||
IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3");
|
||||
ServiceContext.MediaTypeService.Save(contentType3);
|
||||
|
||||
var contentTypes = new[] { contentType1, contentType2, contentType3 };
|
||||
var parentId = -1;
|
||||
|
||||
var ids = new List<int>();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (var index = 0; index < contentTypes.Length; index++)
|
||||
{
|
||||
var contentType = contentTypes[index];
|
||||
var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId);
|
||||
ServiceContext.MediaService.Save(contentItem);
|
||||
parentId = contentItem.Id;
|
||||
|
||||
ids.Add(contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
//delete the first content type, all other content of different content types should be in the recycle bin
|
||||
ServiceContext.MediaTypeService.Delete(contentTypes[0]);
|
||||
|
||||
var found = ServiceContext.MediaService.GetByIds(ids);
|
||||
|
||||
Assert.AreEqual(4, found.Count());
|
||||
foreach (var content in found)
|
||||
{
|
||||
Assert.IsTrue(content.Trashed);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Content_Type_With_Hierarchy_Of_Content_Items_Moves_Orphaned_Content_To_Recycle_Bin()
|
||||
{
|
||||
@@ -491,60 +451,6 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Media_Types_With_Hierarchy_Of_Media_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items()
|
||||
{
|
||||
MediaService.Trashed += MediaServiceOnTrashed;
|
||||
|
||||
try
|
||||
{
|
||||
IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1");
|
||||
ServiceContext.MediaTypeService.Save(contentType1);
|
||||
IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2");
|
||||
ServiceContext.MediaTypeService.Save(contentType2);
|
||||
IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3");
|
||||
ServiceContext.MediaTypeService.Save(contentType3);
|
||||
|
||||
var contentTypes = new[] { contentType1, contentType2, contentType3 };
|
||||
var parentId = -1;
|
||||
|
||||
var ids = new List<int>();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (var index = 0; index < contentTypes.Length; index++)
|
||||
{
|
||||
var contentType = contentTypes[index];
|
||||
var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId);
|
||||
ServiceContext.MediaService.Save(contentItem);
|
||||
parentId = contentItem.Id;
|
||||
|
||||
ids.Add(contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var contentType in contentTypes.Reverse())
|
||||
{
|
||||
ServiceContext.MediaTypeService.Delete(contentType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
MediaService.Trashed -= MediaServiceOnTrashed;
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaServiceOnTrashed(IMediaService sender, MoveEventArgs<IMedia> e)
|
||||
{
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
{
|
||||
//if this item doesn't exist then Fail!
|
||||
var exists = ServiceContext.MediaService.GetById(item.Entity.Id);
|
||||
if (exists == null)
|
||||
Assert.Fail("The item doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Content_Types_With_Hierarchy_Of_Content_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items_1()
|
||||
{
|
||||
@@ -1109,12 +1015,13 @@ namespace Umbraco.Tests.Services
|
||||
var metaContentType = MockedContentTypes.CreateMetaContentType();
|
||||
service.Save(metaContentType);
|
||||
|
||||
var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", metaContentType);
|
||||
var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", metaContentType) as IContentType;
|
||||
service.Save(simpleContentType);
|
||||
var categoryId = simpleContentType.Id;
|
||||
|
||||
// Act
|
||||
var sut = simpleContentType.DeepCloneWithResetIdentities("newcategory");
|
||||
Assert.IsNotNull(sut);
|
||||
service.Save(sut);
|
||||
|
||||
// Assert
|
||||
@@ -1146,11 +1053,12 @@ namespace Umbraco.Tests.Services
|
||||
var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2", null, true);
|
||||
service.Save(parentContentType2);
|
||||
|
||||
var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true);
|
||||
var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true) as IContentType;
|
||||
service.Save(simpleContentType);
|
||||
|
||||
// Act
|
||||
var clone = simpleContentType.DeepCloneWithResetIdentities("newcategory");
|
||||
Assert.IsNotNull(clone);
|
||||
clone.RemoveContentType("parent1");
|
||||
clone.AddContentType(parentContentType2);
|
||||
clone.ParentId = parentContentType2.Id;
|
||||
@@ -2114,22 +2022,6 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsNull(contentType2.Description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_Description_Is_Always_Null_After_Saving_Media_Type()
|
||||
{
|
||||
var service = ServiceContext.MediaTypeService;
|
||||
var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type");
|
||||
mediaType.Description = null;
|
||||
service.Save(mediaType);
|
||||
|
||||
var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2");
|
||||
mediaType2.Description = string.Empty;
|
||||
service.Save(mediaType2);
|
||||
|
||||
Assert.IsNull(mediaType.Description);
|
||||
Assert.IsNull(mediaType2.Description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Variations_In_Compositions()
|
||||
{
|
||||
|
||||
195
src/Umbraco.Tests/Services/MediaTypeServiceTests.cs
Normal file
195
src/Umbraco.Tests/Services/MediaTypeServiceTests.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)]
|
||||
public class MediaTypeServiceTests : TestWithSomeContentBase
|
||||
{
|
||||
[Test]
|
||||
public void Empty_Description_Is_Always_Null_After_Saving_Media_Type()
|
||||
{
|
||||
var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type");
|
||||
mediaType.Description = null;
|
||||
ServiceContext.MediaTypeService.Save(mediaType);
|
||||
|
||||
var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2");
|
||||
mediaType2.Description = string.Empty;
|
||||
ServiceContext.MediaTypeService.Save(mediaType2);
|
||||
|
||||
Assert.IsNull(mediaType.Description);
|
||||
Assert.IsNull(mediaType2.Description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Media_Type_With_Hierarchy_Of_Media_Items_Moves_Orphaned_Media_To_Recycle_Bin()
|
||||
{
|
||||
IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1");
|
||||
ServiceContext.MediaTypeService.Save(contentType1);
|
||||
IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2");
|
||||
ServiceContext.MediaTypeService.Save(contentType2);
|
||||
IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3");
|
||||
ServiceContext.MediaTypeService.Save(contentType3);
|
||||
|
||||
var contentTypes = new[] { contentType1, contentType2, contentType3 };
|
||||
var parentId = -1;
|
||||
|
||||
var ids = new List<int>();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (var index = 0; index < contentTypes.Length; index++)
|
||||
{
|
||||
var contentType = contentTypes[index];
|
||||
var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId);
|
||||
ServiceContext.MediaService.Save(contentItem);
|
||||
parentId = contentItem.Id;
|
||||
|
||||
ids.Add(contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
//delete the first content type, all other content of different content types should be in the recycle bin
|
||||
ServiceContext.MediaTypeService.Delete(contentTypes[0]);
|
||||
|
||||
var found = ServiceContext.MediaService.GetByIds(ids);
|
||||
|
||||
Assert.AreEqual(4, found.Count());
|
||||
foreach (var content in found)
|
||||
{
|
||||
Assert.IsTrue(content.Trashed);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Media_Types_With_Hierarchy_Of_Media_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items()
|
||||
{
|
||||
MediaService.Trashed += MediaServiceOnTrashed;
|
||||
|
||||
try
|
||||
{
|
||||
IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1");
|
||||
ServiceContext.MediaTypeService.Save(contentType1);
|
||||
IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2");
|
||||
ServiceContext.MediaTypeService.Save(contentType2);
|
||||
IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3");
|
||||
ServiceContext.MediaTypeService.Save(contentType3);
|
||||
|
||||
var contentTypes = new[] { contentType1, contentType2, contentType3 };
|
||||
var parentId = -1;
|
||||
|
||||
var ids = new List<int>();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (var index = 0; index < contentTypes.Length; index++)
|
||||
{
|
||||
var contentType = contentTypes[index];
|
||||
var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId);
|
||||
ServiceContext.MediaService.Save(contentItem);
|
||||
parentId = contentItem.Id;
|
||||
|
||||
ids.Add(contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var contentType in contentTypes.Reverse())
|
||||
{
|
||||
ServiceContext.MediaTypeService.Delete(contentType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
MediaService.Trashed -= MediaServiceOnTrashed;
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaServiceOnTrashed(IMediaService sender, MoveEventArgs<IMedia> e)
|
||||
{
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
{
|
||||
//if this item doesn't exist then Fail!
|
||||
var exists = ServiceContext.MediaService.GetById(item.Entity.Id);
|
||||
if (exists == null)
|
||||
Assert.Fail("The item doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Copy_MediaType_By_Performing_Clone()
|
||||
{
|
||||
// Arrange
|
||||
var mediaType = MockedContentTypes.CreateImageMediaType("Image2") as IMediaType;
|
||||
ServiceContext.MediaTypeService.Save(mediaType);
|
||||
|
||||
// Act
|
||||
var sut = mediaType.DeepCloneWithResetIdentities("Image2_2");
|
||||
Assert.IsNotNull(sut);
|
||||
ServiceContext.MediaTypeService.Save(sut);
|
||||
|
||||
// Assert
|
||||
Assert.That(sut.HasIdentity, Is.True);
|
||||
Assert.AreEqual(mediaType.ParentId, sut.ParentId);
|
||||
Assert.AreEqual(mediaType.Level, sut.Level);
|
||||
Assert.AreEqual(mediaType.PropertyTypes.Count(), sut.PropertyTypes.Count());
|
||||
Assert.AreNotEqual(mediaType.Id, sut.Id);
|
||||
Assert.AreNotEqual(mediaType.Key, sut.Key);
|
||||
Assert.AreNotEqual(mediaType.Path, sut.Path);
|
||||
Assert.AreNotEqual(mediaType.SortOrder, sut.SortOrder);
|
||||
Assert.AreNotEqual(mediaType.PropertyTypes.First(x => x.Alias.Equals("umbracoFile")).Id, sut.PropertyTypes.First(x => x.Alias.Equals("umbracoFile")).Id);
|
||||
Assert.AreNotEqual(mediaType.PropertyGroups.First(x => x.Name.Equals("Media")).Id, sut.PropertyGroups.First(x => x.Name.Equals("Media")).Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Copy_MediaType_To_New_Parent_By_Performing_Clone()
|
||||
{
|
||||
// Arrange
|
||||
var parentMediaType1 = MockedContentTypes.CreateSimpleMediaType("parent1", "Parent1");
|
||||
ServiceContext.MediaTypeService.Save(parentMediaType1);
|
||||
var parentMediaType2 = MockedContentTypes.CreateSimpleMediaType("parent2", "Parent2", null, true);
|
||||
ServiceContext.MediaTypeService.Save(parentMediaType2);
|
||||
var mediaType = MockedContentTypes.CreateImageMediaType("Image2") as IMediaType;
|
||||
ServiceContext.MediaTypeService.Save(mediaType);
|
||||
|
||||
// Act
|
||||
var clone = mediaType.DeepCloneWithResetIdentities("newcategory");
|
||||
Assert.IsNotNull(clone);
|
||||
clone.RemoveContentType("parent1");
|
||||
clone.AddContentType(parentMediaType2);
|
||||
clone.ParentId = parentMediaType2.Id;
|
||||
ServiceContext.MediaTypeService.Save(clone);
|
||||
|
||||
// Assert
|
||||
Assert.That(clone.HasIdentity, Is.True);
|
||||
|
||||
var clonedMediaType = ServiceContext.MediaTypeService.Get(clone.Id);
|
||||
var originalMediaType = ServiceContext.MediaTypeService.Get(mediaType.Id);
|
||||
|
||||
Assert.That(clonedMediaType.CompositionAliases().Any(x => x.Equals("parent2")), Is.True);
|
||||
Assert.That(clonedMediaType.CompositionAliases().Any(x => x.Equals("parent1")), Is.False);
|
||||
|
||||
Assert.AreEqual(clonedMediaType.Path, "-1," + parentMediaType2.Id + "," + clonedMediaType.Id);
|
||||
Assert.AreEqual(clonedMediaType.PropertyTypes.Count(), originalMediaType.PropertyTypes.Count());
|
||||
|
||||
Assert.AreNotEqual(clonedMediaType.ParentId, originalMediaType.ParentId);
|
||||
Assert.AreEqual(clonedMediaType.ParentId, parentMediaType2.Id);
|
||||
|
||||
Assert.AreNotEqual(clonedMediaType.Id, originalMediaType.Id);
|
||||
Assert.AreNotEqual(clonedMediaType.Key, originalMediaType.Key);
|
||||
Assert.AreNotEqual(clonedMediaType.Path, originalMediaType.Path);
|
||||
|
||||
Assert.AreNotEqual(clonedMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id, originalMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id);
|
||||
Assert.AreNotEqual(clonedMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id, originalMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,7 @@
|
||||
<Compile Include="Services\EntityXmlSerializerTests.cs" />
|
||||
<Compile Include="Packaging\CreatedPackagesRepositoryTests.cs" />
|
||||
<Compile Include="Services\MemberGroupServiceTests.cs" />
|
||||
<Compile Include="Services\MediaTypeServiceTests.cs" />
|
||||
<Compile Include="Testing\Objects\TestDataSource.cs" />
|
||||
<Compile Include="Published\PublishedSnapshotTestObjects.cs" />
|
||||
<Compile Include="Published\ModelTypeTests.cs" />
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
|
||||
function umbTagsEditorController($rootScope, assetsService, umbRequestHelper, angularHelper, $timeout, $element) {
|
||||
|
||||
var vm = this;
|
||||
let vm = this;
|
||||
|
||||
var typeahead;
|
||||
var tagsHound;
|
||||
let typeahead;
|
||||
let tagsHound;
|
||||
|
||||
vm.$onInit = onInit;
|
||||
vm.$onChanges = onChanges;
|
||||
@@ -52,50 +52,52 @@
|
||||
|
||||
vm.isLoading = false;
|
||||
|
||||
//ensure that the models are formatted correctly
|
||||
configureViewModel();
|
||||
|
||||
// Set the visible prompt to -1 to ensure it will not be visible
|
||||
vm.promptIsVisible = "-1";
|
||||
|
||||
tagsHound = new Bloodhound({
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||||
initialize: false,
|
||||
identify: function (obj) { return obj.id; },
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('text'),
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
//pre-fetch the tags for this category
|
||||
prefetch: {
|
||||
url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture }),
|
||||
//TTL = 5 minutes
|
||||
ttl: 300000,
|
||||
transform: dataTransform
|
||||
ttl: 300000
|
||||
},
|
||||
//dynamically get the tags for this category (they may have changed on the server)
|
||||
remote: {
|
||||
url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture }),
|
||||
transform: dataTransform
|
||||
url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture, query: "%QUERY" }),
|
||||
wildcard: "%QUERY"
|
||||
}
|
||||
});
|
||||
|
||||
tagsHound.initialize(true);
|
||||
|
||||
//configure the type ahead
|
||||
$timeout(function () {
|
||||
tagsHound.initialize().then(function() {
|
||||
|
||||
//configure the type ahead
|
||||
|
||||
var sources = {
|
||||
//see: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md#options
|
||||
// name = the data set name, we'll make this the tag group name
|
||||
name: vm.config.group,
|
||||
display: "value",
|
||||
// name = the data set name, we'll make this the tag group name + culture
|
||||
name: vm.config.group + (vm.culture ? vm.culture : ""),
|
||||
display: "text",
|
||||
//source: tagsHound
|
||||
source: function (query, cb) {
|
||||
source: function (query, syncCallback, asyncCallback) {
|
||||
tagsHound.search(query,
|
||||
function(suggestions) {
|
||||
cb(removeCurrentTagsFromSuggestions(suggestions));
|
||||
syncCallback(removeCurrentTagsFromSuggestions(suggestions));
|
||||
}, function(suggestions) {
|
||||
asyncCallback(removeCurrentTagsFromSuggestions(suggestions));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var opts = {
|
||||
//This causes some strangeness as it duplicates the textbox, best leave off for now.
|
||||
hint: false,
|
||||
hint: true,
|
||||
highlight: true,
|
||||
cacheKey: new Date(), // Force a cache refresh each time the control is initialized
|
||||
minLength: 1
|
||||
@@ -104,28 +106,35 @@
|
||||
typeahead = $element.find('.tags-' + vm.htmlId).typeahead(opts, sources)
|
||||
.bind("typeahead:selected", function (obj, datum, name) {
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
addTagInternal(datum["value"]);
|
||||
addTagInternal(datum["text"]);
|
||||
vm.tagToAdd = "";
|
||||
// clear the typed text
|
||||
typeahead.typeahead('val', '');
|
||||
});
|
||||
}).bind("typeahead:autocompleted", function (obj, datum, name) {
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
addTagInternal(datum["value"]);
|
||||
addTagInternal(datum["text"]);
|
||||
vm.tagToAdd = "";
|
||||
// clear the typed text
|
||||
typeahead.typeahead('val', '');
|
||||
});
|
||||
|
||||
}).bind("typeahead:opened", function (obj) {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for value changes
|
||||
* @param {any} changes
|
||||
*/
|
||||
function onChanges(changes) {
|
||||
|
||||
// watch for value changes externally
|
||||
//when the model 'value' changes, sync the viewModel object
|
||||
if (changes.value) {
|
||||
if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) {
|
||||
|
||||
@@ -166,6 +175,7 @@
|
||||
});
|
||||
|
||||
updateModelValue(vm.viewModel);
|
||||
|
||||
}
|
||||
}
|
||||
else if (angular.isArray(vm.value)) {
|
||||
@@ -175,12 +185,10 @@
|
||||
}
|
||||
|
||||
function updateModelValue(val) {
|
||||
if (val) {
|
||||
vm.onValueChanged({ value: val });
|
||||
}
|
||||
else {
|
||||
vm.onValueChanged({ value: [] });
|
||||
}
|
||||
|
||||
val = val ? val : [];
|
||||
|
||||
vm.onValueChanged({ value: val });
|
||||
|
||||
reValidate();
|
||||
}
|
||||
@@ -252,29 +260,19 @@
|
||||
function hidePrompt() {
|
||||
vm.promptIsVisible = "-1";
|
||||
}
|
||||
|
||||
//helper method to format the data for bloodhound
|
||||
function dataTransform(list) {
|
||||
//transform the result to what bloodhound wants
|
||||
var tagList = _.map(list, function (i) {
|
||||
return { value: i.text };
|
||||
});
|
||||
// remove current tags from the list
|
||||
return $.grep(tagList, function (tag) {
|
||||
return ($.inArray(tag.value, vm.viewModel) === -1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// helper method to remove current tags
|
||||
function removeCurrentTagsFromSuggestions(suggestions) {
|
||||
return $.grep(suggestions, function (suggestion) {
|
||||
return ($.inArray(suggestion.value, vm.viewModel) === -1);
|
||||
return ($.inArray(suggestion.text, vm.viewModel) === -1);
|
||||
});
|
||||
}
|
||||
|
||||
function reValidate() {
|
||||
//this is required to re-validate
|
||||
vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
|
||||
//this is required to re-validate for the mandatory validation
|
||||
if (vm.tagEditorForm && vm.tagEditorForm.tagCount) {
|
||||
vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -201,7 +201,8 @@ input[type="button"] {
|
||||
}
|
||||
// Made for Umbraco, 2019
|
||||
.btn-selection {
|
||||
.buttonBackground(@pinkLight, ligthen(@pinkLight, 20%), @blueExtraDark, @blueDark);
|
||||
@btnSelectionBackgroundHover: darken(@pinkLight, 10%);
|
||||
.buttonBackground(@pinkLight, @btnSelectionBackgroundHover, @blueExtraDark, @blueDark);
|
||||
}
|
||||
// Made for Umbraco, 2019, used for buttons that has to stand back.
|
||||
.btn-white {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
.umb-layout-selector__active-layout {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border: 1px solid @inputBorder;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
.umb-layout-selector__active-layout:hover {
|
||||
border-color: @gray-8;
|
||||
border-color: @inputBorderFocus;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown {
|
||||
|
||||
@@ -90,13 +90,13 @@ input.umb-table__input {
|
||||
|
||||
.umb-table-body .umb-table-row {
|
||||
color: @gray-5;
|
||||
border-top: 1px solid @gray-8;
|
||||
border-top: 1px solid @gray-9;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
min-height: 52px;
|
||||
&:hover {
|
||||
background-color: @gray-10;
|
||||
background-color: @ui-option-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +151,26 @@ input.umb-table__input {
|
||||
|
||||
// Show checkmark when checked, hide file icon
|
||||
.umb-table-row--selected {
|
||||
/*
|
||||
.umb-table-body__fileicon {
|
||||
display: none;
|
||||
}
|
||||
.umb-table-body__checkicon {
|
||||
display: inline-block;
|
||||
}
|
||||
*/
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index:1;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
border: 2px solid @ui-selected-border;
|
||||
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Table Row Styles
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.TagsController",
|
||||
function ($scope, angularHelper) {
|
||||
function ($scope) {
|
||||
|
||||
$scope.valueChanged = function(value) {
|
||||
$scope.model.value = value;
|
||||
// the model value seems to be a reference to the same array, so we need
|
||||
// to set the form as dirty explicitly when the content of the array changes
|
||||
angularHelper.getCurrentForm($scope).$setDirty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
|
||||
},
|
||||
{
|
||||
"tagsDataBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TagsDataController>(
|
||||
controller => controller.GetTags("", ""))
|
||||
controller => controller.GetTags("", "", null))
|
||||
},
|
||||
{
|
||||
"examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ExamineManagementController>(
|
||||
|
||||
@@ -43,12 +43,16 @@ namespace Umbraco.Web.Editors
|
||||
public class EntityController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private readonly ITreeService _treeService;
|
||||
private readonly UmbracoTreeSearcher _treeSearcher;
|
||||
private readonly SearchableTreeCollection _searchableTreeCollection;
|
||||
|
||||
public EntityController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState,
|
||||
ITreeService treeService)
|
||||
ITreeService treeService, SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher)
|
||||
: base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState)
|
||||
{
|
||||
_treeService = treeService;
|
||||
_searchableTreeCollection = searchableTreeCollection;
|
||||
_treeSearcher = treeSearcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,15 +73,6 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
|
||||
private readonly UmbracoTreeSearcher _treeSearcher;
|
||||
private readonly SearchableTreeCollection _searchableTreeCollection;
|
||||
|
||||
public EntityController(SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher)
|
||||
{
|
||||
_searchableTreeCollection = searchableTreeCollection;
|
||||
_treeSearcher = treeSearcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an Umbraco alias given a string
|
||||
/// </summary>
|
||||
|
||||
@@ -592,11 +592,6 @@ namespace Umbraco.Web.Editors
|
||||
if (builtInAliases.Contains(p.Alias) == false && valueMapped != null)
|
||||
{
|
||||
p.SetValue(valueMapped.GetValue());
|
||||
|
||||
// FIXME: /task - ok, I give up, at that point tags are dead here, until we figure it out
|
||||
// p.TagChanges.Behavior = valueMapped.TagChanges.Behavior;
|
||||
// p.TagChanges.Enable = valueMapped.TagChanges.Enable;
|
||||
// p.TagChanges.Tags = valueMapped.TagChanges.Tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
@@ -15,10 +17,28 @@ namespace Umbraco.Web.PropertyEditors
|
||||
[PluginController("UmbracoApi")]
|
||||
public class TagsDataController : UmbracoAuthorizedApiController
|
||||
{
|
||||
public IEnumerable<TagModel> GetTags(string tagGroup, string culture)
|
||||
/// <summary>
|
||||
/// Returns all tags matching tagGroup, culture and an optional query
|
||||
/// </summary>
|
||||
/// <param name="tagGroup"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<TagModel> GetTags(string tagGroup, string culture, string query = null)
|
||||
{
|
||||
if (culture == string.Empty) culture = null;
|
||||
return Umbraco.TagQuery.GetAllTags(tagGroup, culture);
|
||||
|
||||
var result = Umbraco.TagQuery.GetAllTags(tagGroup, culture);
|
||||
|
||||
|
||||
if (!query.IsNullOrWhiteSpace())
|
||||
{
|
||||
//TODO: add the query to TagQuery + the tag service, this is ugly but all we can do for now.
|
||||
//currently we are post filtering this :( but works for now
|
||||
result = result.Where(x => x.Text.InvariantContains(query));
|
||||
}
|
||||
|
||||
return result.OrderBy(x => x.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user