From 551e8120c926b38366b36e8ae36f97ffd09c6c3b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 12 Sep 2018 12:08:51 +0100 Subject: [PATCH 1/8] Adds state property to ContentItemBase (Not sure if this will blow things up or if I need to use a diff model for ListView children now) Adds/copies the same logic from ContentSavedStateResolver.cs --- .../Models/ContentEditing/ContentItemBasic.cs | 5 ++ .../Models/Mapping/ContentMapperProfile.cs | 48 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index 427dadb2c9..df23b1fc72 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Umbraco.Core.Models; using Umbraco.Core.Models.Validation; using Umbraco.Web.WebApi; @@ -44,6 +45,10 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "sortOrder")] public int SortOrder { get; set; } + [DataMember(Name = "state")] + [JsonConverter(typeof(StringEnumConverter))] + public ContentSavedState State { get; set; } + protected bool Equals(ContentItemBasic other) { return Id == other.Id; diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index d7dc5cf46a..6ae36a1f19 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -83,7 +83,8 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) .ForMember(dest => dest.Published, opt => opt.ResolveUsing()) - .ForMember(dest => dest.Name, opt => opt.ResolveUsing()); + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing()); //FROM IContent TO ContentPropertyCollectionDto //NOTE: the property mapping for cultures relies on a culture being set in the mapping context @@ -91,6 +92,51 @@ namespace Umbraco.Web.Models.Mapping } } + internal class CultureStateResolver : IValueResolver, ContentSavedState> + { + //WB: Note this is same logic as ContentSavedStateResolver.cs + //But this is for ContentItemBasic instead of ContentVariantDisplay + public ContentSavedState Resolve(IContent source, ContentItemBasic destination, ContentSavedState destMember, ResolutionContext context) + { + PublishedState publishedState; + bool isEdited; + + if (source.ContentType.VariesByCulture()) + { + //Get the culture from the context which will be set during the mapping operation for each variant + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null) + throw new InvalidOperationException($"No culture found in mapping operation when one is required for a culture variant"); + + publishedState = source.PublishedState == PublishedState.Unpublished //if the entire document is unpublished, then flag every variant as unpublished + ? PublishedState.Unpublished + : source.IsCulturePublished(culture) + ? PublishedState.Published + : PublishedState.Unpublished; + + isEdited = source.IsCultureEdited(culture); + } + else + { + publishedState = source.PublishedState == PublishedState.Unpublished + ? PublishedState.Unpublished + : PublishedState.Published; + + isEdited = source.Edited; + } + + if (publishedState == PublishedState.Unpublished) + return isEdited && source.Id > 0 ? ContentSavedState.Draft : ContentSavedState.NotCreated; + + if (publishedState == PublishedState.Published) + return isEdited ? ContentSavedState.PublishedPendingChanges : ContentSavedState.Published; + + throw new NotSupportedException($"PublishedState {publishedState} is not supported."); + } + } + internal class CultureUpdateDateResolver : IValueResolver, DateTime> { public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) From 0c135ea3e2fa7ccda4bbd3ad5d1f1ccf9c63deca Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 12 Sep 2018 14:52:29 +0200 Subject: [PATCH 2/8] Add state to list view --- .../src/less/components/umb-content-grid.less | 6 +++--- .../src/views/components/umb-content-grid.html | 16 ++++++++++------ .../src/views/components/umb-table.html | 8 ++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index 15a319b520..b0c90483f7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -36,7 +36,7 @@ } .umb-content-grid__icon.-light { - color: @gray-8; + color: @gray-5; } @@ -58,7 +58,7 @@ } .umb-content-grid__item-name.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-list { @@ -69,7 +69,7 @@ } .umb-content-grid__details-list.-light { - color: @gray-8; + color: @gray-5; } .umb-content-grid__details-label { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html index 33be361206..30e2b81a1c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html @@ -15,12 +15,16 @@ {{ item.name }} -
    -
  • -
    {{ property.header }}:
    -
    {{ item[property.alias] }}
    -
  • -
+
    +
  • +
    :
    +
    +
  • +
  • +
    {{ property.header }}:
    +
    {{ item[property.alias] }}
    +
  • +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index 5245384aac..91c0af5005 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -15,6 +15,9 @@ +
