Got media controllers working just like content, have tried to share as much code as possible between them. Can probably do a bit better but there's minimal code replication.
This commit is contained in:
@@ -1055,6 +1055,7 @@ namespace Umbraco.Core.Services
|
||||
uow.Commit();
|
||||
|
||||
//Special case for the Upload DataType
|
||||
//TODO: Should we handle this with events?
|
||||
var uploadDataTypeId = new Guid(Constants.PropertyEditors.UploadField);
|
||||
if (content.Properties.Any(x => x.PropertyType.DataTypeId == uploadDataTypeId))
|
||||
{
|
||||
|
||||
@@ -6,23 +6,12 @@
|
||||
|
||||
<div class="span8">
|
||||
<div class="btn-toolbar pull-right umb-btn-toolbar">
|
||||
|
||||
<div class="btn-group">
|
||||
<a class="btn" ng-click="preview(content)"
|
||||
data-shortcut="ctrl+s">Preview page</a>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-success" href="#" ng-click="save(content)" data-shortcut="ctrl+s"
|
||||
prevent-default>Save</a>
|
||||
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-success" href="#" ng-click="saveAndPublish(content)"
|
||||
prevent-default>Publish</a>
|
||||
|
||||
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
|
||||
<li><a href="#" ng-click="save(content)"
|
||||
prevent-default data-shortcut="ctrl+s">Save draft</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function mediaEditController($scope, $routeParams, mediaResource, notificationsService) {
|
||||
function mediaEditController($scope, $routeParams, mediaResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper) {
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
@@ -13,6 +13,13 @@ function mediaEditController($scope, $routeParams, mediaResource, notificationsS
|
||||
.then(function (data) {
|
||||
$scope.contentLoaded = true;
|
||||
$scope.content = data;
|
||||
|
||||
//in one particular special case, after we've created a new item we redirect back to the edit
|
||||
// route but there might be server validation errors in the collection which we need to display
|
||||
// after the redirect, so we will bind all subscriptions which will show the server validation errors
|
||||
// if there are any and then clear them so the collection no longer persists them.
|
||||
serverValidationManager.executeAndClearAllSubscriptions();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,22 +35,27 @@ function mediaEditController($scope, $routeParams, mediaResource, notificationsS
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: Clean this up and share this code with the content editor
|
||||
$scope.saveAndPublish = function (cnt) {
|
||||
mediaResource.saveMedia(cnt, $routeParams.create, $scope.files)
|
||||
.then(function (data) {
|
||||
$scope.content = data;
|
||||
notificationsService.success("Published", "Media has been saved and published");
|
||||
});
|
||||
};
|
||||
//ensure there is a form object assigned.
|
||||
var currentForm = angularHelper.getRequiredCurrentForm($scope);
|
||||
|
||||
$scope.save = function (cnt) {
|
||||
|
||||
$scope.$broadcast("saving", { scope: $scope });
|
||||
|
||||
//don't continue if the form is invalid
|
||||
if (currentForm.$invalid) return;
|
||||
|
||||
serverValidationManager.reset();
|
||||
|
||||
//TODO: Clean this up and share this code with the content editor
|
||||
$scope.save = function (cnt) {
|
||||
mediaResource.saveMedia(cnt, $routeParams.create, $scope.files)
|
||||
.then(function (data) {
|
||||
$scope.content = data;
|
||||
notificationsService.success("Saved", "Media has been saved");
|
||||
});
|
||||
contentEditingHelper.handleSuccessfulSave({
|
||||
scope: $scope,
|
||||
newContent: data
|
||||
});
|
||||
}, function (err) {
|
||||
contentEditingHelper.handleSaveError(err, $scope);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -777,7 +777,7 @@ To manage your website, simply open the umbraco back office and start adding con
|
||||
<key alias="editContentSendToPublish">Sent For Approval</key>
|
||||
<key alias="editContentSendToPublishText">Changes have been sent for approval</key>
|
||||
<key alias="editMediaSaved">Media saved</key>
|
||||
<key alias="editMediaSavedText"></key>
|
||||
<key alias="editMediaSavedText">Media saved without any errors</key>
|
||||
<key alias="editMemberSaved">Member saved</key>
|
||||
<key alias="editStylesheetPropertySaved">Stylesheet Property Saved</key>
|
||||
<key alias="editStylesheetSaved">Stylesheet saved</key>
|
||||
|
||||
@@ -763,7 +763,7 @@ To manage your website, simply open the umbraco back office and start adding con
|
||||
<key alias="editContentSendToPublish">Sent For Approval</key>
|
||||
<key alias="editContentSendToPublishText">Changes have been sent for approval</key>
|
||||
<key alias="editMediaSaved">Media saved</key>
|
||||
<key alias="editMediaSavedText"></key>
|
||||
<key alias="editMediaSavedText">Media saved without any errors</key>
|
||||
<key alias="editMemberSaved">Member saved</key>
|
||||
<key alias="editStylesheetPropertySaved">Stylesheet Property Saved</key>
|
||||
<key alias="editStylesheetSaved">Stylesheet saved</key>
|
||||
|
||||
@@ -22,11 +22,94 @@ using umbraco;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
public abstract class ContentControllerBase : UmbracoAuthorizedJsonController
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
protected ContentControllerBase()
|
||||
: this(UmbracoContext.Current)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
protected ContentControllerBase(UmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
protected void HandleContentNotFound(int id)
|
||||
{
|
||||
ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id));
|
||||
var errorResponse = Request.CreateErrorResponse(
|
||||
HttpStatusCode.NotFound,
|
||||
ModelState);
|
||||
throw new HttpResponseException(errorResponse);
|
||||
}
|
||||
|
||||
protected void UpdateName<TPersisted>(ContentItemSave<TPersisted> contentItem)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
//Don't update the name if it is empty
|
||||
if (!contentItem.Name.IsNullOrWhiteSpace())
|
||||
{
|
||||
contentItem.PersistedContent.Name = contentItem.Name;
|
||||
}
|
||||
}
|
||||
|
||||
protected void MapPropertyValues<TPersisted>(ContentItemSave<TPersisted> contentItem)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
//Map the property values
|
||||
foreach (var p in contentItem.ContentDto.Properties)
|
||||
{
|
||||
//get the dbo property
|
||||
var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
|
||||
|
||||
//create the property data to send to the property editor
|
||||
var d = new Dictionary<string, object>();
|
||||
//add the files if any
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray();
|
||||
if (files.Any())
|
||||
{
|
||||
d.Add("files", files);
|
||||
}
|
||||
var data = new ContentPropertyData(p.Value, d);
|
||||
|
||||
//get the deserialized value from the property editor
|
||||
if (p.PropertyEditor == null)
|
||||
{
|
||||
LogHelper.Warn<ContentController>("No property editor found for property " + p.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleInvalidModelState<T, TPersisted>(ContentItemDisplayBase<T, TPersisted> display)
|
||||
where TPersisted : IContentBase
|
||||
where T : ContentPropertyBasic
|
||||
{
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
display.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, display));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The API controller used for editing content
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
public class ContentController : UmbracoAuthorizedJsonController
|
||||
public class ContentController : ContentControllerBase
|
||||
{
|
||||
private readonly ContentModelMapper _contentModelMapper;
|
||||
|
||||
@@ -66,11 +149,7 @@ namespace Umbraco.Web.Editors
|
||||
var foundContent = Services.ContentService.GetById(id);
|
||||
if (foundContent == null)
|
||||
{
|
||||
ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id));
|
||||
var errorResponse = Request.CreateErrorResponse(
|
||||
HttpStatusCode.NotFound,
|
||||
ModelState);
|
||||
throw new HttpResponseException(errorResponse);
|
||||
HandleContentNotFound(id);
|
||||
}
|
||||
return _contentModelMapper.ToContentItemDisplay(foundContent);
|
||||
}
|
||||
@@ -107,43 +186,14 @@ namespace Umbraco.Web.Editors
|
||||
// * and validated
|
||||
// * any file attachments have been saved to their temporary location for us to use
|
||||
// * we have a reference to the DTO object and the persisted object
|
||||
|
||||
//Don't update the name if it is empty
|
||||
if (!contentItem.Name.IsNullOrWhiteSpace())
|
||||
{
|
||||
contentItem.PersistedContent.Name = contentItem.Name;
|
||||
}
|
||||
|
||||
UpdateName(contentItem);
|
||||
|
||||
//TODO: We need to support 'send to publish'
|
||||
|
||||
//TODO: We'll need to save the new template, publishat, etc... values here
|
||||
|
||||
//Map the property values
|
||||
foreach (var p in contentItem.ContentDto.Properties)
|
||||
{
|
||||
//get the dbo property
|
||||
var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
|
||||
|
||||
//create the property data to send to the property editor
|
||||
var d = new Dictionary<string, object>();
|
||||
//add the files if any
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray();
|
||||
if (files.Any())
|
||||
{
|
||||
d.Add("files", files);
|
||||
}
|
||||
var data = new ContentPropertyData(p.Value, d);
|
||||
|
||||
//get the deserialized value from the property editor
|
||||
if (p.PropertyEditor == null)
|
||||
{
|
||||
LogHelper.Warn<ContentController>("No property editor found for property " + p.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value);
|
||||
}
|
||||
}
|
||||
MapPropertyValues(contentItem);
|
||||
|
||||
//We need to manually check the validation results here because:
|
||||
// * We still need to save the entity even if there are validation value errors
|
||||
@@ -191,12 +241,9 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//return the updated model
|
||||
var display = _contentModelMapper.ToContentItemDisplay(contentItem.PersistedContent);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
display.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, display));
|
||||
}
|
||||
HandleInvalidModelState(display);
|
||||
|
||||
//put the correct msgs in
|
||||
switch (contentItem.Action)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
@@ -14,6 +15,7 @@ using Umbraco.Web.WebApi;
|
||||
using System.Linq;
|
||||
using Umbraco.Web.WebApi.Binders;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using umbraco;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -30,7 +32,7 @@ namespace Umbraco.Web.Editors
|
||||
//}
|
||||
|
||||
[PluginController("UmbracoApi")]
|
||||
public class MediaController : UmbracoAuthorizedJsonController
|
||||
public class MediaController : ContentControllerBase
|
||||
{
|
||||
private readonly MediaModelMapper _mediaModelMapper;
|
||||
|
||||
@@ -67,7 +69,7 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var emptyContent = new Umbraco.Core.Models.Media("Empty", parentId, contentType);
|
||||
var emptyContent = new Core.Models.Media("Empty", parentId, contentType);
|
||||
return _mediaModelMapper.ToMediaItemDisplay(emptyContent);
|
||||
}
|
||||
|
||||
@@ -81,11 +83,7 @@ namespace Umbraco.Web.Editors
|
||||
var foundContent = Services.MediaService.GetById(id);
|
||||
if (foundContent == null)
|
||||
{
|
||||
ModelState.AddModelError("id", string.Format("media with id: {0} was not found", id));
|
||||
var errorResponse = Request.CreateErrorResponse(
|
||||
HttpStatusCode.NotFound,
|
||||
ModelState);
|
||||
throw new HttpResponseException(errorResponse);
|
||||
HandleContentNotFound(id);
|
||||
}
|
||||
return _mediaModelMapper.ToMediaItemDisplay(foundContent);
|
||||
}
|
||||
@@ -123,35 +121,26 @@ namespace Umbraco.Web.Editors
|
||||
// * any file attachments have been saved to their temporary location for us to use
|
||||
// * we have a reference to the DTO object and the persisted object
|
||||
|
||||
//Now, we just need to save the data
|
||||
UpdateName(contentItem);
|
||||
|
||||
contentItem.PersistedContent.Name = contentItem.Name;
|
||||
//TODO: We'll need to save the new template, publishat, etc... values here
|
||||
MapPropertyValues(contentItem);
|
||||
|
||||
//Save the property values (for properties that have a valid editor ... not legacy)
|
||||
foreach (var p in contentItem.ContentDto.Properties.Where(x => x.PropertyEditor != null))
|
||||
//We need to manually check the validation results here because:
|
||||
// * We still need to save the entity even if there are validation value errors
|
||||
// * Depending on if the entity is new, and if there are non property validation errors (i.e. the name is null)
|
||||
// then we cannot continue saving, we can only display errors
|
||||
// * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display
|
||||
// a message indicating this
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
//get the dbo property
|
||||
var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
|
||||
|
||||
//create the property data to send to the property editor
|
||||
var d = new Dictionary<string, object>();
|
||||
//add the files if any
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray();
|
||||
if (files.Any())
|
||||
if (ValidationHelper.ModelHasRequiredForPersistenceErrors(contentItem)
|
||||
&& (contentItem.Action == ContentSaveAction.SaveNew))
|
||||
{
|
||||
d.Add("files", files);
|
||||
}
|
||||
var data = new ContentPropertyData(p.Value, d);
|
||||
|
||||
//get the deserialized value from the property editor
|
||||
if (p.PropertyEditor == null)
|
||||
{
|
||||
LogHelper.Warn<MediaController>("No property editor found for property " + p.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value);
|
||||
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
||||
// add the modelstate to the outgoing object and throw a 403
|
||||
var forDisplay = _mediaModelMapper.ToMediaItemDisplay(contentItem.PersistedContent);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, forDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +148,21 @@ namespace Umbraco.Web.Editors
|
||||
Services.MediaService.Save(contentItem.PersistedContent);
|
||||
|
||||
//return the updated model
|
||||
return _mediaModelMapper.ToMediaItemDisplay(contentItem.PersistedContent);
|
||||
var display = _mediaModelMapper.ToMediaItemDisplay(contentItem.PersistedContent);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
|
||||
//put the correct msgs in
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
case ContentSaveAction.SaveNew:
|
||||
display.AddSuccessNotification(ui.Text("speechBubbles", "editMediaSaved"), ui.Text("speechBubbles", "editMediaSavedText"));
|
||||
break;
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -13,33 +12,11 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// A model representing a content item to be displayed in the back office
|
||||
/// </summary>
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class ContentItemDisplay : TabbedContentItem<ContentPropertyDisplay, IContent>, INotificationModel
|
||||
public class ContentItemDisplay : ContentItemDisplayBase<ContentPropertyDisplay, IContent>
|
||||
{
|
||||
public ContentItemDisplay()
|
||||
{
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
|
||||
[DataMember(Name = "publishDate")]
|
||||
public DateTime? PublishDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for validation of a content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will
|
||||
/// still save the item but it cannot be published. So we need a way of returning validation errors as well as the
|
||||
/// updated model.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "modelState")]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
public abstract class ContentItemDisplayBase<T, TPersisted> : TabbedContentItem<T, TPersisted>, INotificationModel, IErrorModel
|
||||
where T : ContentPropertyBasic
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
protected ContentItemDisplayBase()
|
||||
{
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for validation of a content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will
|
||||
/// still save the item but it cannot be published. So we need a way of returning validation errors as well as the
|
||||
/// updated model.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "modelState")]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
17
src/Umbraco.Web/Models/ContentEditing/IErrorModel.cs
Normal file
17
src/Umbraco.Web/Models/ContentEditing/IErrorModel.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
public interface IErrorModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used for validation of a content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will
|
||||
/// still save the item but it cannot be published. So we need a way of returning validation errors as well as the
|
||||
/// updated model.
|
||||
/// </remarks>
|
||||
IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// A model representing a content item to be displayed in the back office
|
||||
/// </summary>
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class MediaItemDisplay : TabbedContentItem<ContentPropertyDisplay, IMedia>
|
||||
public class MediaItemDisplay : ContentItemDisplayBase<ContentPropertyDisplay, IMedia>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ using Umbraco.Core.Models;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
public abstract class TabbedContentItem<T, TPersisted> : ContentItemBasic<T, TPersisted>
|
||||
where T : ContentPropertyBasic where TPersisted : IContentBase
|
||||
where T : ContentPropertyBasic
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
protected TabbedContentItem()
|
||||
{
|
||||
|
||||
@@ -305,8 +305,10 @@
|
||||
<Compile Include="Editors\UmbracoAuthorizedJsonController.cs" />
|
||||
<Compile Include="Editors\ValidationHelper.cs" />
|
||||
<Compile Include="HttpCookieExtensions.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentItemDisplayBase.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentSaveAction.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentTypeBasic.cs" />
|
||||
<Compile Include="Models\ContentEditing\IErrorModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\IHaveUploadedFiles.cs" />
|
||||
<Compile Include="Models\ContentEditing\INotificationModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\MediaItemDisplay.cs" />
|
||||
|
||||
Reference in New Issue
Block a user