diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index e6439acade..4b9831682c 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -160,5 +160,8 @@ namespace Umbraco.Core.Models
return result;
}
+
+ ///
+ IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) => (IContentType)DeepCloneWithResetIdentities(newAlias);
}
}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 064bdd3d4a..3da2838d0e 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -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)
diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs
index 4ae2fd190c..83e1acfbc0 100644
--- a/src/Umbraco.Core/Models/MediaType.cs
+++ b/src/Umbraco.Core/Models/MediaType.cs
@@ -44,29 +44,7 @@ namespace Umbraco.Core.Models
///
public override bool IsPublishing => IsPublishingConst;
- ///
- /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
- ///
- ///
- 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;
- }
+ ///
+ IMediaType IMediaType.DeepCloneWithResetIdentities(string newAlias) => (IMediaType)DeepCloneWithResetIdentities(newAlias);
}
}
diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
index bf43693d3b..78bb9821e4 100644
--- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
@@ -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
diff --git a/src/Umbraco.Examine/MemberValueSetValidator.cs b/src/Umbraco.Examine/MemberValueSetValidator.cs
index 71de6001ce..0c585854e9 100644
--- a/src/Umbraco.Examine/MemberValueSetValidator.cs
+++ b/src/Umbraco.Examine/MemberValueSetValidator.cs
@@ -23,10 +23,10 @@ namespace Umbraco.Examine
///
/// By default these are the member fields we index
///
- 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 ValidCategories = new[] { IndexTypes.Member };
protected override IEnumerable ValidIndexCategories => ValidCategories;
-
+
}
}
diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
index 8dc8a2b45c..1319d00c48 100644
--- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
@@ -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();
-
- 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();
-
- 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 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()
{
diff --git a/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs
new file mode 100644
index 0000000000..915dc5ceec
--- /dev/null
+++ b/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs
@@ -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();
+
+ 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();
+
+ 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 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);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 1703743c6f..3bfc3387fe 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -149,6 +149,7 @@
+
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
index 1850445bd4..edf54ca034 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
@@ -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);
+ }
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less
index a246ef585e..c6a8447342 100644
--- a/src/Umbraco.Web.UI.Client/src/less/buttons.less
+++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less
@@ -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 {
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less
index adaf45b4b0..cf407b667f 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less
@@ -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 {
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
index c1687636d3..291eef43e0 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
@@ -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
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
index a61930f877..688ac7693f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
@@ -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();
}
}
diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
index 83bb152e0a..954491eab9 100644
--- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
},
{
"tagsDataBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl(
- controller => controller.GetTags("", ""))
+ controller => controller.GetTags("", "", null))
},
{
"examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl(
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index ca747e83f5..915b73e39a 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -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;
}
///
@@ -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;
- }
-
///
/// Returns an Umbraco alias given a string
///
diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs
index f4a8dfbd56..338e38fbe6 100644
--- a/src/Umbraco.Web/Editors/MemberController.cs
+++ b/src/Umbraco.Web/Editors/MemberController.cs
@@ -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;
}
}
}
diff --git a/src/Umbraco.Web/PropertyEditors/TagsDataController.cs b/src/Umbraco.Web/PropertyEditors/TagsDataController.cs
index 15c39bf994..7d699077c3 100644
--- a/src/Umbraco.Web/PropertyEditors/TagsDataController.cs
+++ b/src/Umbraco.Web/PropertyEditors/TagsDataController.cs
@@ -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 GetTags(string tagGroup, string culture)
+ ///
+ /// Returns all tags matching tagGroup, culture and an optional query
+ ///
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable 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);
}
}
}