Merge pull request #11452 from umbraco/v9/feature/v8_merge_22_10_2021
Merge v8 into v9 22/10/2021
This commit is contained in:
@@ -673,19 +673,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
edited = true;
|
||||
}
|
||||
|
||||
// To establish the new value of "edited" we compare all properties publishedValue to editedValue and look
|
||||
// for differences.
|
||||
//
|
||||
// If we SaveAndPublish but the publish fails (e.g. already scheduled for release)
|
||||
// we have lost the publishedValue on IContent (in memory vs database) so we cannot correctly make that comparison.
|
||||
//
|
||||
// This is a slight change to behaviour, historically a publish, followed by change & save, followed by undo change & save
|
||||
// would change edited back to false.
|
||||
if (!publishing && editedSnapshot)
|
||||
{
|
||||
edited = true;
|
||||
}
|
||||
|
||||
if (entity.ContentType.VariesByCulture())
|
||||
{
|
||||
// names also impact 'edited'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
@@ -16,12 +17,11 @@ using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.BackOffice.Filters;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Am abstract API controller providing functionality used for dealing with content and media types
|
||||
/// Am abstract API controller providing functionality used for dealing with content and media types
|
||||
/// </summary>
|
||||
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
|
||||
[PrefixlessBodyModelValidator]
|
||||
@@ -39,13 +39,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
IUmbracoMapper umbracoMapper,
|
||||
ILocalizedTextService localizedTextService)
|
||||
{
|
||||
_editorValidatorCollection = editorValidatorCollection ?? throw new ArgumentNullException(nameof(editorValidatorCollection));
|
||||
_editorValidatorCollection = editorValidatorCollection ??
|
||||
throw new ArgumentNullException(nameof(editorValidatorCollection));
|
||||
CultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary));
|
||||
ContentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService));
|
||||
MediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService));
|
||||
MemberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
|
||||
UmbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
|
||||
LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
||||
LocalizedTextService =
|
||||
localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
||||
}
|
||||
|
||||
protected ICultureDictionary CultureDictionary { get; }
|
||||
@@ -56,22 +58,26 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
public ILocalizedTextService LocalizedTextService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the available composite content types for a given content type
|
||||
/// Returns the available composite content types for a given content type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="filterContentTypes">
|
||||
/// This is normally an empty list but if additional content type aliases are passed in, any content types containing those aliases will be filtered out
|
||||
/// along with any content types that have matching property types that are included in the filtered content types
|
||||
/// This is normally an empty list but if additional content type aliases are passed in, any content types containing
|
||||
/// those aliases will be filtered out
|
||||
/// along with any content types that have matching property types that are included in the filtered content types
|
||||
/// </param>
|
||||
/// <param name="filterPropertyTypes">
|
||||
/// This is normally an empty list but if additional property type aliases are passed in, any content types that have these aliases will be filtered out.
|
||||
/// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot
|
||||
/// be looked up via the db, they need to be passed in.
|
||||
/// This is normally an empty list but if additional property type aliases are passed in, any content types that have
|
||||
/// these aliases will be filtered out.
|
||||
/// This is required because in the case of creating/modifying a content type because new property types being added to
|
||||
/// it are not yet persisted so cannot
|
||||
/// be looked up via the db, they need to be passed in.
|
||||
/// </param>
|
||||
/// <param name="contentTypeId"></param>
|
||||
/// <param name="isElement">Whether the composite content types should be applicable for an element type</param>
|
||||
/// <returns></returns>
|
||||
protected ActionResult<IEnumerable<Tuple<EntityBasic, bool>>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
||||
protected ActionResult<IEnumerable<Tuple<EntityBasic, bool>>> PerformGetAvailableCompositeContentTypes(
|
||||
int contentTypeId,
|
||||
UmbracoObjectTypes type,
|
||||
string[] filterContentTypes,
|
||||
string[] filterPropertyTypes,
|
||||
@@ -89,26 +95,38 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source = ContentTypeService.Get(contentTypeId);
|
||||
if (source == null) return NotFound();
|
||||
if (source == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
allContentTypes = ContentTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source =MediaTypeService.Get(contentTypeId);
|
||||
if (source == null) return NotFound();
|
||||
source = MediaTypeService.Get(contentTypeId);
|
||||
if (source == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
allContentTypes =MediaTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
|
||||
allContentTypes = MediaTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source = MemberTypeService.Get(contentTypeId);
|
||||
if (source == null) return NotFound();
|
||||
if (source == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
allContentTypes = MemberTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
@@ -116,16 +134,20 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
throw new ArgumentOutOfRangeException("The entity type was not a content type");
|
||||
}
|
||||
|
||||
var availableCompositions = ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes, filterPropertyTypes, isElement);
|
||||
ContentTypeAvailableCompositionsResults availableCompositions =
|
||||
ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes,
|
||||
filterPropertyTypes, isElement);
|
||||
|
||||
|
||||
|
||||
var currCompositions = source == null ? new IContentTypeComposition[] { } : source.ContentTypeComposition.ToArray();
|
||||
IContentTypeComposition[] currCompositions =
|
||||
source == null ? new IContentTypeComposition[] { } : source.ContentTypeComposition.ToArray();
|
||||
var compAliases = currCompositions.Select(x => x.Alias).ToArray();
|
||||
var ancestors = availableCompositions.Ancestors.Select(x => x.Alias);
|
||||
IEnumerable<string> ancestors = availableCompositions.Ancestors.Select(x => x.Alias);
|
||||
|
||||
return availableCompositions.Results
|
||||
.Select(x => new Tuple<EntityBasic, bool>(UmbracoMapper.Map<IContentTypeComposition, EntityBasic>(x.Composition), x.Allowed))
|
||||
.Select(x =>
|
||||
new Tuple<EntityBasic, bool>(UmbracoMapper.Map<IContentTypeComposition, EntityBasic>(x.Composition),
|
||||
x.Allowed))
|
||||
.Select(x =>
|
||||
{
|
||||
//we need to ensure that the item is enabled if it is already selected
|
||||
@@ -139,9 +161,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
//translate the name
|
||||
x.Item1.Name = TranslateItem(x.Item1.Name);
|
||||
|
||||
var contentType = allContentTypes.FirstOrDefault(c => c.Key == x.Item1.Key);
|
||||
var containers = GetEntityContainers(contentType, type)?.ToArray();
|
||||
var containerPath = $"/{(containers != null && containers.Any() ? $"{string.Join("/", containers.Select(c => c.Name))}/" : null)}";
|
||||
IContentTypeComposition contentType = allContentTypes.FirstOrDefault(c => c.Key == x.Item1.Key);
|
||||
EntityContainer[] containers = GetEntityContainers(contentType, type)?.ToArray();
|
||||
var containerPath =
|
||||
$"/{(containers != null && containers.Any() ? $"{string.Join("/", containers.Select(c => c.Name))}/" : null)}";
|
||||
x.Item1.AdditionalData["containerPath"] = containerPath;
|
||||
|
||||
return x;
|
||||
@@ -149,7 +172,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<EntityContainer> GetEntityContainers(IContentTypeComposition contentType, UmbracoObjectTypes type)
|
||||
private IEnumerable<EntityContainer> GetEntityContainers(IContentTypeComposition contentType,
|
||||
UmbracoObjectTypes type)
|
||||
{
|
||||
if (contentType == null)
|
||||
{
|
||||
@@ -170,12 +194,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of content types where a particular composition content type is used
|
||||
/// Returns a list of content types where a particular composition content type is used
|
||||
/// </summary>
|
||||
/// <param name="type">Type of content Type, eg documentType or mediaType</param>
|
||||
/// <param name="contentTypeId">Id of composition content type</param>
|
||||
/// <returns></returns>
|
||||
protected ActionResult<IEnumerable<EntityBasic>> PerformGetWhereCompositionIsUsedInContentTypes(int contentTypeId, UmbracoObjectTypes type)
|
||||
protected ActionResult<IEnumerable<EntityBasic>> PerformGetWhereCompositionIsUsedInContentTypes(
|
||||
int contentTypeId, UmbracoObjectTypes type)
|
||||
{
|
||||
var id = 0;
|
||||
|
||||
@@ -190,7 +215,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
source =MediaTypeService.Get(contentTypeId);
|
||||
source = MediaTypeService.Get(contentTypeId);
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
@@ -202,7 +227,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
id = source.Id;
|
||||
}
|
||||
@@ -216,7 +243,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
composedOf =MediaTypeService.GetComposedOf(id);
|
||||
composedOf = MediaTypeService.GetComposedOf(id);
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
@@ -242,10 +269,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
protected string TranslateItem(string text)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (text.StartsWith("#") == false)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
text = text.Substring(1);
|
||||
return CultureDictionary[text].IfNullOrWhiteSpace(text);
|
||||
@@ -261,18 +292,22 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
{
|
||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
||||
var ct = ctId > 0 ? getContentType(ctId) : null;
|
||||
if (ctId > 0 && ct == null) return NotFound();
|
||||
TContentType ct = ctId > 0 ? getContentType(ctId) : null;
|
||||
if (ctId > 0 && ct == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
//Validate that there's no other ct with the same alias
|
||||
// it in fact cannot be the same as any content type alias (member, content or media) because
|
||||
// this would interfere with how ModelsBuilder works and also how many of the published caches
|
||||
// works since that is based on aliases.
|
||||
var allAliases = ContentTypeService.GetAllContentTypeAliases();
|
||||
IEnumerable<string> allAliases = ContentTypeService.GetAllContentTypeAliases();
|
||||
var exists = allAliases.InvariantContains(contentTypeSave.Alias);
|
||||
if (exists && (ctId == 0 || !ct.Alias.InvariantEquals(contentTypeSave.Alias)))
|
||||
{
|
||||
ModelState.AddModelError("Alias", LocalizedTextService.Localize("editcontenttype", "aliasAlreadyExists"));
|
||||
ModelState.AddModelError("Alias",
|
||||
LocalizedTextService.Localize("editcontenttype", "aliasAlreadyExists"));
|
||||
}
|
||||
|
||||
// execute the external validators
|
||||
@@ -280,13 +315,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
var err = CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(ctId, contentTypeSave, ct);
|
||||
TContentTypeDisplay err =
|
||||
CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(ctId, contentTypeSave, ct);
|
||||
return ValidationProblem(err);
|
||||
}
|
||||
|
||||
//filter out empty properties
|
||||
contentTypeSave.Groups = contentTypeSave.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
foreach (var group in contentTypeSave.Groups)
|
||||
foreach (PropertyGroupBasic<TPropertyType> group in contentTypeSave.Groups)
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
@@ -302,12 +338,22 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var responseEx = CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave, TPropertyType>(ex, contentTypeSave, ct, ctId);
|
||||
if (responseEx != null) return ValidationProblem(responseEx);
|
||||
TContentTypeDisplay responseEx =
|
||||
CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave, TPropertyType>(
|
||||
ex, contentTypeSave, ct, ctId);
|
||||
if (responseEx != null)
|
||||
{
|
||||
return ValidationProblem(responseEx);
|
||||
}
|
||||
}
|
||||
|
||||
var exResult = CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(contentTypeSave, ct);
|
||||
if (exResult != null) return ValidationProblem(exResult);
|
||||
TContentTypeDisplay exResult =
|
||||
CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(
|
||||
contentTypeSave, ct);
|
||||
if (exResult != null)
|
||||
{
|
||||
return ValidationProblem(exResult);
|
||||
}
|
||||
|
||||
saveContentType(ct);
|
||||
|
||||
@@ -329,7 +375,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
allowIfselfAsChildSortOrder = contentTypeSave.AllowedContentTypes.IndexOf(0);
|
||||
allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0);
|
||||
|
||||
contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList();
|
||||
contentTypeSave.AllowedContentTypes =
|
||||
contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList();
|
||||
}
|
||||
|
||||
//save as new
|
||||
@@ -342,15 +389,24 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var responseEx = CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave, TPropertyType>(ex, contentTypeSave, ct, ctId);
|
||||
TContentTypeDisplay responseEx =
|
||||
CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave, TPropertyType>(
|
||||
ex, contentTypeSave, ct, ctId);
|
||||
if (responseEx is null)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return ValidationProblem(responseEx);
|
||||
}
|
||||
|
||||
var exResult = CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(contentTypeSave, newCt);
|
||||
if (exResult != null) return ValidationProblem(exResult);
|
||||
TContentTypeDisplay exResult =
|
||||
CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(
|
||||
contentTypeSave, newCt);
|
||||
if (exResult != null)
|
||||
{
|
||||
return ValidationProblem(exResult);
|
||||
}
|
||||
|
||||
//set id to null to ensure its handled as a new type
|
||||
contentTypeSave.Id = null;
|
||||
@@ -364,30 +420,33 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
newCt.AllowedContentTypes =
|
||||
newCt.AllowedContentTypes.Union(
|
||||
new []{ new ContentTypeSort(newCt.Id, allowIfselfAsChildSortOrder) }
|
||||
new[] { new ContentTypeSort(newCt.Id, allowIfselfAsChildSortOrder) }
|
||||
);
|
||||
saveContentType(newCt);
|
||||
}
|
||||
|
||||
return newCt;
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateExternalValidators(ModelStateDictionary modelState, object model)
|
||||
{
|
||||
var modelType = model.GetType();
|
||||
Type modelType = model.GetType();
|
||||
|
||||
var validationResults = _editorValidatorCollection
|
||||
.Where(x => x.ModelType == modelType)
|
||||
.SelectMany(x => x.Validate(model))
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) && x.MemberNames.Any());
|
||||
IEnumerable<ValidationResult> validationResults = _editorValidatorCollection
|
||||
.Where(x => x.ModelType == modelType)
|
||||
.SelectMany(x => x.Validate(model))
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) && x.MemberNames.Any());
|
||||
|
||||
foreach (var r in validationResults)
|
||||
foreach (var m in r.MemberNames)
|
||||
modelState.AddModelError(m, r.ErrorMessage);
|
||||
foreach (ValidationResult r in validationResults)
|
||||
foreach (var m in r.MemberNames)
|
||||
{
|
||||
modelState.AddModelError(m, r.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move
|
||||
/// Move
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <param name="getContentType"></param>
|
||||
@@ -398,13 +457,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
Func<int, TContentType> getContentType,
|
||||
Func<TContentType, int, Attempt<OperationResult<MoveOperationStatusType>>> doMove)
|
||||
{
|
||||
var toMove = getContentType(move.Id);
|
||||
TContentType toMove = getContentType(move.Id);
|
||||
if (toMove == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var result = doMove(toMove, move.ParentId);
|
||||
Attempt<OperationResult<MoveOperationStatusType>> result = doMove(toMove, move.ParentId);
|
||||
if (result.Success)
|
||||
{
|
||||
return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8);
|
||||
@@ -424,7 +483,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move
|
||||
/// Move
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <param name="getContentType"></param>
|
||||
@@ -435,13 +494,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
Func<int, TContentType> getContentType,
|
||||
Func<TContentType, int, Attempt<OperationResult<MoveOperationStatusType, TContentType>>> doCopy)
|
||||
{
|
||||
var toMove = getContentType(move.Id);
|
||||
TContentType toMove = getContentType(move.Id);
|
||||
if (toMove == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var result = doCopy(toMove, move.ParentId);
|
||||
Attempt<OperationResult<MoveOperationStatusType, TContentType>> result = doCopy(toMove, move.ParentId);
|
||||
if (result.Success)
|
||||
{
|
||||
return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8);
|
||||
@@ -461,33 +520,39 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
|
||||
/// Validates the composition and adds errors to the model state if any are found then throws an error response if
|
||||
/// there are errors
|
||||
/// </summary>
|
||||
/// <param name="contentTypeSave"></param>
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
private TContentTypeDisplay CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(TContentTypeSave contentTypeSave, TContentType composition)
|
||||
private TContentTypeDisplay CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType,
|
||||
TContentTypeDisplay>(TContentTypeSave contentTypeSave, TContentType composition)
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
{
|
||||
var service = GetContentTypeService<TContentType>();
|
||||
var validateAttempt = service.ValidateComposition(composition);
|
||||
IContentTypeBaseService<TContentType> service = GetContentTypeService<TContentType>();
|
||||
Attempt<string[]> validateAttempt = service.ValidateComposition(composition);
|
||||
if (validateAttempt == false)
|
||||
{
|
||||
// if it's not successful then we need to return some model state for the property type and property group
|
||||
// aliases that are duplicated
|
||||
var duplicatePropertyTypeAliases = validateAttempt.Result.Distinct();
|
||||
var invalidPropertyGroupAliases = (validateAttempt.Exception as InvalidCompositionException)?.PropertyGroupAliases ?? Array.Empty<string>();
|
||||
IEnumerable<string> duplicatePropertyTypeAliases = validateAttempt.Result.Distinct();
|
||||
var invalidPropertyGroupAliases =
|
||||
(validateAttempt.Exception as InvalidCompositionException)?.PropertyGroupAliases ??
|
||||
Array.Empty<string>();
|
||||
|
||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, duplicatePropertyTypeAliases, invalidPropertyGroupAliases);
|
||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave,
|
||||
duplicatePropertyTypeAliases, invalidPropertyGroupAliases);
|
||||
|
||||
var display = UmbracoMapper.Map<TContentTypeDisplay>(composition);
|
||||
TContentTypeDisplay display = UmbracoMapper.Map<TContentTypeDisplay>(composition);
|
||||
//map the 'save' data on top
|
||||
display = UmbracoMapper.Map(contentTypeSave, display);
|
||||
display.Errors = ModelState.ToErrorDictionary();
|
||||
return display;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -495,30 +560,42 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
where T : IContentTypeComposition
|
||||
{
|
||||
if (typeof(T).Implements<IContentType>())
|
||||
{
|
||||
return ContentTypeService as IContentTypeBaseService<T>;
|
||||
}
|
||||
|
||||
if (typeof(T).Implements<IMediaType>())
|
||||
{
|
||||
return MediaTypeService as IContentTypeBaseService<T>;
|
||||
}
|
||||
|
||||
if (typeof(T).Implements<IMemberType>())
|
||||
{
|
||||
return MemberTypeService as IContentTypeBaseService<T>;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Type " + typeof(T).FullName + " does not have a service.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds errors to the model state if any invalid aliases are found then throws an error response if there are errors
|
||||
/// Adds errors to the model state if any invalid aliases are found then throws an error response if there are errors
|
||||
/// </summary>
|
||||
/// <param name="contentTypeSave"></param>
|
||||
/// <param name="duplicatePropertyTypeAliases"></param>
|
||||
/// <param name="invalidPropertyGroupAliases"></param>
|
||||
/// <returns></returns>
|
||||
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> duplicatePropertyTypeAliases, IEnumerable<string> invalidPropertyGroupAliases)
|
||||
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave,
|
||||
IEnumerable<string> duplicatePropertyTypeAliases, IEnumerable<string> invalidPropertyGroupAliases)
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
{
|
||||
foreach (var propertyTypeAlias in duplicatePropertyTypeAliases)
|
||||
{
|
||||
// Find the property type relating to these
|
||||
var property = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyTypeAlias);
|
||||
var group = contentTypeSave.Groups.Single(x => x.Properties.Contains(property));
|
||||
TPropertyType property = contentTypeSave.Groups.SelectMany(x => x.Properties)
|
||||
.Single(x => x.Alias == propertyTypeAlias);
|
||||
PropertyGroupBasic<TPropertyType> group =
|
||||
contentTypeSave.Groups.Single(x => x.Properties.Contains(property));
|
||||
var propertyIndex = group.Properties.IndexOf(property);
|
||||
var groupIndex = contentTypeSave.Groups.IndexOf(group);
|
||||
|
||||
@@ -529,7 +606,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
foreach (var propertyGroupAlias in invalidPropertyGroupAliases)
|
||||
{
|
||||
// Find the property group relating to these
|
||||
var group = contentTypeSave.Groups.Single(x => x.Alias == propertyGroupAlias);
|
||||
PropertyGroupBasic<TPropertyType> group =
|
||||
contentTypeSave.Groups.Single(x => x.Alias == propertyGroupAlias);
|
||||
var groupIndex = contentTypeSave.Groups.IndexOf(group);
|
||||
var key = $"Groups[{groupIndex}].Name";
|
||||
ModelState.AddModelError(key, "Different group types aren't allowed between compositions");
|
||||
@@ -537,7 +615,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the exception is an InvalidCompositionException create a response exception to be thrown for validation errors
|
||||
/// If the exception is an InvalidCompositionException create a response exception to be thrown for validation errors
|
||||
/// </summary>
|
||||
/// <typeparam name="TContentTypeDisplay"></typeparam>
|
||||
/// <typeparam name="TContentTypeSave"></typeparam>
|
||||
@@ -547,7 +625,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
/// <param name="ct"></param>
|
||||
/// <param name="ctId"></param>
|
||||
/// <returns></returns>
|
||||
private TContentTypeDisplay CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave, TPropertyType>(
|
||||
private TContentTypeDisplay CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentTypeSave,
|
||||
TPropertyType>(
|
||||
Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId)
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
@@ -562,23 +641,27 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
invalidCompositionException = (InvalidCompositionException)ex.InnerException;
|
||||
}
|
||||
|
||||
if (invalidCompositionException != null)
|
||||
{
|
||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidCompositionException.PropertyTypeAliases, invalidCompositionException.PropertyGroupAliases);
|
||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave,
|
||||
invalidCompositionException.PropertyTypeAliases, invalidCompositionException.PropertyGroupAliases);
|
||||
return CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(ctId, contentTypeSave, ct);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to throw the ModelState validation results when the ModelState is invalid
|
||||
/// Used to throw the ModelState validation results when the ModelState is invalid
|
||||
/// </summary>
|
||||
/// <typeparam name="TContentTypeDisplay"></typeparam>
|
||||
/// <typeparam name="TContentTypeSave"></typeparam>
|
||||
/// <param name="ctId"></param>
|
||||
/// <param name="contentTypeSave"></param>
|
||||
/// <param name="ct"></param>
|
||||
private TContentTypeDisplay CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(int ctId, TContentTypeSave contentTypeSave, TContentType ct)
|
||||
private TContentTypeDisplay CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(int ctId,
|
||||
TContentTypeSave contentTypeSave, TContentType ct)
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
where TContentTypeSave : ContentTypeSave
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,21 +12,20 @@ using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// An API controller used for dealing with member groups
|
||||
/// An API controller used for dealing with member groups
|
||||
/// </summary>
|
||||
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)]
|
||||
[ParameterSwapControllerActionSelector(nameof(GetById), "id", typeof(int), typeof(Guid), typeof(Udi))]
|
||||
public class MemberGroupController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IMemberGroupService _memberGroupService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
|
||||
public MemberGroupController(
|
||||
IMemberGroupService memberGroupService,
|
||||
@@ -35,11 +34,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
|
||||
_umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
|
||||
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
||||
_localizedTextService =
|
||||
localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the member group json for the member group id
|
||||
/// Gets the member group json for the member group id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the member group json for the member group guid
|
||||
/// Gets the member group json for the member group guid
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
@@ -72,7 +72,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the member group json for the member group udi
|
||||
/// Gets the member group json for the member group udi
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
@@ -100,7 +100,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult DeleteById(int id)
|
||||
{
|
||||
var memberGroup = _memberGroupService.GetById(id);
|
||||
IMemberGroup memberGroup = _memberGroupService.GetById(id);
|
||||
if (memberGroup == null)
|
||||
{
|
||||
return NotFound();
|
||||
@@ -112,7 +112,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
|
||||
public IEnumerable<MemberGroupDisplay> GetAllGroups()
|
||||
=> _memberGroupService.GetAll()
|
||||
.Select(_umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>);
|
||||
.Select(_umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>);
|
||||
|
||||
public MemberGroupDisplay GetEmpty()
|
||||
{
|
||||
@@ -123,18 +123,21 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
public bool IsMemberGroupNameUnique(int id, string oldName, string newName)
|
||||
{
|
||||
if (newName == oldName)
|
||||
{
|
||||
return true; // name hasn't changed
|
||||
}
|
||||
|
||||
var memberGroup = _memberGroupService.GetByName(newName);
|
||||
IMemberGroup memberGroup = _memberGroupService.GetByName(newName);
|
||||
if (memberGroup == null)
|
||||
{
|
||||
return true; // no member group found
|
||||
}
|
||||
|
||||
return memberGroup.Id == id;
|
||||
}
|
||||
|
||||
public ActionResult<MemberGroupDisplay> PostSave(MemberGroupSave saveModel)
|
||||
{
|
||||
|
||||
var id = int.Parse(saveModel.Id.ToString(), CultureInfo.InvariantCulture);
|
||||
IMemberGroup memberGroup = id > 0 ? _memberGroupService.GetById(id) : new MemberGroup();
|
||||
if (memberGroup == null)
|
||||
@@ -157,7 +160,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
var display = _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(memberGroup);
|
||||
MemberGroupDisplay display = _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(memberGroup);
|
||||
display.AddErrorNotification(
|
||||
_localizedTextService.Localize("speechBubbles", "memberGroupNameDuplicate"),
|
||||
string.Empty);
|
||||
|
||||
@@ -35,9 +35,6 @@
|
||||
// List of elements that can be focusable within the focus lock
|
||||
var focusableElementsSelector = '[role="button"], a[href]:not([disabled]):not(.ng-hide), button:not([disabled]):not(.ng-hide), textarea:not([disabled]):not(.ng-hide), input:not([disabled]):not(.ng-hide), select:not([disabled]):not(.ng-hide)';
|
||||
|
||||
// Grab the body element so we can add the tabbing class on it when needed
|
||||
var bodyElement = document.querySelector('body');
|
||||
|
||||
function getDomNodes(){
|
||||
infiniteEditorsWrapper = document.querySelector('.umb-editors');
|
||||
if(infiniteEditorsWrapper) {
|
||||
@@ -47,7 +44,10 @@
|
||||
|
||||
function getFocusableElements(targetElm) {
|
||||
var elm = targetElm ? targetElm : target;
|
||||
focusableElements = elm.querySelectorAll(focusableElementsSelector);
|
||||
|
||||
// Filter out elements that are children of parents with the .ng-hide class
|
||||
focusableElements = [...elm.querySelectorAll(focusableElementsSelector)].filter(elm => !elm.closest('.ng-hide'));
|
||||
|
||||
// Set first and last focusable elements
|
||||
firstFocusableElement = focusableElements[0];
|
||||
lastFocusableElement = focusableElements[focusableElements.length - 1];
|
||||
|
||||
@@ -118,7 +118,7 @@ Use this directive to render a ui component for selecting child items to a paren
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function ChildSelectorDirective() {
|
||||
function ChildSelectorDirective(overlayService, localizationService) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
@@ -126,10 +126,30 @@ Use this directive to render a ui component for selecting child items to a paren
|
||||
scope.dialogModel = {};
|
||||
scope.showDialog = false;
|
||||
|
||||
scope.removeChild = (selectedChild, $index) => {
|
||||
if (scope.onRemove) {
|
||||
scope.onRemove(selectedChild, $index);
|
||||
}
|
||||
scope.removeChild = (selectedChild, $index, event) => {
|
||||
const dialog = {
|
||||
view: "views/components/overlays/umb-template-remove-confirm.html",
|
||||
layout: selectedChild,
|
||||
submitButtonLabelKey: "defaultdialogs_yesRemove",
|
||||
submitButtonStyle: "danger",
|
||||
submit: function () {
|
||||
if(scope.onRemove) {
|
||||
scope.onRemove(selectedChild, $index);
|
||||
overlayService.close();
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
localizationService.localize("general_delete").then(value => {
|
||||
dialog.title = value;
|
||||
overlayService.open(dialog);
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
scope.addChild = $event => {
|
||||
|
||||
@@ -214,13 +214,14 @@ When building a custom infinite editor view you can use the same components as a
|
||||
* Method to tell editors that they are begin blurred.
|
||||
*/
|
||||
function blur() {
|
||||
|
||||
/* keyboard shortcuts will be overwritten by the new infinite editor
|
||||
if (isEnabled === true) {
|
||||
/* keyboard shortcuts will be overwritten by the new infinite editor
|
||||
so we need to store the shortcuts for the current editor so they can be rebound
|
||||
when the infinite editor closes
|
||||
*/
|
||||
unbindKeyboardShortcuts();
|
||||
isEnabled = false;
|
||||
unbindKeyboardShortcuts();
|
||||
isEnabled = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @ngdoc method
|
||||
|
||||
@@ -621,7 +621,6 @@
|
||||
}
|
||||
|
||||
.preview-rows {
|
||||
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
@@ -675,9 +674,9 @@
|
||||
}
|
||||
|
||||
.preview-rows.columns {
|
||||
min-height: 18px;
|
||||
min-height: 16px;
|
||||
line-height: 11px;
|
||||
padding: 1px;
|
||||
padding: 0 1px 1px 1px;
|
||||
|
||||
&.prevalues-rows {
|
||||
min-height: 30px;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<div>
|
||||
|
||||
<div ng-if="model.layout" class="umb-alert umb-alert--warning mb2">
|
||||
<localize key="contentTypeEditor_removeChildNode">You are removing the child node</localize> <strong>{{model.layout.name}}</strong>.
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<localize key="contentTypeEditor_removeChildNodeWarning">
|
||||
Removing a child node will limit the editors options to create different content types beneath a node.
|
||||
</localize>
|
||||
</p>
|
||||
|
||||
<localize key="defaultdialogs_confirmdelete">Are you sure you want to remove</localize>?
|
||||
|
||||
</div>
|
||||
@@ -24,8 +24,8 @@
|
||||
<span class="umb-child-selector__child-name">{{selectedChild.name}}</span>
|
||||
</div>
|
||||
<div class="umb-child-selector__child-actions">
|
||||
<button type="button" class="umb-child-selector__child-remove" ng-click="removeChild(selectedChild, $index)">
|
||||
<umb-icon icon="icon-trash"></umb-icon>
|
||||
<button type="button" class="umb-node-preview__action umb-node-preview__action--red umb-child-selector__child-remove" ng-click="removeChild(selectedChild, $index, $event)">
|
||||
<localize key="general_remove">Remove</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,66 +1,70 @@
|
||||
<div ng-controller="Umbraco.Editors.DataType.CreateController">
|
||||
|
||||
<div ng-show="!model.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer" ng-cloak>
|
||||
<div class="umb-pane">
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
<div ng-show="!model.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer" ng-cloak>
|
||||
<div class="umb-pane">
|
||||
<h5>
|
||||
<localize key="create_createUnder">Create an item under</localize>
|
||||
{{currentNode.name}}
|
||||
</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li data-element="action-data-type" class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="createDataType()" umb-auto-focus>
|
||||
<umb-icon icon="icon-autofill" class="icon large"></umb-icon>
|
||||
<span class="menu-label">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" data-element="action-data-type">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="createDataType()" type="button"
|
||||
umb-auto-focus>
|
||||
<umb-icon class="icon large" icon="icon-autofill"></umb-icon>
|
||||
<span class="menu-label">
|
||||
<localize key="create_newDataType">New data type</localize>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li data-element="action-folder" class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()">
|
||||
<umb-icon icon="icon-folder" class="icon large"></umb-icon>
|
||||
<span class="menu-label">
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action" data-element="action-folder">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-folder"></umb-icon>
|
||||
<span class="menu-label">
|
||||
<localize key="create_newFolder">New folder</localize>...
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button type="button" class="btn btn-info" ng-click="close()">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-show="model.creatingFolder">
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="createContainer()"
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer" ng-cloak>
|
||||
<div class="umb-pane" ng-show="model.creatingFolder">
|
||||
<umb-control-group label="Enter a folder name" hide-label="false">
|
||||
<input type="text"
|
||||
name="folderName"
|
||||
ng-model="model.folderName"
|
||||
class="umb-textstring textstring input-block-level"
|
||||
focus-when="{{model.creatingFolder}}"
|
||||
required />
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="close()"
|
||||
label-key="general_close">
|
||||
</umb-button>
|
||||
<umb-button type="submit"
|
||||
button-style="primary"
|
||||
label-key="general_create">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button class="btn btn-info" ng-click="close()" type="button">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="model.creatingFolder">
|
||||
<form name="createFolderForm" ng-submit="createContainer()"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer" ng-cloak>
|
||||
<div class="umb-pane" ng-show="model.creatingFolder">
|
||||
<umb-control-group hide-label="false" label="Enter a folder name">
|
||||
<input class="umb-textstring textstring input-block-level"
|
||||
focus-when="{{model.creatingFolder}}"
|
||||
name="folderName"
|
||||
ng-model="model.folderName"
|
||||
required
|
||||
type="text"/>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="close()"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
<umb-button button-style="primary"
|
||||
label-key="general_create"
|
||||
type="submit">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,102 +8,102 @@
|
||||
*/
|
||||
function DocumentTypesCreateController($scope, $location, navigationService, contentTypeResource, formHelper, appState) {
|
||||
|
||||
$scope.model = {
|
||||
allowCreateFolder: $scope.currentNode.parentId === null || $scope.currentNode.nodeType === 'container',
|
||||
folderName: '',
|
||||
creatingFolder: false
|
||||
};
|
||||
$scope.model = {
|
||||
allowCreateFolder: $scope.currentNode.parentId === null || $scope.currentNode.nodeType === 'container',
|
||||
folderName: '',
|
||||
creatingFolder: false
|
||||
};
|
||||
|
||||
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
$scope.model.disableTemplates = disableTemplates;
|
||||
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
$scope.model.disableTemplates = disableTemplates;
|
||||
|
||||
var node = $scope.currentNode;
|
||||
var node = $scope.currentNode;
|
||||
|
||||
$scope.showCreateFolder = function () {
|
||||
$scope.model.creatingFolder = true;
|
||||
};
|
||||
$scope.showCreateFolder = function () {
|
||||
$scope.model.creatingFolder = true;
|
||||
};
|
||||
|
||||
$scope.createContainer = function () {
|
||||
$scope.createContainer = function () {
|
||||
|
||||
if (formHelper.submitForm({ scope: $scope, formCtrl: $scope.createFolderForm })) {
|
||||
if (formHelper.submitForm({scope: $scope, formCtrl: $scope.createFolderForm})) {
|
||||
|
||||
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
|
||||
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
|
||||
|
||||
navigationService.hideMenu();
|
||||
|
||||
var currPath = node.path ? node.path : '-1';
|
||||
|
||||
navigationService.syncTree({
|
||||
tree: 'documentTypes',
|
||||
path: currPath + ',' + folderId,
|
||||
forceReload: true,
|
||||
activate: true
|
||||
});
|
||||
|
||||
formHelper.resetForm({ scope: $scope, formCtrl: $scope.createFolderForm });
|
||||
|
||||
var section = appState.getSectionState('currentSection');
|
||||
|
||||
}, function (err) {
|
||||
|
||||
formHelper.resetForm({ scope: $scope, formCtrl: $scope.createFolderForm, hasErrors: true });
|
||||
$scope.error = err;
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function createDocType(config) {
|
||||
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.search('iscomposition', null);
|
||||
$location.search('iselement', null);
|
||||
$location.search('icon', null);
|
||||
|
||||
var icon = null;
|
||||
|
||||
if (config.icon != undefined && config.icon != null) {
|
||||
icon = config.icon;
|
||||
if (config.color) {
|
||||
icon += ' ' + config.color;
|
||||
}
|
||||
}
|
||||
|
||||
$location
|
||||
.path('/settings/documentTypes/edit/' + node.id)
|
||||
.search('create', 'true')
|
||||
.search('notemplate', config.notemplate ? 'true' : null)
|
||||
.search('iscomposition', config.iscomposition ? 'true' : null)
|
||||
.search('iselement', config.iselement ? 'true' : null)
|
||||
.search('icon', icon);
|
||||
navigationService.hideMenu();
|
||||
|
||||
var currPath = node.path ? node.path : '-1';
|
||||
|
||||
navigationService.syncTree({
|
||||
tree: 'documentTypes',
|
||||
path: currPath + ',' + folderId,
|
||||
forceReload: true,
|
||||
activate: true
|
||||
});
|
||||
|
||||
formHelper.resetForm({scope: $scope, formCtrl: $scope.createFolderForm});
|
||||
|
||||
var section = appState.getSectionState('currentSection');
|
||||
|
||||
}, function (err) {
|
||||
|
||||
formHelper.resetForm({scope: $scope, formCtrl: $scope.createFolderForm, hasErrors: true});
|
||||
$scope.error = err;
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function createDocType(config) {
|
||||
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.search('iscomposition', null);
|
||||
$location.search('iselement', null);
|
||||
$location.search('icon', null);
|
||||
|
||||
var icon = null;
|
||||
|
||||
if (config.icon != undefined && config.icon != null) {
|
||||
icon = config.icon;
|
||||
if (config.color) {
|
||||
icon += ' ' + config.color;
|
||||
}
|
||||
}
|
||||
|
||||
$location
|
||||
.path('/settings/documentTypes/edit/' + node.id)
|
||||
.search('create', 'true')
|
||||
.search('notemplate', config.notemplate ? 'true' : null)
|
||||
.search('iscomposition', config.iscomposition ? 'true' : null)
|
||||
.search('iselement', config.iselement ? 'true' : null)
|
||||
.search('icon', icon);
|
||||
navigationService.hideMenu();
|
||||
}
|
||||
|
||||
// Disabling logic for creating document type with template if disableTemplates is set to true
|
||||
if (!disableTemplates) {
|
||||
$scope.createDocType = function (icon) {
|
||||
createDocType({ icon });
|
||||
};
|
||||
}
|
||||
|
||||
$scope.createComponent = function (icon) {
|
||||
createDocType({ notemplate: true, icon });
|
||||
// Disabling logic for creating document type with template if disableTemplates is set to true
|
||||
if (!disableTemplates) {
|
||||
$scope.createDocType = function (icon) {
|
||||
createDocType({icon});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.createComposition = function (icon) {
|
||||
createDocType({ notemplate: true, iscomposition: true, iselement: true, icon });
|
||||
};
|
||||
$scope.createComponent = function (icon) {
|
||||
createDocType({notemplate: true, icon});
|
||||
};
|
||||
|
||||
$scope.createElement = function (icon) {
|
||||
createDocType({ notemplate: true, iselement: true, icon });
|
||||
};
|
||||
$scope.createComposition = function (icon) {
|
||||
createDocType({notemplate: true, iscomposition: true, iselement: true, icon});
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
const showMenu = true;
|
||||
navigationService.hideDialog(showMenu);
|
||||
};
|
||||
$scope.createElement = function (icon) {
|
||||
createDocType({notemplate: true, iselement: true, icon});
|
||||
};
|
||||
|
||||
$scope.close = function () {
|
||||
const showMenu = true;
|
||||
navigationService.hideDialog(showMenu);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('umbraco').controller('Umbraco.Editors.DocumentTypes.CreateController', DocumentTypesCreateController);
|
||||
|
||||
@@ -1,58 +1,61 @@
|
||||
<div class="umb-dialog" ng-controller="Umbraco.Editors.DocumentTypes.ImportController as vm">
|
||||
|
||||
<div class="umb-dialog-body with-footer">
|
||||
<div class="umb-pane">
|
||||
<div ng-if="vm.state === 'upload'">
|
||||
<p>
|
||||
<localize key="settings_importDocumentTypeHelp">
|
||||
To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click 'Import' (you'll be asked for confirmation on the next screen)
|
||||
</localize>
|
||||
</p>
|
||||
<div class="umb-dialog-body with-footer">
|
||||
<div class="umb-pane">
|
||||
<div ng-if="vm.state === 'upload'">
|
||||
<p>
|
||||
<localize key="settings_importDocumentTypeHelp">
|
||||
To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click
|
||||
'Import' (you'll be asked for confirmation on the next screen)
|
||||
</localize>
|
||||
</p>
|
||||
|
||||
<form name="importDoctype">
|
||||
<form name="importDoctype">
|
||||
|
||||
<!-- Select files -->
|
||||
<button class="btn btn-action"
|
||||
name="file"
|
||||
ngf-select
|
||||
ng-model="filesHolder"
|
||||
ngf-change="handleFiles($files, $event)"
|
||||
ngf-multiple="true"
|
||||
ngf-pattern="*.udt">
|
||||
<localize key="general_import">Import</localize>
|
||||
</button>
|
||||
</form>
|
||||
<!-- Select files -->
|
||||
<button class="btn btn-action"
|
||||
name="file"
|
||||
ng-model="filesHolder"
|
||||
ngf-change="handleFiles($files, $event)"
|
||||
ngf-multiple="true"
|
||||
ngf-pattern="*.udt"
|
||||
ngf-select>
|
||||
<localize key="general_import">Import</localize>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div ng-if="importDoctype.file.$error.pattern">Please choose a .udt file to import</div>
|
||||
<div ng-if="importDoctype.file.$error.pattern">Please choose a .udt file to import</div>
|
||||
|
||||
</div>
|
||||
<div ng-if="vm.state === 'confirm'">
|
||||
<strong>
|
||||
<localize key="name">Name</localize>:
|
||||
</strong>
|
||||
{{vm.model.name}}
|
||||
<br />
|
||||
<strong>
|
||||
<localize key="alias">Alias</localize>:
|
||||
</strong>
|
||||
{{vm.model.alias}}
|
||||
<br />
|
||||
<br />
|
||||
<button class="btn btn-primary" ng-click="import()">
|
||||
<localize key="actions_importDocumentType">Import</localize>
|
||||
</button>
|
||||
</div>
|
||||
<div ng-if="vm.state === 'done'">
|
||||
{{vm.model.name}} has been imported!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="vm.state === 'confirm'">
|
||||
<strong>
|
||||
<localize key="name">Name</localize>
|
||||
:
|
||||
</strong>
|
||||
{{vm.model.name}}
|
||||
<br/>
|
||||
<strong>
|
||||
<localize key="alias">Alias</localize>
|
||||
:
|
||||
</strong>
|
||||
{{vm.model.alias}}
|
||||
<br/>
|
||||
<br/>
|
||||
<button class="btn btn-primary" ng-click="import()">
|
||||
<localize key="actions_importDocumentType">Import</localize>
|
||||
</button>
|
||||
</div>
|
||||
<div ng-if="vm.state === 'done'">
|
||||
{{vm.model.name}} has been imported!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="close()"
|
||||
label-key="{{vm.cancelButtonLabel}}">
|
||||
</umb-button>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="close()"
|
||||
button-style="link"
|
||||
label-key="{{vm.cancelButtonLabel}}"
|
||||
type="button">
|
||||
</umb-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,72 +1,76 @@
|
||||
<div ng-controller="Umbraco.Editors.MediaTypes.CreateController">
|
||||
|
||||
<div ng-show="!model.creatingFolder" ng-cloak>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div ng-cloak ng-show="!model.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
|
||||
<div class="umb-pane">
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
<div class="umb-pane">
|
||||
<h5>
|
||||
<localize key="create_createUnder">Create an item under</localize>
|
||||
{{currentNode.name}}
|
||||
</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="createMediaType()" umb-auto-focus>
|
||||
<umb-icon icon="icon-item-arrangement" class="icon large"></umb-icon>
|
||||
<span class="menu-label">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="createMediaType()" type="button"
|
||||
umb-auto-focus>
|
||||
<umb-icon class="icon large" icon="icon-item-arrangement"></umb-icon>
|
||||
<span class="menu-label">
|
||||
<localize key="general_new">New</localize>
|
||||
<localize key="content_mediatype">Media type</localize>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()">
|
||||
<umb-icon icon="icon-folder" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button type="button" class="btn btn-info" ng-click="close()">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-folder"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button class="btn btn-info" ng-click="close()" type="button">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-cloak ng-show="model.creatingFolder">
|
||||
<form name="createFolderForm" ng-submit="createContainer()"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div class="umb-pane" ng-show="model.creatingFolder">
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group hide-label="false" label="Enter a folder name">
|
||||
<input class="umb-textstring textstring input-block-level"
|
||||
focus-when="{{model.creatingFolder}}"
|
||||
name="folderName"
|
||||
ng-model="model.folderName"
|
||||
required
|
||||
type="text"/>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="model.creatingFolder" ng-cloak>
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="createContainer()"
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div class="umb-pane" ng-show="model.creatingFolder">
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="Enter a folder name" hide-label="false">
|
||||
<input type="text"
|
||||
name="folderName"
|
||||
ng-model="model.folderName"
|
||||
class="umb-textstring textstring input-block-level"
|
||||
focus-when="{{model.creatingFolder}}"
|
||||
required />
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="close()"
|
||||
label-key="general_close">
|
||||
</umb-button>
|
||||
<umb-button type="submit"
|
||||
button-style="primary"
|
||||
label-key="general_create">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="close()"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
<umb-button button-style="primary"
|
||||
label-key="general_create"
|
||||
type="submit">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,97 +1,107 @@
|
||||
<div ng-controller="Umbraco.Editors.PartialViewMacros.CreateController as vm" ng-cloak>
|
||||
<div ng-cloak ng-controller="Umbraco.Editors.PartialViewMacros.CreateController as vm">
|
||||
|
||||
<div ng-if="!vm.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div ng-if="!vm.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
|
||||
<div class="umb-pane">
|
||||
<!-- Main options -->
|
||||
<div ng-if="!vm.showSnippets">
|
||||
<div class="umb-pane">
|
||||
<!-- Main options -->
|
||||
<div ng-if="!vm.showSnippets">
|
||||
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
<h5>
|
||||
<localize key="create_createUnder">Create an item under</localize>
|
||||
{{currentNode.name}}
|
||||
</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFile()" umb-auto-focus>
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacro">New partial view macro</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileWithoutMacro()">
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroNoMacro">New partial view macro (without macro)</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()">
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroFromSnippet">>New partial view macro from snippet</localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<umb-icon icon="icon-folder" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Snippets list -->
|
||||
<div ng-if="vm.showSnippets">
|
||||
|
||||
<h5><localize key="defaultdialogs_selectSnippet">Select snippet</localize></h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileFromSnippet(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.createFile()" type="button"
|
||||
umb-auto-focus>
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label"><localize
|
||||
key="create_newPartialViewMacro">New partial view macro</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileWithoutMacro()"
|
||||
type="button">
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroNoMacro">New partial view macro (without macro)</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroFromSnippet">>New partial view macro from snippet</localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-folder"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Dialog footer -->
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button class="btn btn-info" ng-click="vm.close()">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
<!-- Snippets list -->
|
||||
<div ng-if="vm.showSnippets">
|
||||
|
||||
<h5>
|
||||
<localize key="defaultdialogs_selectSnippet">Select snippet</localize>
|
||||
</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileFromSnippet(snippet)"
|
||||
style="padding-top: 6px; padding-bottom: 6px;" type="button">
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.creatingFolder">
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="vm.createFolder(createFolderForm)"
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<!-- Create folder -->
|
||||
<div class="umb-pane">
|
||||
<div ng-show="vm.createFolderError">
|
||||
<div class="alert alert-error">
|
||||
<div>{{vm.createFolderError.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="Enter a folder name" hide-label="false">
|
||||
<input type="text" name="folderName" ng-model="vm.folderName" class="umb-textstring textstring input-block-level" umb-auto-focus required no-dirty-check />
|
||||
</umb-control-group>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="vm.close()"
|
||||
label-key="general_close">
|
||||
</umb-button>
|
||||
<umb-button type="submit"
|
||||
button-style="primary"
|
||||
label-key="general_create">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Dialog footer -->
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button class="btn btn-info" ng-click="vm.close()">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.creatingFolder">
|
||||
<form name="createFolderForm" ng-submit="vm.createFolder(createFolderForm)"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<!-- Create folder -->
|
||||
<div class="umb-pane">
|
||||
<div ng-show="vm.createFolderError">
|
||||
<div class="alert alert-error">
|
||||
<div>{{vm.createFolderError.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group hide-label="false" label="Enter a folder name">
|
||||
<input class="umb-textstring textstring input-block-level" name="folderName" ng-model="vm.folderName"
|
||||
no-dirty-check required type="text" umb-auto-focus/>
|
||||
</umb-control-group>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="vm.close()"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
<umb-button button-style="primary"
|
||||
label-key="general_create"
|
||||
type="submit">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,87 +1,95 @@
|
||||
<div ng-controller="Umbraco.Editors.PartialViews.CreateController as vm" ng-cloak>
|
||||
<div ng-cloak ng-controller="Umbraco.Editors.PartialViews.CreateController as vm">
|
||||
|
||||
<div ng-if="!vm.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div ng-if="!vm.creatingFolder">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
|
||||
<div class="umb-pane">
|
||||
<div class="umb-pane">
|
||||
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
<h5>
|
||||
<localize key="create_createUnder">Create an item under</localize>
|
||||
{{currentNode.name}}
|
||||
</h5>
|
||||
|
||||
<!-- Main options -->
|
||||
<div ng-if="!vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView()" umb-auto-focus>
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newEmptyPartialView">New empty partial view</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()">
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="create_newPartialViewFromSnippet">New partial view from snippet</localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<umb-icon icon="icon-folder" class="icon large"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Snippets list -->
|
||||
<div ng-if="vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<button type="button" class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<umb-icon icon="icon-article" class="icon large"></umb-icon>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Main options -->
|
||||
<div ng-if="!vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView()" type="button"
|
||||
umb-auto-focus>
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label"><localize
|
||||
key="create_newEmptyPartialView">New empty partial view</localize></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label"><localize
|
||||
key="create_newPartialViewFromSnippet">New partial view from snippet</localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()" type="button">
|
||||
<umb-icon class="icon large" icon="icon-folder"></umb-icon>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Dialog footer -->
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button type="button" class="btn btn-info" ng-click="vm.close()">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
<!-- Snippets list -->
|
||||
<div ng-if="vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<button class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView(snippet)"
|
||||
style="padding-top: 6px; padding-bottom: 6px;" type="button">
|
||||
<umb-icon class="icon large" icon="icon-article"></umb-icon>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.creatingFolder">
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="vm.createFolder(createFolderForm)"
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<!-- Create folder -->
|
||||
<div class="umb-pane">
|
||||
<div ng-show="vm.createFolderError">
|
||||
<div class="alert alert-error">
|
||||
<div>{{vm.createFolderError.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="@create_enterFolderName" localize="label" hide-label="false">
|
||||
<input type="text" name="folderName" ng-model="vm.folderName" class="umb-textstring textstring input-block-level" umb-auto-focus required />
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="vm.close()"
|
||||
label-key="general_close">
|
||||
</umb-button>
|
||||
<umb-button type="submit"
|
||||
button-style="primary"
|
||||
label-key="general_create">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Dialog footer -->
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<button class="btn btn-info" ng-click="vm.close()" type="button">
|
||||
<localize key="buttons_somethingElse">Do something else</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.creatingFolder">
|
||||
<form name="createFolderForm" ng-submit="vm.createFolder(createFolderForm)"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<!-- Create folder -->
|
||||
<div class="umb-pane">
|
||||
<div ng-show="vm.createFolderError">
|
||||
<div class="alert alert-error">
|
||||
<div>{{vm.createFolderError.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group hide-label="false" label="@create_enterFolderName" localize="label">
|
||||
<input class="umb-textstring textstring input-block-level" name="folderName" ng-model="vm.folderName"
|
||||
required type="text" umb-auto-focus/>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="vm.close()"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
<umb-button button-style="primary"
|
||||
label-key="general_create"
|
||||
type="submit">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
function imageFilePickerController($scope, editorService) {
|
||||
var vm = this;
|
||||
vm.model = $scope.model;
|
||||
|
||||
$scope.add = function() {
|
||||
vm.add = add;
|
||||
vm.remove = remove;
|
||||
|
||||
function add() {
|
||||
var mediaPickerOptions = {
|
||||
view: "mediapicker",
|
||||
multiPicker: false,
|
||||
disableFolderSelect: true,
|
||||
onlyImages: true,
|
||||
submit: function (model) {
|
||||
$scope.model.value = model.selection[0].image;
|
||||
vm.model.value = model.selection[0].image;
|
||||
|
||||
editorService.close();
|
||||
},
|
||||
@@ -18,8 +23,8 @@ function imageFilePickerController($scope, editorService) {
|
||||
editorService.mediaPicker(mediaPickerOptions);
|
||||
};
|
||||
|
||||
$scope.remove = function () {
|
||||
$scope.model.value = null;
|
||||
function remove() {
|
||||
vm.model.value = null;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<div ng-controller="Umbraco.PrevalueEditors.ImageFilePickerController" class="umb-property-editor umb-mediapicker">
|
||||
<div ng-controller="Umbraco.PrevalueEditors.ImageFilePickerController as vm" class="umb-property-editor umb-mediapicker umb-mediapicker-single">
|
||||
<div class="flex flex-wrap error">
|
||||
<ul class="umb-sortable-thumbnails">
|
||||
<li ng-if="vm.model.value" class="umb-sortable-thumbnails__wrapper">
|
||||
<img ng-src="{{vm.model.value}}" alt="">
|
||||
|
||||
<ul class="umb-sortable-thumbnails" ng-if="model.value">
|
||||
<li class="umb-sortable-thumbnails__wrapper">
|
||||
<img ng-src="{{model.value}}" alt="">
|
||||
<div class="umb-sortable-thumbnails__actions" data-element="sortable-thumbnail-actions">
|
||||
<button type="button" aria-label="Remove" class="umb-sortable-thumbnails__action -red btn-reset" ng-click="vm.remove()">
|
||||
<umb-icon icon="icon-delete" class="icon"></umb-icon>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="umb-sortable-thumbnails__actions">
|
||||
<button type="button" aria-label="Remove" class="umb-sortable-thumbnails__action -red btn-reset" ng-click="remove()">
|
||||
<umb-icon icon="icon-delete" class="icon"></umb-icon>
|
||||
<li style="border: none;" class="add-wrapper unsortable" ng-hide="vm.model.value">
|
||||
<button type="button" aria-label="Open media picker" class="add-link add-link-square btn-reset umb-outline umb-outline--surrounding" ng-click="vm.add()">
|
||||
<umb-icon icon="icon-add" class="icon large"></umb-icon>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button type="button"
|
||||
class="add-link umb-outline umb-outline--surrounding"
|
||||
ng-class="{'add-link-square': !model.value }"
|
||||
ng-click="add()"
|
||||
ng-hide="model.value">
|
||||
<umb-icon icon="icon-add" class="icon large"></umb-icon>
|
||||
</button>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,7 +79,6 @@ ng-form.ng-invalid-val-server-match-settings > .umb-block-list__block > .umb-blo
|
||||
}
|
||||
.umb-block-list__block--actions {
|
||||
position: absolute;
|
||||
z-index:999999999;// We always want to be on top of custom view, but we need to make sure we still are behind relevant Umbraco CMS UI. ToDo: Needs further testing.
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 0;
|
||||
|
||||
@@ -44,14 +44,15 @@ function contentPickerController($scope, $q, $routeParams, $location, entityReso
|
||||
function validate() {
|
||||
if ($scope.contentPickerForm) {
|
||||
//Validate!
|
||||
if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) {
|
||||
var hasItemsOrMandatory = $scope.renderModel.length !== 0 || ($scope.model.validation && $scope.model.validation.mandatory);
|
||||
if (hasItemsOrMandatory && $scope.minNumberOfItems > $scope.renderModel.length) {
|
||||
$scope.contentPickerForm.minCount.$setValidity("minCount", false);
|
||||
}
|
||||
else {
|
||||
$scope.contentPickerForm.minCount.$setValidity("minCount", true);
|
||||
}
|
||||
|
||||
if ($scope.model.config && $scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) < $scope.renderModel.length) {
|
||||
if ($scope.maxNumberOfItems < $scope.renderModel.length) {
|
||||
$scope.contentPickerForm.maxCount.$setValidity("maxCount", false);
|
||||
}
|
||||
else {
|
||||
@@ -145,6 +146,10 @@ function contentPickerController($scope, $q, $routeParams, $location, entityReso
|
||||
|
||||
$scope.umbProperty.setPropertyActions(propertyActions);
|
||||
}
|
||||
|
||||
// use these to avoid the nested property lookups/null-checks
|
||||
$scope.minNumberOfItems = $scope.model.config.minNumber ? parseInt($scope.model.config.minNumber) : 0;
|
||||
$scope.maxNumberOfItems = $scope.model.config.maxNumber ? parseInt($scope.model.config.maxNumber) : 0;
|
||||
}
|
||||
|
||||
//Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that!
|
||||
@@ -194,7 +199,7 @@ function contentPickerController($scope, $q, $routeParams, $location, entityReso
|
||||
dialogOptions.dataTypeKey = $scope.model.dataTypeKey;
|
||||
|
||||
// if we can't pick more than one item, explicitly disable multiPicker in the dialog options
|
||||
if ($scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) === 1) {
|
||||
if ($scope.maxNumberOfItems === 1) {
|
||||
dialogOptions.multiPicker = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</umb-node-preview>
|
||||
</div>
|
||||
|
||||
<button ng-show="model.config.multiPicker === true && renderModel.length < model.config.maxNumber || renderModel.length === 0 || !model.config.maxNumber"
|
||||
<button ng-show="model.config.multiPicker === true && renderModel.length < maxNumberOfItems || renderModel.length === 0 || !maxNumberOfItems"
|
||||
type="button"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="openCurrentPicker()"
|
||||
@@ -29,35 +29,35 @@
|
||||
<span class="sr-only">...</span>
|
||||
</button>
|
||||
|
||||
<div class="umb-contentpicker__min-max-help" ng-if="model.config.multiPicker === true && (model.config.maxNumber > 1 || model.config.minNumber > 0)">
|
||||
<div class="umb-contentpicker__min-max-help" ng-if="model.config.multiPicker === true && (maxNumberOfItems > 1 || minNumberOfItems > 0) && (renderModel.length !== 0 || (model.validation && model.validation.mandatory))">
|
||||
|
||||
<!-- Both min and max items -->
|
||||
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber !== model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
<span ng-if="minNumberOfItems !== maxNumberOfItems">
|
||||
<span ng-if="renderModel.length < maxNumberOfItems">Add between {{minNumberOfItems}} and {{maxNumberOfItems}} items</span>
|
||||
<span ng-if="renderModel.length > maxNumberOfItems">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{maxNumberOfItems}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Equal min and max -->
|
||||
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber === model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add {{model.config.minNumber - renderModel.length}} item(s)</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
<span ng-if="minNumberOfItems === maxNumberOfItems">
|
||||
<span ng-if="renderModel.length < maxNumberOfItems">Add {{minNumberOfItems - renderModel.length}} item(s)</span>
|
||||
<span ng-if="renderModel.length > maxNumberOfItems">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{maxNumberOfItems}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Only max -->
|
||||
<span ng-if="!model.config.minNumber && model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add up to {{model.config.maxNumber}} items</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
<span ng-if="!minNumberOfItems && maxNumberOfItems">
|
||||
<span ng-if="renderModel.length < maxNumberOfItems">Add up to {{maxNumberOfItems}} items</span>
|
||||
<span ng-if="renderModel.length > maxNumberOfItems">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{maxNumberOfItems}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Only min -->
|
||||
<span ng-if="model.config.minNumber && !model.config.maxNumber && renderModel.length < model.config.minNumber">
|
||||
Add at least {{model.config.minNumber}} item(s)
|
||||
<span ng-if="minNumberOfItems && !maxNumberOfItems && renderModel.length < minNumberOfItems">
|
||||
Add at least {{minNumberOfItems}} item(s)
|
||||
</span>
|
||||
|
||||
</div>
|
||||
@@ -70,12 +70,12 @@
|
||||
|
||||
<div ng-messages="contentPickerForm.minCount.$error" show-validation-on-submit>
|
||||
<div class="help-inline" ng-message="minCount">
|
||||
<localize key="validation_minCount">You need to add at least</localize> {{model.config.minNumber}} <localize key="validation_items">items</localize>
|
||||
<localize key="validation_minCount">You need to add at least</localize> {{minNumberOfItems}} <localize key="validation_items">items</localize>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-messages="contentPickerForm.maxCount.$error" show-validation-on-submit>
|
||||
<div class="help-inline" ng-message="maxCount">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
<localize key="validation_maxCount">You can only have</localize> {{maxNumberOfItems}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -125,8 +125,29 @@ angular.module("umbraco")
|
||||
editorService.open(layoutConfigOverlay);
|
||||
}
|
||||
|
||||
function deleteTemplate(index) {
|
||||
$scope.model.value.templates.splice(index, 1);
|
||||
function deleteTemplate(template, index, event) {
|
||||
|
||||
const dialog = {
|
||||
view: "views/propertyEditors/grid/overlays/layoutdeleteconfirm.html",
|
||||
layout: template,
|
||||
submitButtonLabelKey: "contentTypeEditor_yesDelete",
|
||||
submitButtonStyle: "danger",
|
||||
submit: function (model) {
|
||||
$scope.model.value.templates.splice(index, 1);
|
||||
overlayService.close();
|
||||
},
|
||||
close: function () {
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
localizationService.localize("general_delete").then(value => {
|
||||
dialog.title = value;
|
||||
overlayService.open(dialog);
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
/****************
|
||||
@@ -167,7 +188,6 @@ angular.module("umbraco")
|
||||
}
|
||||
|
||||
function deleteLayout(layout, index, event) {
|
||||
|
||||
const dialog = {
|
||||
view: "views/propertyeditors/grid/overlays/rowdeleteconfirm.html",
|
||||
layout: layout,
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<div>
|
||||
<p>{{template.name}}</p>
|
||||
<button type="button" class="btn btn-small btn-link" ng-click="vm.deleteTemplate($index)">
|
||||
<button type="button" class="btn btn-small btn-link" ng-click="vm.deleteTemplate(template, $index, $event)">
|
||||
<umb-icon icon="icon-delete" class="red"></umb-icon>
|
||||
<localize key="general_delete">Delete</localize>
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div>
|
||||
<div ng-if="model.layout" class="umb-alert umb-alert--warning mb2">
|
||||
<localize key="grid_deleteLayout">You are deleting the layout</localize> <strong>{{model.layout.name}}</strong>.
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<localize key="grid_deletingALayout">
|
||||
Modifying layout will result in loss of data for any existing content that is based on this configuration.
|
||||
</localize>
|
||||
</p>
|
||||
|
||||
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize>?
|
||||
|
||||
</div>
|
||||
@@ -18,8 +18,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Alias</th>
|
||||
<th>Header</th>
|
||||
<th><localize key="general_alias">Alias</localize></th>
|
||||
<th><localize key="general_header">Header</localize></th>
|
||||
<th><localize key="template_template">Template</localize></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
@@ -32,14 +32,16 @@
|
||||
<td>
|
||||
<div class="list-view-layout__name flex-column content-start">
|
||||
<span class="list-view-layout__name-text" ng-bind="val.alias"></span>
|
||||
<span class="list-view-layout__system" ng-show="val.isSystem == 1">(system field)</span>
|
||||
<span class="list-view-layout__system" ng-show="val.isSystem == 1">(<localize key="general_systemField">systemField</localize>)</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ng-form name="headerForm" ng-if="!val.isSystem">
|
||||
<input type="text" name="header" ng-model="val.header" required />
|
||||
<span ng-messages="headerForm.header.$error" show-validation-on-submit>
|
||||
<span class="help-inline" ng-message="required">Required</span>
|
||||
<span class="help-inline" ng-message="required">
|
||||
<localize key="general_required">Required</localize>
|
||||
</span>
|
||||
</span>
|
||||
</ng-form>
|
||||
|
||||
@@ -53,8 +55,8 @@
|
||||
</ng-form>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn-icon" ng-click="removeField(val)" aria-label="Remove">
|
||||
<umb-icon icon="icon-trash"></umb-icon>
|
||||
<button type="button" class="umb-node-preview__action umb-node-preview__action--red" ng-click="removeField(val)">
|
||||
<localize key="general_remove">Remove</localize><span class="sr-only"> {{val.alias}}</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -63,5 +65,4 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,69 +1,72 @@
|
||||
<div ng-controller="Umbraco.Editors.RelationTypes.CreateController as vm" ng-cloak>
|
||||
<div ng-cloak ng-controller="Umbraco.Editors.RelationTypes.CreateController as vm">
|
||||
|
||||
<form novalidate name="createRelationTypeForm"
|
||||
val-form-manager
|
||||
ng-submit="vm.createRelationType()">
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div class="umb-pane">
|
||||
<!-- Name -->
|
||||
<umb-control-group label="@relationType_name" alias="relationTypeName">
|
||||
<input type="text" name="relationTypeName" id="relationTypeName" ng-model="vm.relationType.name" class="umb-textstring textstring input-block-level" umb-auto-focus required />
|
||||
</umb-control-group>
|
||||
<form name="createRelationTypeForm" ng-submit="vm.createRelationType()"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
<div class="umb-pane">
|
||||
<!-- Name -->
|
||||
<umb-control-group alias="relationTypeName" label="@relationType_name">
|
||||
<input class="umb-textstring textstring input-block-level" id="relationTypeName" name="relationTypeName" ng-model="vm.relationType.name"
|
||||
required type="text" umb-auto-focus/>
|
||||
</umb-control-group>
|
||||
|
||||
<!-- Direction -->
|
||||
<umb-control-group label="@relationType_direction">
|
||||
<p tabindex="0" class="sr-only"><localize key="relationType_direction"></localize></p>
|
||||
<ul class="unstyled">
|
||||
<li>
|
||||
<label class="radio" for="parentToChild">
|
||||
<umb-radiobutton name="relationType-direction"
|
||||
value="false"
|
||||
model="vm.relationType.isBidirectional" required="true" input-id="parentToChild"
|
||||
label-key="relationType_parentToChild">
|
||||
</umb-radiobutton>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="radio" for="bidirectional">
|
||||
<umb-radiobutton name="relationType-direction"
|
||||
value="true"
|
||||
model="vm.relationType.isBidirectional" required="true" input-id="bidirectional"
|
||||
label-key="relationType_bidirectional">
|
||||
</umb-radiobutton>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</umb-control-group>
|
||||
<!-- Direction -->
|
||||
<umb-control-group label="@relationType_direction">
|
||||
<p class="sr-only" tabindex="0">
|
||||
<localize key="relationType_direction"></localize>
|
||||
</p>
|
||||
<ul class="unstyled">
|
||||
<li>
|
||||
<label class="radio" for="parentToChild">
|
||||
<umb-radiobutton input-id="parentToChild"
|
||||
label-key="relationType_parentToChild"
|
||||
model="vm.relationType.isBidirectional" name="relationType-direction" required="true"
|
||||
value="false">
|
||||
</umb-radiobutton>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="radio" for="bidirectional">
|
||||
<umb-radiobutton input-id="bidirectional"
|
||||
label-key="relationType_bidirectional"
|
||||
model="vm.relationType.isBidirectional" name="relationType-direction" required="true"
|
||||
value="true">
|
||||
</umb-radiobutton>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</umb-control-group>
|
||||
|
||||
<!-- Parent -->
|
||||
<umb-control-group label="@relationType_parent" alias="relationType-parent">
|
||||
<select name="relationType-parent" id="relationType-parent"
|
||||
ng-model="vm.relationType.parentObjectType"
|
||||
class="umb-property-editor umb-dropdown" required>
|
||||
<option ng-repeat="objectType in vm.objectTypes" value="{{objectType.id}}">{{objectType.name}}</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
<!-- Parent -->
|
||||
<umb-control-group alias="relationType-parent" label="@relationType_parent">
|
||||
<select class="umb-property-editor umb-dropdown" id="relationType-parent"
|
||||
name="relationType-parent"
|
||||
ng-model="vm.relationType.parentObjectType" required>
|
||||
<option ng-repeat="objectType in vm.objectTypes" value="{{objectType.id}}">{{objectType.name}}</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
|
||||
<!-- Child -->
|
||||
<umb-control-group label="@relationType_child" alias="relationType-child">
|
||||
<select name="relationType-child" id="relationType-child"
|
||||
ng-model="vm.relationType.childObjectType"
|
||||
class="umb-property-editor umb-dropdown" required>
|
||||
<option ng-repeat="objectType in vm.objectTypes" value="{{objectType.id}}">{{objectType.name}}</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
action="close()"
|
||||
label-key="general_close">
|
||||
</umb-button>
|
||||
<umb-button type="submit"
|
||||
button-style="primary"
|
||||
label-key="general_create">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Child -->
|
||||
<umb-control-group alias="relationType-child" label="@relationType_child">
|
||||
<select class="umb-property-editor umb-dropdown" id="relationType-child"
|
||||
name="relationType-child"
|
||||
ng-model="vm.relationType.childObjectType" required>
|
||||
<option ng-repeat="objectType in vm.objectTypes" value="{{objectType.id}}">{{objectType.name}}</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<umb-button action="close()"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
<umb-button button-style="primary"
|
||||
label-key="general_create"
|
||||
type="submit">
|
||||
</umb-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user