+ Status +
+
+ + +
From b06ba12e42539c2d9d1c53a4640f62ff2c77e99a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 12 Sep 2018 15:15:11 +0200 Subject: [PATCH 3/8] fix js error on invariant nodes --- .../umbvariantcontenteditors.directive.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js index 7d6d5dc131..4902d943a7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontenteditors.directive.js @@ -179,6 +179,15 @@ variant.variants[i].active = false; } } + + // keep track of the open variants across the different split views + // push the first variant then update the variant index based on the editor index + if(vm.openVariants && vm.openVariants.length === 0) { + vm.openVariants.push(variant.language.culture); + } else { + vm.openVariants[editorIndex] = variant.language.culture; + } + } //then assign the variant to a view model to the content app @@ -186,14 +195,6 @@ return a.alias === "content"; }); contentApp.viewModel = variant; - - // keep track of the open variants across the different split views - // push the first variant then update the variant index based on the editor index - if(vm.openVariants && vm.openVariants.length === 0) { - vm.openVariants.push(variant.language.culture); - } else { - vm.openVariants[editorIndex] = variant.language.culture; - } // make sure the same app it set to active in the new variant if(activeAppAlias) { From 46ff45c2f0e5acd6f2768d6329a5eaf481d65024 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 12 Sep 2018 15:34:48 +0100 Subject: [PATCH 4/8] Makes State a ContentSavedState enum nullable - otherwise media items was getting a default enum of not created --- src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs | 5 +---- src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index df23b1fc72..d3077b0fd3 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -5,9 +5,6 @@ using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Validation; -using Umbraco.Web.WebApi; namespace Umbraco.Web.Models.ContentEditing { @@ -47,7 +44,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "state")] [JsonConverter(typeof(StringEnumConverter))] - public ContentSavedState State { get; set; } + public ContentSavedState? State { get; set; } = null; protected bool Equals(ContentItemBasic other) { diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 6ae36a1f19..445b88216c 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -92,14 +92,15 @@ namespace Umbraco.Web.Models.Mapping } } - internal class CultureStateResolver : IValueResolver, ContentSavedState> + internal class CultureStateResolver : IValueResolver, ContentSavedState?> { //WB: Note this is same logic as ContentSavedStateResolver.cs //But this is for ContentItemBasic instead of ContentVariantDisplay - public ContentSavedState Resolve(IContent source, ContentItemBasic destination, ContentSavedState destMember, ResolutionContext context) + public ContentSavedState? Resolve(IContent source, ContentItemBasic destination, ContentSavedState? destMember, ResolutionContext context) { PublishedState publishedState; bool isEdited; + if (source.ContentType.VariesByCulture()) { From 8c90b7fa28579c0a114ef4c2dc2c89e6b51f6acb Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 14 Sep 2018 10:08:09 +1000 Subject: [PATCH 5/8] Cleans up code, fixes tests, adds comments --- .../Models/ContentEditing/ContentItemBasic.cs | 4 +- .../ContentEditing/ContentSavedState.cs | 13 +- .../Models/Mapping/ContentMapperProfile.cs | 141 +++++++----------- .../Mapping/ContentSavedStateResolver.cs | 10 +- .../Models/Mapping/MediaMapperProfile.cs | 4 +- .../Models/Mapping/MemberMapperProfile.cs | 6 +- .../Models/Mapping/SavedStateResolver.cs | 19 +++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 8 files changed, 97 insertions(+), 101 deletions(-) create mode 100644 src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index d3077b0fd3..2b0da65dc9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Models.ContentEditing public DateTime CreateDate { get; set; } [DataMember(Name = "published")] - public bool Published { get; set; } + public bool Published => State == ContentSavedState.Published || State == ContentSavedState.PublishedPendingChanges; /// /// Determines if the content item is a draft @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "state")] [JsonConverter(typeof(StringEnumConverter))] - public ContentSavedState? State { get; set; } = null; + public ContentSavedState State { get; set; } protected bool Equals(ContentItemBasic other) { diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs index f433af58d6..58b742e238 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs @@ -5,24 +5,29 @@ /// public enum ContentSavedState { + /// + /// The state has not been set + /// + NotSet = -1, + /// /// The item isn't created yet /// - NotCreated, + NotCreated = 1, /// /// The item is saved but isn't published /// - Draft, + Draft = 2, /// /// The item is published and there are no pending changes /// - Published, + Published = 3, /// /// The item is published and there are pending changes /// - PublishedPendingChanges + PublishedPendingChanges = 4 } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index e197038849..9befb64aba 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -32,7 +32,6 @@ namespace Umbraco.Web.Models.Mapping var contentTypeBasicResolver = new ContentTypeBasicResolver(); var defaultTemplateResolver = new DefaultTemplateResolver(); var variantResolver = new ContentVariantResolver(localizationService); - var contentSavedStateResolver = new ContentSavedStateResolver(); //FROM IContent TO ContentItemDisplay CreateMap() @@ -67,7 +66,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Segment, opt => opt.Ignore()) .ForMember(dest => dest.Language, opt => opt.Ignore()) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing(contentSavedStateResolver)) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)); //FROM IContent TO ContentItemBasic @@ -81,110 +80,76 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) .ForMember(dest => dest.Alias, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) - .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) - .ForMember(dest => dest.Published, opt => opt.ResolveUsing()) - .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing()); + .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) + .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()); //FROM IContent TO ContentPropertyCollectionDto //NOTE: the property mapping for cultures relies on a culture being set in the mapping context CreateMap(); } - } - internal class CultureStateResolver : IValueResolver, ContentSavedState?> - { - //WB: Note this is same logic as ContentSavedStateResolver.cs - //But this is for ContentItemBasic instead of ContentVariantDisplay - public ContentSavedState? Resolve(IContent source, ContentItemBasic destination, ContentSavedState? destMember, ResolutionContext context) + /// + /// Resolves the update date for a content item/content variant + /// + private class UpdateDateResolver : IValueResolver, DateTime> { - PublishedState publishedState; - bool isEdited; - - - if (source.ContentType.VariesByCulture()) + public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) { - //Get the culture from the context which will be set during the mapping operation for each variant var culture = context.GetCulture(); //a culture needs to be in the context for a variant content item - if (culture == null) - throw new InvalidOperationException($"No culture found in mapping operation when one is required for a culture variant"); + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.UpdateDate; - publishedState = source.PublishedState == PublishedState.Unpublished //if the entire document is unpublished, then flag every variant as unpublished - ? PublishedState.Unpublished - : source.IsCulturePublished(culture) - ? PublishedState.Published - : PublishedState.Unpublished; - - isEdited = source.IsCultureEdited(culture); + var pubDate = source.GetPublishDate(culture); + return pubDate.HasValue ? pubDate.Value : source.UpdateDate; } - else - { - publishedState = source.PublishedState == PublishedState.Unpublished - ? PublishedState.Unpublished - : PublishedState.Published; - - isEdited = source.Edited; - } - - if (publishedState == PublishedState.Unpublished) - return isEdited && source.Id > 0 ? ContentSavedState.Draft : ContentSavedState.NotCreated; - - if (publishedState == PublishedState.Published) - return isEdited ? ContentSavedState.PublishedPendingChanges : ContentSavedState.Published; - - throw new NotSupportedException($"PublishedState {publishedState} is not supported."); } - } - internal class CultureUpdateDateResolver : IValueResolver, DateTime> - { - public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) + /// + /// Resolves the published flag for a content item/content variant + /// + private class PublishedResolver : IValueResolver, bool> { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.UpdateDate; - - var pubDate = source.GetPublishDate(culture); - return pubDate.HasValue ? pubDate.Value : source.UpdateDate; - } - } - - internal class CulturePublishedResolver : IValueResolver, bool> - { - public bool Resolve(IContent source, ContentItemBasic destination, bool destMember, ResolutionContext context) - { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Published; - - return source.IsCulturePublished(culture); - } - } - - internal class CultureNameResolver : IValueResolver, string> - { - public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) - { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Name; - - if (source.CultureNames.TryGetValue(culture, out var name) && !string.IsNullOrWhiteSpace(name)) + public bool Resolve(IContent source, ContentItemBasic destination, bool destMember, ResolutionContext context) { - return name; - } - else - { - return $"({ source.Name })"; + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.Published; + + return source.IsCulturePublished(culture); } } + + /// + /// Resolves the name for a content item/content variant + /// + private class NameResolver : IValueResolver, string> + { + public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) + { + var culture = context.GetCulture(); + + //a culture needs to be in the context for a variant content item + if (culture == null || source.ContentType.VariesByCulture() == false) + return source.Name; + + if (source.CultureNames.TryGetValue(culture, out var name) && !string.IsNullOrWhiteSpace(name)) + { + return name; + } + else + { + return $"({ source.Name })"; + } + } + } + } + + + } diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs index 4fb4d3370a..0a9e7a2f2b 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs @@ -6,9 +6,15 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class ContentSavedStateResolver : IValueResolver + + /// + /// Returns the for an item + /// + /// + internal class ContentSavedStateResolver : IValueResolver, ContentSavedState> + where T : ContentPropertyBasic { - public ContentSavedState Resolve(IContent source, ContentVariantDisplay destination, ContentSavedState destMember, ResolutionContext context) + public ContentSavedState Resolve(IContent source, IContentProperties destination, ContentSavedState destMember, ResolutionContext context) { PublishedState publishedState; bool isEdited; diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index caa9f29e70..4af1b18ac3 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.TreeNodeUrl, opt => opt.ResolveUsing(contentTreeNodeUrlResolver)) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs index 22d499f3d6..2b06491bc9 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.MembershipScenario, opt => opt.ResolveUsing(src => membershipScenarioMappingResolver.Resolve(src))) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -91,7 +91,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -115,7 +115,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Path, opt => opt.Ignore()) .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) - .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.State, opt => opt.UseValue(ContentSavedState.Draft)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs new file mode 100644 index 0000000000..36ae46d35d --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs @@ -0,0 +1,19 @@ +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Returns the for an item + /// + /// + internal class SavedStateResolver : IValueResolver, ContentSavedState> + where T : ContentPropertyBasic + { + public ContentSavedState Resolve(IContentBase source, IContentProperties destination, ContentSavedState destMember, ResolutionContext context) + { + return source.Id == 0 ? ContentSavedState.NotCreated : ContentSavedState.Draft; + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 771d4d7e9f..878794f0d7 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -142,6 +142,7 @@ + From ec71814ec21f3a3c3b34a71ac41702a6da31ccac Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 14 Sep 2018 11:31:43 +1000 Subject: [PATCH 6/8] Fixes up nullable State, fixes up tracking global query strings in case they are removed by other routes --- .../src/common/services/navigation.service.js | 23 +++++++++++++++++++ src/Umbraco.Web.UI.Client/src/init.js | 23 +++++++++++++++---- .../views/components/umb-content-grid.html | 4 ++-- .../Models/ContentEditing/ContentItemBasic.cs | 11 ++++++++- .../ContentEditing/ContentSavedState.cs | 5 ---- .../Models/Mapping/ContentMapperProfile.cs | 2 +- .../Mapping/ContentSavedStateResolver.cs | 15 ++++++++++++ .../Models/Mapping/MediaMapperProfile.cs | 4 ++-- .../Models/Mapping/MemberMapperProfile.cs | 4 ++-- .../Models/Mapping/SavedStateResolver.cs | 19 --------------- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 11 files changed, 73 insertions(+), 38 deletions(-) delete mode 100644 src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 3ad2864fa8..ffcf9f2eef 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -204,6 +204,29 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q }); }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#retainQueryStrings + * @methodOf umbraco.services.navigationService + * + * @description + * Will check the next route parameters to see if any of the query strings that should be retained from the previous route are missing, + * if they are they will be merged and an object containing all route parameters is returned. If nothing should be changed, then null is returned. + * @param {Object} currRouteParams The current route parameters + * @param {Object} nextRouteParams The next route parameters + */ + retainQueryStrings: function (currRouteParams, nextRouteParams) { + var toRetain = angular.copy(nextRouteParams); + var updated = false; + _.each(retainedQueryStrings, function (r) { + if (currRouteParams[r] && !nextRouteParams[r]) { + toRetain[r] = currRouteParams[r]; + updated = true; + } + }); + return updated ? toRetain : null; + }, + /** * @ngdoc method * @name umbraco.services.navigationService#load diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 50e5c21203..3f44638096 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -122,12 +122,25 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH $route.reload(); } else { - //check if the location being changed is only the mculture query string, if so, cancel the routing since this is just - //used as a global persistent query string that does not change routes. - + + //check if the location being changed is only due to global/state query strings which means the location change + //isn't actually going to cause a route change. if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) { - //continue the route - $route.reload(); + //The location change will cause a route change. We need to ensure that the global/state + //query strings have not been stripped out. If they have, we'll re-add them and re-route. + + var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params); + if (toRetain) { + $route.updateParams(toRetain); + } + else { + //continue the route + $route.reload(); + } + } + else { + //navigation is not changing but we should update the currentRouteParams to include all current parameters + currentRouteParams = angular.copy(next.params); } } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html index 30e2b81a1c..760597fcab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-content-grid.html @@ -15,7 +15,7 @@ {{ item.name }}
-
    +
    • :
      @@ -36,4 +36,4 @@ There are no items to show - \ No newline at end of file + diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index 2b0da65dc9..16eb3b9e87 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -20,6 +20,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "createDate")] public DateTime CreateDate { get; set; } + /// + /// Boolean indicating if this item is published or not based on it's + /// [DataMember(Name = "published")] public bool Published => State == ContentSavedState.Published || State == ContentSavedState.PublishedPendingChanges; @@ -42,9 +45,15 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "sortOrder")] public int SortOrder { get; set; } + /// + /// The saved/published state of an item + /// + /// + /// This is nullable since it's only relevant for content (non-content like media + members will be null) + /// [DataMember(Name = "state")] [JsonConverter(typeof(StringEnumConverter))] - public ContentSavedState State { get; set; } + public ContentSavedState? State { get; set; } protected bool Equals(ContentItemBasic other) { diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs index 58b742e238..2ceb682a13 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentSavedState.cs @@ -5,11 +5,6 @@ /// public enum ContentSavedState { - /// - /// The state has not been set - /// - NotSet = -1, - /// /// The item isn't created yet /// diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 9befb64aba..1da5bbbb44 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) .ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing()) .ForMember(dest => dest.Name, opt => opt.ResolveUsing()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing>()); + .ForMember(dest => dest.State, opt => opt.ResolveUsing>()); //FROM IContent TO ContentPropertyCollectionDto //NOTE: the property mapping for cultures relies on a culture being set in the mapping context diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs index 0a9e7a2f2b..b2b4408110 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs @@ -7,6 +7,21 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { + /// + /// Returns the for an item + /// + /// + internal class ContentBasicSavedStateResolver : IValueResolver, ContentSavedState?> + where T : ContentPropertyBasic + { + private readonly ContentSavedStateResolver _inner = new ContentSavedStateResolver(); + + public ContentSavedState? Resolve(IContent source, IContentProperties destination, ContentSavedState? destMember, ResolutionContext context) + { + return _inner.Resolve(source, destination, default, context); + } + } + /// /// Returns the for an item /// diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index 4af1b18ac3..c07d66b6e2 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.TreeNodeUrl, opt => opt.ResolveUsing(contentTreeNodeUrlResolver)) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) - .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs index 2b06491bc9..f64d8dc529 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.MembershipScenario, opt => opt.ResolveUsing(src => membershipScenarioMappingResolver.Resolve(src))) .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) @@ -91,7 +91,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) - .ForMember(dest => dest.State, opt => opt.ResolveUsing>()) + .ForMember(dest => dest.State, opt => opt.UseValue(null)) .ForMember(dest => dest.Edited, opt => opt.Ignore()) .ForMember(dest => dest.Updater, opt => opt.Ignore()) .ForMember(dest => dest.Alias, opt => opt.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs deleted file mode 100644 index 36ae46d35d..0000000000 --- a/src/Umbraco.Web/Models/Mapping/SavedStateResolver.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Returns the for an item - /// - /// - internal class SavedStateResolver : IValueResolver, ContentSavedState> - where T : ContentPropertyBasic - { - public ContentSavedState Resolve(IContentBase source, IContentProperties destination, ContentSavedState destMember, ResolutionContext context) - { - return source.Id == 0 ? ContentSavedState.NotCreated : ContentSavedState.Draft; - } - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 878794f0d7..771d4d7e9f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -142,7 +142,6 @@ - From 458b9dac4d86e48d20f42743078384982e7b1721 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 14 Sep 2018 11:47:29 +1000 Subject: [PATCH 7/8] Ensure list view is updated when cultures are changed --- .../listview/listview.controller.js | 900 +++++++++--------- 1 file changed, 454 insertions(+), 446 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 5f38f7daae..2fab250faf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,167 +1,175 @@ -function listViewController($scope, $routeParams, $injector, $location, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, mediaResource, listViewHelper, navigationService, editorService) { +function listViewController($scope, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, mediaResource, listViewHelper, navigationService, editorService) { - //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content - // that isn't created yet, if we continue this will use the parent id in the route params which isn't what - // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove - // the list view tab entirely when it's new. - if ($routeParams.create) { - $scope.isNew = true; - return; - } + //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content + // that isn't created yet, if we continue this will use the parent id in the route params which isn't what + // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove + // the list view tab entirely when it's new. + if ($routeParams.create) { + $scope.isNew = true; + return; + } - //Now we need to check if this is for media, members or content because that will depend on the resources we use - var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; + //Now we need to check if this is for media, members or content because that will depend on the resources we use + var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; - //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { - $scope.entityType = "member"; - contentResource = $injector.get('memberResource'); - getContentTypesCallback = $injector.get('memberTypeResource').getTypes; - getListResultsCallback = contentResource.getPagedResults; - deleteItemCallback = contentResource.deleteByKey; - getIdCallback = function (selected) { - return selected.key; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; - }; - } - else { - //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { - $scope.entityType = "media"; - contentResource = $injector.get('mediaResource'); - getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; - } - else { - $scope.entityType = "content"; - contentResource = $injector.get('contentResource'); - getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; - } - getListResultsCallback = contentResource.getChildren; - deleteItemCallback = contentResource.deleteById; - getIdCallback = function (selected) { - return selected.id; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + "?page=" + $scope.options.pageNumber; - }; - } + //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) + if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { + $scope.entityType = "member"; + contentResource = $injector.get('memberResource'); + getContentTypesCallback = $injector.get('memberTypeResource').getTypes; + getListResultsCallback = contentResource.getPagedResults; + deleteItemCallback = contentResource.deleteByKey; + getIdCallback = function (selected) { + return selected.key; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; + }; + } + else { + //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) + if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { + $scope.entityType = "media"; + contentResource = $injector.get('mediaResource'); + getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; + } + else { + $scope.entityType = "content"; + contentResource = $injector.get('contentResource'); + getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; + } + getListResultsCallback = contentResource.getChildren; + deleteItemCallback = contentResource.deleteById; + getIdCallback = function (selected) { + return selected.id; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + "?page=" + $scope.options.pageNumber; + }; + } - $scope.pagination = []; - $scope.isNew = false; - $scope.actionInProgress = false; - $scope.selection = []; - $scope.folders = []; - $scope.page = {}; - $scope.listViewResultSet = { - totalPages: 0, - items: [] - }; + $scope.pagination = []; + $scope.isNew = false; + $scope.actionInProgress = false; + $scope.selection = []; + $scope.folders = []; + $scope.page = {}; + $scope.listViewResultSet = { + totalPages: 0, + items: [] + }; - //when this is null, we don't check permissions - $scope.currentNodePermissions = null; + //when this is null, we don't check permissions + $scope.currentNodePermissions = null; - if ($scope.entityType === "content") { - //Just ensure we do have an editorState - if (editorState.current) { - //Fetch current node allowed actions for the current user - //This is the current node & not each individual child node in the list - var currentUserPermissions = editorState.current.allowedActions; + if ($scope.entityType === "content") { + //Just ensure we do have an editorState + if (editorState.current) { + //Fetch current node allowed actions for the current user + //This is the current node & not each individual child node in the list + var currentUserPermissions = editorState.current.allowedActions; - //Create a nicer model rather than the funky & hard to remember permissions strings - $scope.currentNodePermissions = { - "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O - "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C - "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D - "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M - "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U - "canUnpublish": _.contains(currentUserPermissions, 'U') //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) - }; - } - } + //Create a nicer model rather than the funky & hard to remember permissions strings + $scope.currentNodePermissions = { + "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O + "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C + "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D + "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M + "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U + "canUnpublish": _.contains(currentUserPermissions, 'U') //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) + }; + } + } - //when this is null, we don't check permissions - $scope.buttonPermissions = null; + //when this is null, we don't check permissions + $scope.buttonPermissions = null; - //When we are dealing with 'content', we need to deal with permissions on child nodes. - // Currently there is no real good way to - if ($scope.entityType === "content") { + //When we are dealing with 'content', we need to deal with permissions on child nodes. + // Currently there is no real good way to + if ($scope.entityType === "content") { - var idsWithPermissions = null; + var idsWithPermissions = null; - $scope.buttonPermissions = { - canCopy: true, - canCreate: true, - canDelete: true, - canMove: true, - canPublish: true, - canUnpublish: true - }; + $scope.buttonPermissions = { + canCopy: true, + canCreate: true, + canDelete: true, + canMove: true, + canPublish: true, + canUnpublish: true + }; - $scope.$watch("selection.length", function (newVal, oldVal) { + $scope.$watch("selection.length", function (newVal, oldVal) { - if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { + if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { - //get all of the selected ids - var ids = _.map($scope.selection, function (i) { - return i.id.toString(); - }); + //get all of the selected ids + var ids = _.map($scope.selection, function (i) { + return i.id.toString(); + }); - //remove the dictionary items that don't have matching ids - var filtered = {}; - _.each(idsWithPermissions, function (value, key, list) { - if (_.contains(ids, key)) { - filtered[key] = value; - } - }); - idsWithPermissions = filtered; + //remove the dictionary items that don't have matching ids + var filtered = {}; + _.each(idsWithPermissions, function (value, key, list) { + if (_.contains(ids, key)) { + filtered[key] = value; + } + }); + idsWithPermissions = filtered; - //find all ids that we haven't looked up permissions for - var existingIds = _.keys(idsWithPermissions); - var missingLookup = _.map(_.difference(ids, existingIds), function (i) { - return Number(i); - }); + //find all ids that we haven't looked up permissions for + var existingIds = _.keys(idsWithPermissions); + var missingLookup = _.map(_.difference(ids, existingIds), function (i) { + return Number(i); + }); - if (missingLookup.length > 0) { - currentUserResource.getPermissions(missingLookup).then(function (p) { - $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); - }); + if (missingLookup.length > 0) { + currentUserResource.getPermissions(missingLookup).then(function (p) { + $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + }); + } + else { + $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); + } } - else { - $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); - } - } - }); + }); - } + } - //Get the current culturename from the QueryString - to pass into the WebAPI call - var cultureNameQs = $location.search().cculture ? $location.search().cculture : $location.search().mculture; + $scope.options = { + displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, + pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, + pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1, + filter: '', + orderBy: ($scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), + orderDirection: $scope.model.config.orderDirection ? $scope.model.config.orderDirection.trim() : "desc", + orderBySystemField: true, + includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ + { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, + { alias: 'updater', header: 'Last edited by', isSystem: 1 } + ], + layout: { + layouts: $scope.model.config.layouts, + activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) + }, + allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish, + allowBulkUnpublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkUnpublish, + allowBulkCopy: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkCopy, + allowBulkMove: $scope.model.config.bulkActionPermissions.allowBulkMove, + allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete, + cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture + }; - $scope.options = { - displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, - pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, - pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1, - filter: '', - orderBy: ($scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), - orderDirection: $scope.model.config.orderDirection ? $scope.model.config.orderDirection.trim() : "desc", - orderBySystemField: true, - includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ - { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, - { alias: 'updater', header: 'Last edited by', isSystem: 1 } - ], - layout: { - layouts: $scope.model.config.layouts, - activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) - }, - allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish, - allowBulkUnpublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkUnpublish, - allowBulkCopy: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkCopy, - allowBulkMove: $scope.model.config.bulkActionPermissions.allowBulkMove, - allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete, - cultureName: cultureNameQs - }; + //watch for culture changes in the query strings and update accordingly + $scope.$watch(function () { + return $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + }, function (newVal, oldVal) { + if (newVal !== oldVal) { + //update the options + $scope.options.cultureName = newVal; + $scope.reloadView($scope.contentId); + } + }); // Check if selected order by field is actually custom field for (var j = 0; j < $scope.options.includeProperties.length; j++) { @@ -172,8 +180,8 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout } } - //update all of the system includeProperties to enable sorting - _.each($scope.options.includeProperties, function (e, i) { + //update all of the system includeProperties to enable sorting + _.each($scope.options.includeProperties, function (e, i) { //NOTE: special case for contentTypeAlias, it's a system property that cannot be sorted // to do that, we'd need to update the base query for content to include the content type alias column @@ -194,11 +202,11 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout e.header = v; }); } - }); + }); - $scope.selectLayout = function (selectedLayout) { - $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, selectedLayout, $scope.model.config.layouts); - }; + $scope.selectLayout = function (selectedLayout) { + $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, selectedLayout, $scope.model.config.layouts); + }; function showNotificationsAndReset(err, reload, successMsg) { @@ -213,10 +221,10 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }; } - $timeout(function() { - $scope.bulkStatus = ""; - $scope.actionInProgress = false; - }, + $timeout(function () { + $scope.bulkStatus = ""; + $scope.actionInProgress = false; + }, 500); if (reload === true) { @@ -225,177 +233,177 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout if (successMsg) { localizationService.localize("bulk_done") - .then(function(v) { + .then(function (v) { notificationsService.success(v, successMsg); }); } } $scope.next = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - $scope.goToPage = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.goToPage = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - $scope.prev = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.prev = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - /*Loads the search results, based on parameters set in prev,next,sort and so on*/ - /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state - with simple values */ + /*Loads the search results, based on parameters set in prev,next,sort and so on*/ + /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state + with simple values */ - $scope.getContent = function() { - $scope.reloadView($scope.contentId, true); - } + $scope.getContent = function () { + $scope.reloadView($scope.contentId, true); + } - $scope.reloadView = function (id, reloadFolders) { + $scope.reloadView = function (id, reloadFolders) { - $scope.viewLoaded = false; + $scope.viewLoaded = false; - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - getListResultsCallback(id, $scope.options).then(function (data) { + getListResultsCallback(id, $scope.options).then(function (data) { - $scope.actionInProgress = false; - $scope.listViewResultSet = data; + $scope.actionInProgress = false; + $scope.listViewResultSet = data; - //update all values for display - if ($scope.listViewResultSet.items) { - _.each($scope.listViewResultSet.items, function (e, index) { - setPropertyValues(e); - }); - } + //update all values for display + if ($scope.listViewResultSet.items) { + _.each($scope.listViewResultSet.items, function (e, index) { + setPropertyValues(e); + }); + } - if (reloadFolders && $scope.entityType === 'media') { - //The folders aren't loaded - we only need to do this once since we're never changing node ids - mediaResource.getChildFolders($scope.contentId) + if (reloadFolders && $scope.entityType === 'media') { + //The folders aren't loaded - we only need to do this once since we're never changing node ids + mediaResource.getChildFolders($scope.contentId) .then(function (page) { - $scope.folders = page.items; - $scope.viewLoaded = true; + $scope.folders = page.items; + $scope.viewLoaded = true; }); - } else { - $scope.viewLoaded = true; - } + } else { + $scope.viewLoaded = true; + } - //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example - // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last - // available page and then re-load again - if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { - $scope.options.pageNumber = $scope.listViewResultSet.totalPages; + //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example + // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last + // available page and then re-load again + if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { + $scope.options.pageNumber = $scope.listViewResultSet.totalPages; - //reload! - $scope.reloadView(id); - } + //reload! + $scope.reloadView(id); + } - }); - }; + }); + }; - var searchListView = _.debounce(function () { - $scope.$apply(function () { - makeSearch(); - }); - }, 500); - - $scope.forceSearch = function (ev) { - //13: enter - switch (ev.keyCode) { - case 13: + var searchListView = _.debounce(function () { + $scope.$apply(function () { makeSearch(); - break; - } - }; + }); + }, 500); - $scope.enterSearch = function () { - $scope.viewLoaded = false; - searchListView(); - }; + $scope.forceSearch = function (ev) { + //13: enter + switch (ev.keyCode) { + case 13: + makeSearch(); + break; + } + }; - function makeSearch() { - if ($scope.options.filter !== null && $scope.options.filter !== undefined) { - $scope.options.pageNumber = 1; - $scope.reloadView($scope.contentId); - } - } + $scope.enterSearch = function () { + $scope.viewLoaded = false; + searchListView(); + }; - $scope.isAnythingSelected = function () { - if ($scope.selection.length === 0) { - return false; - } else { - return true; - } - }; + function makeSearch() { + if ($scope.options.filter !== null && $scope.options.filter !== undefined) { + $scope.options.pageNumber = 1; + $scope.reloadView($scope.contentId); + } + } - $scope.selectedItemsCount = function () { - return $scope.selection.length; - }; + $scope.isAnythingSelected = function () { + if ($scope.selection.length === 0) { + return false; + } else { + return true; + } + }; - $scope.clearSelection = function () { - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - }; + $scope.selectedItemsCount = function () { + return $scope.selection.length; + }; - $scope.getIcon = function (entry) { - return iconHelper.convertFromLegacyIcon(entry.icon); - }; + $scope.clearSelection = function () { + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + }; - function serial(selected, fn, getStatusMsg, index) { - return fn(selected, index).then(function (content) { - index++; - $scope.bulkStatus = getStatusMsg(index, selected.length); - return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; - }, function (err) { - var reload = index > 0; - showNotificationsAndReset(err, reload); - return err; - }); - } + $scope.getIcon = function (entry) { + return iconHelper.convertFromLegacyIcon(entry.icon); + }; - function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { - var selected = $scope.selection; - if (selected.length === 0) - return; - if (confirmMsg && !confirm(confirmMsg)) - return; + function serial(selected, fn, getStatusMsg, index) { + return fn(selected, index).then(function (content) { + index++; + $scope.bulkStatus = getStatusMsg(index, selected.length); + return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; + }, function (err) { + var reload = index > 0; + showNotificationsAndReset(err, reload); + return err; + }); + } - $scope.actionInProgress = true; - $scope.bulkStatus = getStatusMsg(0, selected.length); + function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { + var selected = $scope.selection; + if (selected.length === 0) + return; + if (confirmMsg && !confirm(confirmMsg)) + return; - return serial(selected, fn, getStatusMsg, 0).then(function (result) { - // executes once the whole selection has been processed - // in case of an error (caught by serial), result will be the error - if (!(result.data && angular.isArray(result.data.notifications))) - showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); - }); - } + $scope.actionInProgress = true; + $scope.bulkStatus = getStatusMsg(0, selected.length); - $scope.delete = function() { + return serial(selected, fn, getStatusMsg, 0).then(function (result) { + // executes once the whole selection has been processed + // in case of an error (caught by serial), result will be the error + if (!(result.data && angular.isArray(result.data.notifications))) + showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); + }); + } + + $scope.delete = function () { var confirmDeleteText = ""; localizationService.localize("defaultdialogs_confirmdelete") - .then(function(value) { + .then(function (value) { confirmDeleteText = value; var attempt = applySelected( - function(selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, - function(count, total) { + function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, + function (count, total) { var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); return localizationService.localize(key, [count, total]); }, - function(total) { + function (total) { var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); return localizationService.localize(key, [total]); }, confirmDeleteText + "?"); if (attempt) { - attempt.then(function() { + attempt.then(function () { //executes if all is successful, let's sync the tree var activeNode = appState.getTreeState("selectedNode"); if (activeNode) { @@ -406,43 +414,43 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }); }; - $scope.publish = function () { + $scope.publish = function () { applySelected( - function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, - function (count, total) { - var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); - return localizationService.localize(key, [total]); - }); - }; + function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, + function (count, total) { + var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); + return localizationService.localize(key, [total]); + }); + }; - $scope.unpublish = function() { + $scope.unpublish = function () { applySelected( - function(selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, - function(count, total) { + function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, + function (count, total) { var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); return localizationService.localize(key, [count, total]); }, - function(total) { + function (total) { var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); return localizationService.localize(key, [total]); }); }; - $scope.move = function() { + $scope.move = function () { var move = { section: $scope.entityType, currentNode: $scope.contentId, - submit: function(model) { + submit: function (model) { if (model.target) { performMove(model.target); } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } } @@ -450,205 +458,205 @@ function listViewController($scope, $routeParams, $injector, $location, $timeout }; - function performMove(target) { + function performMove(target) { - //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return - // a specific value from one of the methods, so we'll have to try this way. Even though the first method - // will fire once per every node moved, the destination path will be the same and we need to use that to sync. - var newPath = null; - applySelected( - function(selected, index) { - return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) - .then(function(path) { - newPath = path; - return path; - }); - }, - function(count, total) { - var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function(total) { - var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); - return localizationService.localize(key, [total]); - }) - .then(function() { - //executes if all is successful, let's sync the tree - if (newPath) { + //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return + // a specific value from one of the methods, so we'll have to try this way. Even though the first method + // will fire once per every node moved, the destination path will be the same and we need to use that to sync. + var newPath = null; + applySelected( + function (selected, index) { + return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) + .then(function (path) { + newPath = path; + return path; + }); + }, + function (count, total) { + var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); + return localizationService.localize(key, [total]); + }) + .then(function () { + //executes if all is successful, let's sync the tree + if (newPath) { - //we need to do a double sync here: first refresh the node where the content was moved, - // then refresh the node where the content was moved from - navigationService.syncTree({ - tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), - path: newPath, - forceReload: true, - activate: false - }) - .then(function(args) { - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - if (activeNode) { - navigationService.reloadNode(activeNode); - } - }); - } - }); - } + //we need to do a double sync here: first refresh the node where the content was moved, + // then refresh the node where the content was moved from + navigationService.syncTree({ + tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), + path: newPath, + forceReload: true, + activate: false + }) + .then(function (args) { + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + if (activeNode) { + navigationService.reloadNode(activeNode); + } + }); + } + }); + } $scope.copy = function () { var copyEditor = { section: $scope.entityType, currentNode: $scope.contentId, - submit: function(model) { + submit: function (model) { if (model.target) { performCopy(model.target, model.relateToOriginal); } editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; editorService.copy(copyEditor); }; - function performCopy(target, relateToOriginal) { - applySelected( - function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, - function (count, total) { - var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); - return localizationService.localize(key, [total]); - }); - } + function performCopy(target, relateToOriginal) { + applySelected( + function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, + function (count, total) { + var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); + return localizationService.localize(key, [total]); + }); + } - function getCustomPropertyValue(alias, properties) { - var value = ''; - var index = 0; - var foundAlias = false; - for (var i = 0; i < properties.length; i++) { - if (properties[i].alias == alias) { - foundAlias = true; - break; - } - index++; - } + function getCustomPropertyValue(alias, properties) { + var value = ''; + var index = 0; + var foundAlias = false; + for (var i = 0; i < properties.length; i++) { + if (properties[i].alias == alias) { + foundAlias = true; + break; + } + index++; + } - if (foundAlias) { - value = properties[index].value; - } + if (foundAlias) { + value = properties[index].value; + } - return value; - } + return value; + } - /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ - function setPropertyValues(result) { + /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ + function setPropertyValues(result) { - //set the edit url - result.editPath = createEditUrlCallback(result); + //set the edit url + result.editPath = createEditUrlCallback(result); - _.each($scope.options.includeProperties, function (e, i) { + _.each($scope.options.includeProperties, function (e, i) { - var alias = e.alias; + var alias = e.alias; - // First try to pull the value directly from the alias (e.g. updatedBy) - var value = result[alias]; + // First try to pull the value directly from the alias (e.g. updatedBy) + var value = result[alias]; - // If this returns an object, look for the name property of that (e.g. owner.name) - if (value === Object(value)) { - value = value['name']; - } + // If this returns an object, look for the name property of that (e.g. owner.name) + if (value === Object(value)) { + value = value['name']; + } - // If we've got nothing yet, look at a user defined property - if (typeof value === 'undefined') { - value = getCustomPropertyValue(alias, result.properties); - } + // If we've got nothing yet, look at a user defined property + if (typeof value === 'undefined') { + value = getCustomPropertyValue(alias, result.properties); + } - // If we have a date, format it - if (isDate(value)) { - value = value.substring(0, value.length - 3); - } + // If we have a date, format it + if (isDate(value)) { + value = value.substring(0, value.length - 3); + } - // set what we've got on the result - result[alias] = value; - }); + // set what we've got on the result + result[alias] = value; + }); - } + } - function isDate(val) { - if (angular.isString(val)) { - return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); - } - return false; - } + function isDate(val) { + if (angular.isString(val)) { + return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); + } + return false; + } - function initView() { - //default to root id if the id is undefined - var id = $routeParams.id; - if (id === undefined) { - id = -1; - } + function initView() { + //default to root id if the id is undefined + var id = $routeParams.id; + if (id === undefined) { + id = -1; + } - getContentTypesCallback(id).then(function (items) { - $scope.listViewAllowedTypes = items; - }); + getContentTypesCallback(id).then(function (items) { + $scope.listViewAllowedTypes = items; + }); - $scope.contentId = id; - $scope.isTrashed = id === "-20" || id === "-21"; + $scope.contentId = id; + $scope.isTrashed = id === "-20" || id === "-21"; - $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; - $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; + $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; + $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; - $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || - $scope.options.allowBulkUnpublish || - $scope.options.allowBulkCopy || - $scope.options.allowBulkMove || - $scope.options.allowBulkDelete; + $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || + $scope.options.allowBulkUnpublish || + $scope.options.allowBulkCopy || + $scope.options.allowBulkMove || + $scope.options.allowBulkDelete; - $scope.reloadView($scope.contentId, true); - } + $scope.reloadView($scope.contentId, true); + } - function getLocalizedKey(alias) { + function getLocalizedKey(alias) { - switch (alias) { - case "sortOrder": - return "general_sort"; - case "updateDate": - return "content_updateDate"; - case "updater": - return "content_updatedBy"; - case "createDate": - return "content_createDate"; - case "owner": - return "content_createBy"; - case "published": - return "content_isPublished"; - case "contentTypeAlias": - //TODO: Check for members - return $scope.entityType === "content" ? "content_documentType" : "content_mediatype"; - case "email": - return "general_email"; - case "username": - return "general_username"; - } - return alias; - } + switch (alias) { + case "sortOrder": + return "general_sort"; + case "updateDate": + return "content_updateDate"; + case "updater": + return "content_updatedBy"; + case "createDate": + return "content_createDate"; + case "owner": + return "content_createBy"; + case "published": + return "content_isPublished"; + case "contentTypeAlias": + //TODO: Check for members + return $scope.entityType === "content" ? "content_documentType" : "content_mediatype"; + case "email": + return "general_email"; + case "username": + return "general_username"; + } + return alias; + } - function getItemKey(itemId) { - for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { - var item = $scope.listViewResultSet.items[i]; - if (item.id === itemId) { - return item.key; - } - } - } + function getItemKey(itemId) { + for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { + var item = $scope.listViewResultSet.items[i]; + if (item.id === itemId) { + return item.key; + } + } + } - //GO! - initView(); + //GO! + initView(); } From b62477ec3b114137487922db522437eba8024339 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 14 Sep 2018 11:52:59 +1000 Subject: [PATCH 8/8] null check --- .../src/views/propertyeditors/listview/listview.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 2fab250faf..e1a1299c55 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -164,7 +164,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs $scope.$watch(function () { return $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; }, function (newVal, oldVal) { - if (newVal !== oldVal) { + if (newVal && newVal !== oldVal) { //update the options $scope.options.cultureName = newVal; $scope.reloadView($scope.contentId);