Gets all list views working correctly with content apps, fixes various issues, cleans up a bunch of code and reduces amount of service locator.
This commit is contained in:
@@ -205,8 +205,6 @@ namespace Umbraco.Core.Migrations.Install
|
||||
{
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) });
|
||||
//TODO: Need a migration to remove this
|
||||
//_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Contents) });
|
||||
//membership property group
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) });
|
||||
}
|
||||
@@ -221,8 +219,6 @@ namespace Umbraco.Core.Migrations.Install
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
//TODO: Need a migration to remove this
|
||||
//_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = Constants.DataTypes.DefaultMediaListView, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
//membership property types
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
|
||||
|
||||
@@ -389,6 +389,8 @@ namespace Umbraco.Examine
|
||||
//the value of the field 'as-is'.
|
||||
foreach (var value in e.IndexItem.ValueSet.Values.ToList()) //ToList here to make a diff collection else we'll get collection modified errors
|
||||
{
|
||||
if (value.Value == null) continue;
|
||||
|
||||
if (value.Value.Count > 0)
|
||||
{
|
||||
if (value.Value.First() is string str)
|
||||
|
||||
@@ -113,9 +113,6 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
|
||||
|
||||
var nextPath = nextLocation.split("#")[1];
|
||||
|
||||
//TODO: We need to check for the query strings that cause the route, if they are just the
|
||||
//nonRoutingQueryStrings when we shouldn't show the confirm route change
|
||||
|
||||
if (nextPath) {
|
||||
|
||||
if (navigationService.isRouteChangingNavigation(currentLocation, nextLocation)) {
|
||||
|
||||
@@ -22,18 +22,9 @@ function ContentRecycleBinController($scope, $routeParams, contentResource, navi
|
||||
|
||||
$routeParams.id = "-20";
|
||||
contentResource.getRecycleBin().then(function (result) {
|
||||
//we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
|
||||
// single property, so we'll extract that property (list view) and use it's data.
|
||||
var listproperty = result.tabs[0].properties[0];
|
||||
|
||||
_.each(listproperty.config, function (val, key) {
|
||||
$scope.model.config[key] = val;
|
||||
});
|
||||
$scope.listViewPath = 'views/propertyeditors/listview/listview.html';
|
||||
$scope.content = result;
|
||||
});
|
||||
|
||||
$scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
|
||||
|
||||
// sync tree node
|
||||
navigationService.syncTree({ tree: "content", path: ["-1", $routeParams.id], forceReload: false });
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<umb-editor-view>
|
||||
<form ng-controller="Umbraco.Editors.Content.RecycleBinController">
|
||||
<form ng-controller="Umbraco.Editors.Content.RecycleBinController" val-form-manager>
|
||||
|
||||
<umb-editor-header
|
||||
name="page.name"
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
<umb-editor-header name="page.name"
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
<ng-include
|
||||
src="listViewPath">
|
||||
</ng-include>
|
||||
<div class="umb-editor-sub-views">
|
||||
<div id="sub-view-{{$index}}" ng-repeat="app in content.apps track by app.alias">
|
||||
<umb-editor-sub-view model="app" content="content" />
|
||||
</div>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
|
||||
</form>
|
||||
</umb-editor-view>
|
||||
</umb-editor-view>
|
||||
|
||||
@@ -22,18 +22,9 @@ function MediaRecycleBinController($scope, $routeParams, mediaResource, navigati
|
||||
|
||||
$routeParams.id = "-21";
|
||||
mediaResource.getRecycleBin().then(function (result) {
|
||||
//we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
|
||||
// single property, so we'll extract that property (list view) and use it's data.
|
||||
var listproperty = result.tabs[0].properties[0];
|
||||
|
||||
_.each(listproperty.config, function (val, key) {
|
||||
$scope.model.config[key] = val;
|
||||
});
|
||||
$scope.listViewPath = 'views/propertyeditors/listview/listview.html';
|
||||
$scope.content = result;
|
||||
});
|
||||
|
||||
$scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
|
||||
|
||||
// sync tree node
|
||||
navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false });
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<umb-editor-view>
|
||||
<form ng-controller="Umbraco.Editors.Media.RecycleBinController">
|
||||
|
||||
<umb-editor-header
|
||||
name="page.name"
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
<form ng-controller="Umbraco.Editors.Media.RecycleBinController" val-form-manager>
|
||||
|
||||
<umb-editor-header name="page.name"
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
|
||||
<umb-editor-container>
|
||||
<div ng-include="listViewPath"></div>
|
||||
<div class="umb-editor-sub-views">
|
||||
<div id="sub-view-{{$index}}" ng-repeat="app in content.apps track by app.alias">
|
||||
<umb-editor-sub-view model="app" content="content" />
|
||||
</div>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -4,33 +4,26 @@
|
||||
|
||||
<form novalidate name="contentForm" ng-show="!page.loading" val-form-manager>
|
||||
|
||||
<umb-editor-view umb-tabs>
|
||||
<umb-editor-view umb-tabs>
|
||||
|
||||
<umb-editor-header
|
||||
name="content.name"
|
||||
name-locked="page.lockedName"
|
||||
tabs="content.tabs"
|
||||
menu="page.menu"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
<umb-editor-header name="content.name"
|
||||
name-locked="page.lockedName"
|
||||
tabs="content.tabs"
|
||||
menu="page.menu"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
<umb-editor-container>
|
||||
<div class="umb-editor-sub-views">
|
||||
<div id="sub-view-{{$index}}" ng-repeat="app in content.apps track by app.alias">
|
||||
<umb-editor-sub-view model="app" content="content" />
|
||||
</div>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-tabs-content view="true" class="form-horizontal">
|
||||
<umb-tab id="tab{{tab.id}}" rel="{{tab.id}}" ng-repeat="tab in content.tabs">
|
||||
|
||||
<umb-property property="property" ng-repeat="property in tab.properties">
|
||||
<umb-property-editor model="property"></umb-property-editor>
|
||||
</umb-property>
|
||||
|
||||
</umb-tab>
|
||||
</umb-tabs-content>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
</umb-editor-view>
|
||||
</umb-editor-view>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<div>
|
||||
|
||||
<umb-property ng-repeat="property in model.viewModel track by property.alias" property="property">
|
||||
<umb-property-editor model="property"></umb-property-editor>
|
||||
</umb-property>
|
||||
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-section ng-if="(listViewAllowedTypes && listViewAllowedTypes.length > 0 && !isAnythingSelected()) && currentNodePermissions.canCreate">
|
||||
<umb-editor-sub-header-section ng-if="(listViewAllowedTypes && listViewAllowedTypes.length > 0 && !isAnythingSelected()) && (entityType != 'content' || currentNodePermissions.canCreate)">
|
||||
<div class="btn-group" ng-show="listViewAllowedTypes.length > 1">
|
||||
<a class="btn btn-success dropdown-toggle" ng-click="page.createDropdownOpen = !page.createDropdownOpen" ng-href="">
|
||||
<localize key="actions_create">Create</localize>
|
||||
|
||||
@@ -30,6 +30,7 @@ using Umbraco.Web._Legacy.Actions;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -46,11 +47,13 @@ namespace Umbraco.Web.Editors
|
||||
public class ContentController : ContentControllerBase
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
|
||||
public ContentController(IPublishedSnapshotService publishedSnapshotService)
|
||||
public ContentController(IPublishedSnapshotService publishedSnapshotService, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService));
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -215,14 +218,14 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
public ContentItemDisplay GetRecycleBin()
|
||||
{
|
||||
var apps = new List<ContentApp>();
|
||||
apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, "recycleBin", "content");
|
||||
apps[0].Active = true;
|
||||
var display = new ContentItemDisplay
|
||||
{
|
||||
Id = Constants.System.RecycleBinContent,
|
||||
//Alias = "recycleBin",
|
||||
ParentId = -1,
|
||||
//Name = Services.TextService.Localize("general/recycleBin"),
|
||||
ContentTypeAlias = "recycleBin",
|
||||
//CreateDate = DateTime.Now,
|
||||
IsContainer = true,
|
||||
Path = "-1," + Constants.System.RecycleBinContent,
|
||||
ContentVariants = new List<ContentVariantDisplay>
|
||||
@@ -232,12 +235,10 @@ namespace Umbraco.Web.Editors
|
||||
CreateDate = DateTime.Now,
|
||||
Name = Services.TextService.Localize("general/recycleBin")
|
||||
}
|
||||
}
|
||||
},
|
||||
ContentApps = apps
|
||||
};
|
||||
|
||||
//TODO: Change this over to use "Content Apps"
|
||||
TabsAndPropertiesResolver.AddListView(display.ContentVariants.First(), "content", "recycleBin", Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -46,6 +47,11 @@ namespace Umbraco.Web.Editors
|
||||
[MediaControllerControllerConfiguration]
|
||||
public class MediaController : ContentControllerBase
|
||||
{
|
||||
public MediaController(PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures this controller with a custom action selector
|
||||
/// </summary>
|
||||
@@ -89,6 +95,9 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
public MediaItemDisplay GetRecycleBin()
|
||||
{
|
||||
var apps = new List<ContentApp>();
|
||||
apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, "recycleBin", "media");
|
||||
apps[0].Active = true;
|
||||
var display = new MediaItemDisplay
|
||||
{
|
||||
Id = Constants.System.RecycleBinMedia,
|
||||
@@ -98,12 +107,10 @@ namespace Umbraco.Web.Editors
|
||||
ContentTypeAlias = "recycleBin",
|
||||
CreateDate = DateTime.Now,
|
||||
IsContainer = true,
|
||||
Path = "-1," + Constants.System.RecycleBinMedia
|
||||
Path = "-1," + Constants.System.RecycleBinMedia,
|
||||
ContentApps = apps
|
||||
};
|
||||
|
||||
//TODO: Change this over to use "Content Apps"
|
||||
TabsAndPropertiesResolver.AddListView(display, "media", "recycleBin", Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
@@ -222,6 +229,8 @@ namespace Umbraco.Web.Editors
|
||||
#region GetChildren
|
||||
|
||||
private int[] _userStartNodes;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
|
||||
protected int[] UserStartNodes
|
||||
{
|
||||
get { return _userStartNodes ?? (_userStartNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService)); }
|
||||
|
||||
@@ -26,6 +26,8 @@ using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Binders;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -38,7 +40,13 @@ namespace Umbraco.Web.Editors
|
||||
[OutgoingNoHyphenGuidFormat]
|
||||
public class MemberController : ContentControllerBase
|
||||
{
|
||||
public MemberController(PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
|
||||
}
|
||||
|
||||
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently configured membership scenario for members in umbraco
|
||||
@@ -127,6 +135,10 @@ namespace Umbraco.Web.Editors
|
||||
var foundType = Services.MemberTypeService.Get(listName);
|
||||
var name = foundType != null ? foundType.Name : listName;
|
||||
|
||||
var apps = new List<ContentApp>();
|
||||
apps.AppendListViewApp(Services.DataTypeService, _propertyEditors, listName, "member");
|
||||
apps[0].Active = true;
|
||||
|
||||
var display = new MemberListDisplay
|
||||
{
|
||||
ContentTypeAlias = listName,
|
||||
@@ -135,12 +147,10 @@ namespace Umbraco.Web.Editors
|
||||
IsContainer = true,
|
||||
Name = listName == Constants.Conventions.MemberTypes.AllMembersListId ? "All Members" : name,
|
||||
Path = "-1," + listName,
|
||||
ParentId = -1
|
||||
ParentId = -1,
|
||||
ContentApps = apps
|
||||
};
|
||||
|
||||
//TODO: Change this over to use "Content Apps"
|
||||
TabsAndPropertiesResolver.AddListView(display, "member", listName, Services.DataTypeService, Services.TextService);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// </summary>
|
||||
[DataMember(Name = "viewModel")]
|
||||
public object ViewModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normally reserved for Angular to deal with but in some cases this can be set on the server side
|
||||
/// </summary>
|
||||
[DataMember(Name = "active")]
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
@@ -9,5 +10,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class MemberListDisplay : ContentItemDisplayBase<ContentPropertyDisplay, IMember>
|
||||
{
|
||||
[DataMember(Name = "apps")]
|
||||
public IEnumerable<ContentApp> ContentApps { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
if (source.ContentType.IsContainer)
|
||||
{
|
||||
//If it's a container then add the list view app and view model
|
||||
apps.Add(this.CreateListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "content"));
|
||||
apps.AppendListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "content");
|
||||
}
|
||||
|
||||
return apps;
|
||||
|
||||
@@ -11,17 +11,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal static class ContentAppResolverExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension method to create a list view app for the content app collection
|
||||
/// Helper method to append a list view app to the content app collection
|
||||
/// </summary>
|
||||
/// <typeparam name="TContent"></typeparam>
|
||||
/// <typeparam name="TDisplay"></typeparam>
|
||||
/// <param name="resolver"></param>
|
||||
public static ContentApp CreateListViewApp<TContent, TDisplay>(
|
||||
this IValueResolver<TContent, TDisplay, IEnumerable<ContentApp>> resolver,
|
||||
public static void AppendListViewApp(
|
||||
this ICollection<ContentApp> list,
|
||||
IDataTypeService dataTypeService, PropertyEditorCollection propertyEditors,
|
||||
string contentTypeAlias,
|
||||
string entityType)
|
||||
where TContent: IContentBase
|
||||
string contentTypeAlias, string entityType)
|
||||
{
|
||||
var listViewApp = new ContentApp
|
||||
{
|
||||
@@ -74,7 +70,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
};
|
||||
|
||||
return listViewApp;
|
||||
list.Add(listViewApp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
var apps = new List<ContentApp>();
|
||||
|
||||
if (source.ContentType.IsContainer || source.ContentType.Alias == Umbraco.Core.Constants.Conventions.MediaTypes.Folder)
|
||||
if (source.ContentType.IsContainer || source.ContentType.Alias == Core.Constants.Conventions.MediaTypes.Folder)
|
||||
{
|
||||
apps.Add(this.CreateListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "media"));
|
||||
apps.AppendListViewApp(_dataTypeService, _propertyEditorCollection, source.ContentType.Alias, "media");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -54,12 +54,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ContentType, opt => opt.ResolveUsing(mediaTypeBasicResolver))
|
||||
.ForMember(dest => dest.MediaLink, opt => opt.ResolveUsing(content => string.Join(",", content.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger))))
|
||||
.ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(mediaAppResolver))
|
||||
.AfterMap((media, display) =>
|
||||
{
|
||||
//if (media.ContentType.IsContainer)
|
||||
// TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, textService);
|
||||
});
|
||||
.ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(mediaAppResolver));
|
||||
|
||||
//FROM IMedia TO ContentItemBasic<ContentPropertyBasic, IMedia>
|
||||
CreateMap<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>()
|
||||
|
||||
@@ -26,84 +26,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
IgnoreProperties = ignoreProperties ?? throw new ArgumentNullException(nameof(ignoreProperties));
|
||||
}
|
||||
|
||||
//TODO: Get rid of this it should not be needed anymore since list views are dynamically added to "content apps"
|
||||
internal static void AddListView(
|
||||
ITabbedContent<ContentPropertyDisplay> display,
|
||||
string contentTypeAlias, string entityType,
|
||||
IDataTypeService dataTypeService, ILocalizedTextService localizedTextService)
|
||||
{
|
||||
int dtdId;
|
||||
var customDtdName = Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias;
|
||||
switch (entityType)
|
||||
{
|
||||
case "content":
|
||||
dtdId = Constants.DataTypes.DefaultContentListView;
|
||||
break;
|
||||
case "media":
|
||||
dtdId = Constants.DataTypes.DefaultMediaListView;
|
||||
break;
|
||||
case "member":
|
||||
dtdId = Constants.DataTypes.DefaultMembersListView;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(entityType), "entityType does not match a required value");
|
||||
}
|
||||
|
||||
//first try to get the custom one if there is one
|
||||
var dt = dataTypeService.GetDataType(customDtdName)
|
||||
?? dataTypeService.GetDataType(dtdId);
|
||||
|
||||
if (dt == null)
|
||||
{
|
||||
throw new InvalidOperationException("No list view data type was found for this document type, ensure that the default list view data types exists and/or that your custom list view data type exists");
|
||||
}
|
||||
|
||||
var editor = Current.PropertyEditors[dt.EditorAlias];
|
||||
if (editor == null)
|
||||
{
|
||||
throw new NullReferenceException("The property editor with alias " + dt.EditorAlias + " does not exist");
|
||||
}
|
||||
|
||||
//TODO: We need to move this logic elsewhere, we don't want to have to add a list view to a tab that will
|
||||
// get removed later and magically move to the ListView ContentApp. The ListView ContentApp will need to
|
||||
// manage this data somehow so a content app will also need to be able to specify it's own model.
|
||||
|
||||
//var listViewTab = new Tab<ContentPropertyDisplay>
|
||||
//{
|
||||
// Alias = Constants.Conventions.PropertyGroups.ListViewGroupName,
|
||||
// Label = localizedTextService.Localize("content/childItems"),
|
||||
// Id = display.Tabs.Count() + 1,
|
||||
// IsActive = true
|
||||
//};
|
||||
|
||||
//var listViewConfig = editor.GetConfigurationEditor().ToConfigurationEditor(dt.Configuration);
|
||||
////add the entity type to the config
|
||||
//listViewConfig["entityType"] = entityType;
|
||||
|
||||
////Override Tab Label if tabName is provided
|
||||
//if (listViewConfig.ContainsKey("tabName"))
|
||||
//{
|
||||
// var configTabName = listViewConfig["tabName"];
|
||||
// if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false)
|
||||
// listViewTab.Label = configTabName.ToString();
|
||||
//}
|
||||
|
||||
//var listViewProperties = new List<ContentPropertyDisplay>();
|
||||
//listViewProperties.Add(new ContentPropertyDisplay
|
||||
//{
|
||||
// Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}containerView",
|
||||
// Label = "",
|
||||
// Value = null,
|
||||
// View = editor.GetValueEditor().View,
|
||||
// HideLabel = true,
|
||||
// Config = listViewConfig
|
||||
//});
|
||||
//listViewTab.Properties = listViewProperties;
|
||||
|
||||
//SetChildItemsTabPosition(display, listViewConfig, listViewTab);
|
||||
}
|
||||
|
||||
|
||||
private static int GetTabNumberFromConfig(IDictionary<string, object> listViewConfig)
|
||||
{
|
||||
if (!listViewConfig.TryGetValue("displayAtTabNumber", out var displayTabNum))
|
||||
|
||||
@@ -43,7 +43,20 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
where TPersisted : class, IContentBase
|
||||
where TModelSave : ContentBaseItemSave<TPersisted>
|
||||
{
|
||||
protected ServiceContext Services => Current.Services; // fixme - inject
|
||||
protected Core.Logging.ILogger Logger { get; }
|
||||
protected ServiceContext Services { get; }
|
||||
protected IUmbracoContextAccessor UmbracoContextAccessor { get; }
|
||||
|
||||
public ContentItemBaseBinder() : this(Current.Logger, Current.Services, Current.UmbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentItemBaseBinder(Core.Logging.ILogger logger, ServiceContext services, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
Logger = logger;
|
||||
Services = services;
|
||||
UmbracoContextAccessor = umbracoContextAccessor;
|
||||
}
|
||||
|
||||
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
|
||||
{
|
||||
@@ -58,29 +71,18 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
Directory.CreateDirectory(root);
|
||||
var provider = new MultipartFormDataStreamProvider(root);
|
||||
|
||||
var task = Task.Run(() => GetModelAsync(actionContext, bindingContext, provider))
|
||||
.ContinueWith(x =>
|
||||
{
|
||||
if (x.IsFaulted && x.Exception != null)
|
||||
{
|
||||
throw x.Exception;
|
||||
}
|
||||
|
||||
//now that everything is binded, validate the properties
|
||||
var contentItemValidator = GetValidationHelper();
|
||||
contentItemValidator.ValidateItem(actionContext, x.Result);
|
||||
|
||||
bindingContext.Model = x.Result;
|
||||
});
|
||||
|
||||
task.Wait();
|
||||
var model = GetModel(actionContext, bindingContext, provider);
|
||||
//now that everything is binded, validate the properties
|
||||
var contentItemValidator = GetValidationHelper();
|
||||
contentItemValidator.ValidateItem(actionContext, model);
|
||||
bindingContext.Model = model;
|
||||
|
||||
return bindingContext.Model != null;
|
||||
}
|
||||
|
||||
protected virtual ContentItemValidationHelper<TPersisted, TModelSave> GetValidationHelper()
|
||||
{
|
||||
return new ContentItemValidationHelper<TPersisted, TModelSave>();
|
||||
return new ContentItemValidationHelper<TPersisted, TModelSave>(Logger, UmbracoContextAccessor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -90,29 +92,13 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// <param name="bindingContext"></param>
|
||||
/// <param name="provider"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<TModelSave> GetModelAsync(HttpActionContext actionContext, ModelBindingContext bindingContext, MultipartFormDataStreamProvider provider)
|
||||
private TModelSave GetModel(HttpActionContext actionContext, ModelBindingContext bindingContext, MultipartFormDataStreamProvider provider)
|
||||
{
|
||||
// note
|
||||
//
|
||||
// for some reason, due to the way we do async, HttpContext.Current is null
|
||||
// which means that the 'current' UmbracoContext is null too since we are using HttpContextUmbracoContextAccessor
|
||||
// trying to 'EnsureContext' fails because that accessor cannot access the HttpContext either to register the current UmbracoContext
|
||||
//
|
||||
// so either we go with an HybridUmbracoContextAccessor that relies on a ThreadStatic variable when HttpContext.Current is null
|
||||
// and I don't like it
|
||||
// or
|
||||
// we try to force-set the current http context, because, hey... it's there.
|
||||
// and then there is no need to event 'Ensure' anything
|
||||
// read http://stackoverflow.com/questions/1992141/how-do-i-get-an-httpcontext-object-from-httpcontextbase-in-asp-net-mvc-1
|
||||
|
||||
// what's below works but I cannot say I am proud of it
|
||||
var request = actionContext.Request;
|
||||
var httpContext = (HttpContextBase) request.Properties["MS_HttpContext"];
|
||||
HttpContext.Current = httpContext.ApplicationInstance.Context;
|
||||
|
||||
var content = request.Content;
|
||||
|
||||
var result = await content.ReadAsMultipartAsync(provider);
|
||||
var result = content.ReadAsMultipartAsync(provider).Result;
|
||||
|
||||
if (result.FormData["contentItem"] == null)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,10 @@ using System.Net.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
@@ -14,9 +17,18 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class ContentItemBinder : ContentItemBaseBinder<IContent, ContentItemSave>
|
||||
{
|
||||
public ContentItemBinder() : this(Current.Logger, Current.Services, Current.UmbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentItemBinder(Core.Logging.ILogger logger, ServiceContext services, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
: base(logger, services, umbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ContentItemValidationHelper<IContent, ContentItemSave> GetValidationHelper()
|
||||
{
|
||||
return new ContentValidationHelper();
|
||||
return new ContentValidationHelper(Logger, UmbracoContextAccessor);
|
||||
}
|
||||
|
||||
protected override IContent GetExisting(ContentItemSave model)
|
||||
@@ -49,6 +61,10 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
|
||||
internal class ContentValidationHelper : ContentItemValidationHelper<IContent, ContentItemSave>
|
||||
{
|
||||
public ContentValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the correct information is in the request for saving a culture variant
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
|
||||
@@ -9,6 +11,15 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class MediaItemBinder : ContentItemBaseBinder<IMedia, MediaItemSave>
|
||||
{
|
||||
public MediaItemBinder() : this(Current.Logger, Current.Services, Current.UmbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public MediaItemBinder(Core.Logging.ILogger logger, ServiceContext services, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
: base(logger, services, umbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IMedia GetExisting(MediaItemSave model)
|
||||
{
|
||||
return Services.MediaService.GetById(Convert.ToInt32(model.Id));
|
||||
|
||||
@@ -19,14 +19,25 @@ using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class MemberBinder : ContentItemBaseBinder<IMember, MemberSave>
|
||||
{
|
||||
|
||||
public MemberBinder() : this(Current.Logger, Current.Services, Current.UmbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberBinder(ILogger logger, ServiceContext services, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
: base(logger, services, umbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ContentItemValidationHelper<IMember, MemberSave> GetValidationHelper()
|
||||
{
|
||||
return new MemberValidationHelper();
|
||||
return new MemberValidationHelper(Logger, UmbracoContextAccessor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,6 +190,10 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// </summary>
|
||||
internal class MemberValidationHelper : ContentItemValidationHelper<IMember, MemberSave>
|
||||
{
|
||||
public MemberValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We need to manually validate a few things here like email and login to make sure they are valid and aren't duplicates
|
||||
/// </summary>
|
||||
@@ -244,12 +259,9 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
propertiesToValidate.RemoveAll(property => property.Alias == remove);
|
||||
}
|
||||
|
||||
|
||||
var umbCtx = Current.UmbracoContext; // fixme inject?
|
||||
|
||||
//if the user doesn't have access to sensitive values, then we need to validate the incoming properties to check
|
||||
//if a sensitive value is being submitted.
|
||||
if (umbCtx.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
if (UmbracoContextAccessor.UmbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
var sensitiveProperties = postedItem.PersistedContent.ContentType
|
||||
.PropertyTypes.Where(x => postedItem.PersistedContent.ContentType.IsSensitiveProperty(x.Alias))
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
where TPersisted : class, IContentBase
|
||||
where TModelSave : ContentBaseItemSave<TPersisted>
|
||||
{
|
||||
protected IUmbracoContextAccessor UmbracoContextAccessor { get; }
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
public ContentItemValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
UmbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the content item and updates the Response and ModelState accordingly
|
||||
@@ -94,7 +102,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
/// <param name="persistedProperties"></param>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <returns></returns>
|
||||
protected bool ValidateProperties(List<ContentPropertyBasic> postedProperties , List<Property> persistedProperties, HttpActionContext actionContext)
|
||||
protected bool ValidateProperties(List<ContentPropertyBasic> postedProperties, List<Property> persistedProperties, HttpActionContext actionContext)
|
||||
{
|
||||
foreach (var p in postedProperties)
|
||||
{
|
||||
@@ -131,7 +139,8 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
if (editor == null)
|
||||
{
|
||||
var message = $"Could not find property editor \"{p.DataType.EditorAlias}\" for property with id {p.Id}.";
|
||||
Current.Logger.Warn<ContentItemValidationHelper<TPersisted, TModelSave>>(message);
|
||||
|
||||
Logger.Warn<ContentItemValidationHelper<TPersisted, TModelSave>>(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user