Start updating the model/controller for editing variants in the back office and wiring up the angular side of things
This commit is contained in:
@@ -17,7 +17,6 @@ namespace Umbraco.Core.Models
|
||||
private ITemplate _template;
|
||||
private bool _published;
|
||||
private PublishedState _publishedState;
|
||||
private string _language;
|
||||
private DateTime? _releaseDate;
|
||||
private DateTime? _expireDate;
|
||||
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
|
||||
@@ -79,7 +78,6 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo<Content, ITemplate>(x => x.Template);
|
||||
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.Published);
|
||||
public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.Language);
|
||||
public readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ReleaseDate);
|
||||
public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ExpireDate);
|
||||
public readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo<Content, string>(x => x.NodeName);
|
||||
@@ -149,7 +147,7 @@ namespace Umbraco.Core.Models
|
||||
/// is true or false, but can also temporarily be Publishing or Unpublishing when the
|
||||
/// content item is about to be saved.</remarks>
|
||||
[DataMember]
|
||||
internal PublishedState PublishedState
|
||||
public PublishedState PublishedState
|
||||
{
|
||||
get => _publishedState;
|
||||
set
|
||||
@@ -163,16 +161,6 @@ namespace Umbraco.Core.Models
|
||||
[IgnoreDataMember]
|
||||
public bool Edited { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Language of the data contained within this Content object.
|
||||
/// </summary>
|
||||
[Obsolete("This is not used and will be removed from the codebase in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public string Language
|
||||
{
|
||||
get => _language;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _language, Ps.Value.LanguageSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date this Content should be released and thus be published
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
bool Published { get; }
|
||||
|
||||
PublishedState PublishedState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content has been edited.
|
||||
/// </summary>
|
||||
@@ -54,11 +56,7 @@ namespace Umbraco.Core.Models
|
||||
/// Gets the date and time the content was published.
|
||||
/// </summary>
|
||||
DateTime? PublishDate { get; }
|
||||
|
||||
[Obsolete("This will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
string Language { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time the content item should be published.
|
||||
/// </summary>
|
||||
|
||||
@@ -194,7 +194,6 @@ namespace Umbraco.Tests.Models
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Language = "en";
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
@@ -254,7 +253,6 @@ namespace Umbraco.Tests.Models
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Language = "en";
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
@@ -294,7 +292,6 @@ namespace Umbraco.Tests.Models
|
||||
Assert.AreEqual(clone.CreatorId, content.CreatorId);
|
||||
Assert.AreEqual(clone.ExpireDate, content.ExpireDate);
|
||||
Assert.AreEqual(clone.Key, content.Key);
|
||||
Assert.AreEqual(clone.Language, content.Language);
|
||||
Assert.AreEqual(clone.Level, content.Level);
|
||||
Assert.AreEqual(clone.Path, content.Path);
|
||||
Assert.AreEqual(clone.ReleaseDate, content.ReleaseDate);
|
||||
@@ -355,7 +352,6 @@ namespace Umbraco.Tests.Models
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Language = "en";
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
|
||||
@@ -53,61 +53,6 @@
|
||||
// We don't get the info tab from the server from version 7.8 so we need to manually add it
|
||||
//contentEditingHelper.addInfoTab($scope.content.tabs);
|
||||
|
||||
// prototype variants
|
||||
$scope.content.variants = [
|
||||
{
|
||||
"cultureDisplayName": "English (United States)",
|
||||
"culture": "en-US",
|
||||
"current": true,
|
||||
"segments" : [
|
||||
{
|
||||
"name": "Mobile"
|
||||
},
|
||||
{
|
||||
"name": "Job campign"
|
||||
}
|
||||
],
|
||||
"state": "Published"
|
||||
},
|
||||
{
|
||||
"cultureDisplayName": "Danish",
|
||||
"culture": "da-DK",
|
||||
"current": false,
|
||||
"segments" : [
|
||||
{
|
||||
"name": "Mobile"
|
||||
}
|
||||
],
|
||||
"state": "Published"
|
||||
},
|
||||
{
|
||||
"cultureDisplayName": "Spanish (Spain)",
|
||||
"culture": "es-ES",
|
||||
"current": false,
|
||||
"state": "Published (pending changes)"
|
||||
},
|
||||
{
|
||||
"cultureDisplayName": "French (France)",
|
||||
"culture": "fr-FR",
|
||||
"current": false,
|
||||
"segments" : [
|
||||
{
|
||||
"name": "Mobile"
|
||||
},
|
||||
{
|
||||
"name": "Job campign"
|
||||
}
|
||||
],
|
||||
"state": "Draft"
|
||||
},
|
||||
{
|
||||
"cultureDisplayName": "German (Germany)",
|
||||
"culture": "de-DE",
|
||||
"current": false,
|
||||
"state": "Draft"
|
||||
}
|
||||
];
|
||||
|
||||
// prototype content and info apps
|
||||
var contentApp = {
|
||||
"name": "Content",
|
||||
|
||||
+2
-2
@@ -136,13 +136,13 @@
|
||||
}
|
||||
|
||||
// published node
|
||||
if(node.hasPublishedVersion === true && node.publishDate && node.published === true) {
|
||||
if(node.publishDate && node.published === true) {
|
||||
scope.publishStatus.label = localizationService.localize("content_published");
|
||||
scope.publishStatus.color = "success";
|
||||
}
|
||||
|
||||
// published node with pending changes
|
||||
if(node.hasPublishedVersion === true && node.publishDate && node.published === false) {
|
||||
if (node.edited === true && node.publishDate) {
|
||||
scope.publishStatus.label = localizationService.localize("content_publishedPendingChanges");
|
||||
scope.publishStatus.color = "success"
|
||||
}
|
||||
|
||||
@@ -50,15 +50,15 @@
|
||||
server-validation-field="Alias">
|
||||
</umb-generate-alias>
|
||||
|
||||
<a ng-if="variants.length > 0" class="umb-variant-switcher__toggle" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen">
|
||||
<span>{{vm.currentVariant.cultureDisplayName}}</span>
|
||||
<a ng-if="variants.length > 1" class="umb-variant-switcher__toggle" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen">
|
||||
<span>{{vm.currentVariant.language.name}}</span>
|
||||
<ins class="umb-variant-switcher__expand" ng-class="{'icon-navigation-down': !vm.dropdownOpen, 'icon-navigation-up': vm.dropdownOpen}"> </ins>
|
||||
</a>
|
||||
|
||||
<umb-dropdown ng-if="vm.dropdownOpen" style="width: 100%; max-height: 250px; overflow-y: scroll; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
|
||||
<umb-dropdown-item class="umb-variant-switcher__item" ng-class="{'umb-variant-switcher_item--current': variant.current}" ng-repeat="variant in variants">
|
||||
<a href="" class="umb-variant-switcher__name-wrapper" ng-click="selectVariant($event, variant)" prevent-default>
|
||||
<span class="umb-variant-switcher__name">{{variant.cultureDisplayName}}</span>
|
||||
<span class="umb-variant-switcher__name">{{variant.language.name}}</span>
|
||||
<span class="umb-variant-switcher__state">{{variant.state}}</span>
|
||||
</a>
|
||||
<div ng-if="splitViewOpen !== true" class="umb-variant-switcher__split-view" ng-click="openInSplitView($event, variant)">Open in split view</div>
|
||||
@@ -115,4 +115,4 @@
|
||||
view="dialogModel.view">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
protected HttpResponseMessage HandleContentNotFound(object id, bool throwException = true)
|
||||
{
|
||||
ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id));
|
||||
ModelState.AddModelError("id", $"content with id: {id} was not found");
|
||||
var errorResponse = Request.CreateErrorResponse(
|
||||
HttpStatusCode.NotFound,
|
||||
ModelState);
|
||||
|
||||
@@ -23,6 +23,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the content item is a draft
|
||||
/// </summary>
|
||||
[DataMember(Name = "edited")]
|
||||
public bool Edited { get; set; }
|
||||
|
||||
[DataMember(Name = "owner")]
|
||||
public UserProfile Owner { get; set; }
|
||||
@@ -75,8 +81,8 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "properties")]
|
||||
public virtual IEnumerable<T> Properties
|
||||
{
|
||||
get { return _properties; }
|
||||
set { _properties = value; }
|
||||
get => _properties;
|
||||
set => _properties = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,8 +23,14 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[DataMember(Name = "removeDate")]
|
||||
public DateTime? ExpireDate { get; set; }
|
||||
public DateTime? ExpireDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents the variant info for a content item
|
||||
/// </summary>
|
||||
[DataMember(Name = "variants")]
|
||||
public IEnumerable<ContentVariation> Variants { get; set; }
|
||||
|
||||
[DataMember(Name = "template")]
|
||||
public string TemplateAlias { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the variant info for a content item
|
||||
/// </summary>
|
||||
[DataContract(Name = "contentVariant", Namespace = "")]
|
||||
public class ContentVariation
|
||||
{
|
||||
/// <summary>
|
||||
/// The content name of the variant
|
||||
/// </summary>
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "language")]
|
||||
public Language Language { get; set; }
|
||||
|
||||
[DataMember(Name = "segment")]
|
||||
public string Segment { get; set; }
|
||||
|
||||
//fixme not sure if we need these dates as metadata for displaying the variant info in the drop down?
|
||||
// when we move to being able to edit all variants and switching then this might be irrelevant
|
||||
|
||||
[DataMember(Name = "publishDate")]
|
||||
public DateTime? PublishDate { get; set; }
|
||||
|
||||
[DataMember(Name = "releaseDate")]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[DataMember(Name = "removeDate")]
|
||||
public DateTime? ExpireDate { get; set; }
|
||||
|
||||
[DataMember(Name = "state")]
|
||||
public string PublishedState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the content variant for this culture has been created
|
||||
/// </summary>
|
||||
[DataMember(Name = "exists")]
|
||||
public bool Exists { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this is the variant currently being edited
|
||||
/// </summary>
|
||||
[DataMember(Name = "current")]
|
||||
public bool IsCurrent { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,13 @@ namespace Umbraco.Web.Models
|
||||
{
|
||||
public static class ContentExtensions
|
||||
{
|
||||
public static bool HasVariation(this IContent content, int langId, string segment = null)
|
||||
{
|
||||
//TODO: Wire up with new APIs
|
||||
return false;
|
||||
//return content.Languages.FirstOrDefault(x => x == langId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture that would be selected to render a specified content,
|
||||
/// within the context of a specified current request.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
internal class ContentMapperProfile : Profile
|
||||
{
|
||||
public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService)
|
||||
public ContentMapperProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, ILocalizationService localizationService)
|
||||
{
|
||||
// create, capture, cache
|
||||
var contentOwnerResolver = new OwnerResolver<IContent>(userService);
|
||||
@@ -25,12 +25,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver<IContent, ContentTreeController>();
|
||||
var defaultTemplateResolver = new DefaultTemplateResolver();
|
||||
var contentUrlResolver = new ContentUrlResolver();
|
||||
var variantResolver = new VariationResolver(localizationService);
|
||||
|
||||
//FROM IContent TO ContentItemDisplay
|
||||
CreateMap<IContent, ContentItemDisplay>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key)))
|
||||
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.Variants, opt => opt.ResolveUsing(variantResolver))
|
||||
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
|
||||
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
|
||||
.ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name))
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using ContentVariation = Umbraco.Web.Models.ContentEditing.ContentVariation;
|
||||
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class VariationResolver : IValueResolver<IContent, ContentItemDisplay, IEnumerable<ContentVariation>>
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public VariationResolver(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
|
||||
}
|
||||
|
||||
public IEnumerable<ContentVariation> Resolve(IContent source, ContentItemDisplay destination, IEnumerable<ContentVariation> destMember, ResolutionContext context)
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariation>(); //there's only 1 language defined so we don't have language variants enabled
|
||||
|
||||
var langs = Mapper.Map<IEnumerable<Language>>(allLanguages).ToList();
|
||||
var variants = langs.Select(x => new ContentVariation
|
||||
{
|
||||
Language = x,
|
||||
//fixme these all need to the variant values but we need to wait for the db/service changes
|
||||
Name = source.Name ,
|
||||
ExpireDate = source.ExpireDate,
|
||||
PublishDate = source.PublishDate,
|
||||
ReleaseDate = source.ReleaseDate,
|
||||
Exists = source.HasVariation(x.Id),
|
||||
PublishedState = source.PublishedState.ToString()
|
||||
}).ToList();
|
||||
|
||||
//if there's only one language, by default it is the default
|
||||
if (langs.Count == 1)
|
||||
{
|
||||
langs[0].IsDefaultVariantLanguage = true;
|
||||
langs[0].Mandatory = true;
|
||||
}
|
||||
else if (allLanguages.All(x => !x.IsDefaultVariantLanguage))
|
||||
{
|
||||
//if no language has the default flag, then the defaul language is the one with the lowest id
|
||||
langs[0].IsDefaultVariantLanguage = true;
|
||||
langs[0].Mandatory = true;
|
||||
}
|
||||
|
||||
//TODO: Not sure if this is required right now, IsCurrent could purely be a UI thing, we'll see
|
||||
//set the 'current'
|
||||
variants.First(x => x.Language.IsDefaultVariantLanguage).IsCurrent = true;
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -224,6 +224,7 @@
|
||||
<Compile Include="Media\ImageUrlProviderCollectionBuilder.cs" />
|
||||
<Compile Include="Media\ThumbnailProviders\ThumbnailProviderCollection.cs" />
|
||||
<Compile Include="Media\ThumbnailProviders\ThumbnailProviderCollectionBuilder.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentVariation.cs" />
|
||||
<Compile Include="Models\ContentEditing\EditorNavigation.cs" />
|
||||
<Compile Include="Models\ContentEditing\GetAvailableCompositionsFilter.cs" />
|
||||
<Compile Include="Editors\IEditorValidator.cs" />
|
||||
@@ -266,6 +267,7 @@
|
||||
<Compile Include="Models\Mapping\RedirectUrlMapperProfile.cs" />
|
||||
<Compile Include="Models\Mapping\TemplateMapperProfile.cs" />
|
||||
<Compile Include="Models\Mapping\UserGroupDefaultPermissionsResolver.cs" />
|
||||
<Compile Include="Models\Mapping\VariationResolver.cs" />
|
||||
<Compile Include="Models\RelatedLink.cs" />
|
||||
<Compile Include="Models\RelatedLinkBase.cs" />
|
||||
<Compile Include="Models\RelatedLinks.cs" />
|
||||
|
||||
Reference in New Issue
Block a user