Skip to content

Commit

Permalink
Merge v8/dev 20-10-2021 (#11426)
Browse files Browse the repository at this point in the history
* Adjust icon in umb-checkbox and ensure icon is centered

* Missing nl translation for blockEditor_addBlock

* Implement icon parameter for doctype editor (#11008)

* fix: implement icon parameter for doctype editor

issue #10108

* fix: move color from icon to class attribute

* fix: removed defined colors, defaulting to the standard dark grey (ie "no color picked" in icon picker)

* cleaned up unused dependencies, double quotes to single, removed unused 'color' param from the create methods, and use shorthand object creation in createDocType (if the key has the same name as the variable passed as a prop, we only need to pass the key name)

* fix comment

Co-authored-by: Nathan Woulfe <[email protected]>

* Align sortable handle vertically in multivalues prevalue editor

* 10341: Use different picker for content types (#10896)

* 10341: Use different picker for content types

* use es6 where possible (inc removing underscore for teeny tiny performance improvement)

Co-authored-by: Nathan Woulfe <[email protected]>

* Falling back to contentTypeName when Block List label is empty (#10963)

* Falling back to contentTypeName when Block List label is empty

* Adding $contentTypeName variable for Block List labels

* Fix incorrect attribute

* Grid: Add button styling fix (#10978)

* Add missing focus styling

* Ensure add button is perfectly rounded and remove unused / uneeded CSS.

* Remove redundant border-color property

* Revert removal of unused css

Co-authored-by: BatJan <[email protected]>
Co-authored-by: Jan Skovgaard Olsen <[email protected]>

* Create content template localization (#10945)

* Don't use self-closing element for custom HTML elements

* Use button element for close/cancel in copy dialog

* Update localization of "createBlueprintFrom"

Co-authored-by: Nathan Woulfe <[email protected]>

* Cleanup examine search results, and adds ability to toggle fields (#9141)

* Cleanup examine search results, and adds ability to toggle fields

* update table to use joinarray filter with one-time binding to avoid recalculating filter values, updated filter to not explode when array arg is null

* fix failing tests - improve filter to not fail on non-array params, update tests accordingly

Co-authored-by: Nathan Woulfe <[email protected]>

* Add EntityController GetUrlsByUdis

Enables loading multiple URLs in a single request for Media & Documents

* Update content picker to use GetUrlsByUdis

* Allows replacing MainDom with alternate DB

There are some cases where there is a complex hosting strategy and folks want a readonly database and are hosting on Azure. In that case, it is not entirely possible to have a readonly Umbraco database because SqlMainDom is required and part of that requirement is to have read/write access to the umbraco key value table.
This PR allows for the default MainDom to be replaced and to allow for an SqlMainDomLock to use an alternate connection string so that a separate read/write database can be used.

* Remove inherited property group id/key when local properties are added (#11231)

* Remove inherited property group id/key when local properties are added

* Rebind saved content type values

* Remove inherited from save group

* Rename parameter for clarity

* Removes annoying wait text, which causes layout jank

* v8: Backoffice Welsh language translation updates (#11240)

* Updated the Welsh language file to include newly added keys (based on the en us language file)

* Updated the searchInputDescription key

* Updated the endTitle key

* Use medium sized overlay

* Use umb-icon component for icons in content type groups and tabs

* fixes wrong reference to enterSubmitFolder method in ng-keydown

* 11251: Don't add default dashboard to url

* Fix preview of SVG when height and width not are set

* If caching a published document, make sure you use the published Name… (#11313)

* If caching a published document, make sure you use the published Name. Closes #11074.

* Fix case of new node

Co-authored-by: Moore, Douglas S <[email protected]>

* Added missing Italian translations (#11197)

* Resolve incorrect ContentSavedState for failed publish

Closes #11290 (for v8)

* add modelValue validation for server to correctly update validation errors

* 11048: Bugfix for groups and properties that get replaced (#11257)

(cherry picked from commit 1605dc1)

* Icon fallback to `icon-document` for existing document types (#11283)

* Align create buttons styling (#11352)

* Added button for cancelling dictionary create action

* Use hideMenu

* Align dictionary create with the other creates

* Align import documenttype

* Align for data type folder create

* Align document type create buttons

* Forgot small ng-show

* Align create media folder buttons

* Align create macro buttons

* Align create relation buttons

* Align create partial view macro folder buttons

* Align partial view folder create buttons

* Align create scripts folder buttons

* Align create scripts folder buttons

* Use primary instead of success

* V8: Duplicate MemberGroup names cause MemberGroup mixup (#11291)

* Prevented duplicate member group names

* Added English lang

* Updated 'Exist' typo

* add labels in FR and NL

* Adding property group aliases to ex.message

* Adding invalid prop group aliases as ModelState errors, so we don't introduce breaking changes

* Pointing the actual reason for invalidating composition

* Validate all content type dependencies and throw a single InvalidCompositionException

* Rename based on review comments

* Update composition validation error messages

* Update InvalidCompositionException message

* Allow switching property editor from numeric to slider (#11287)

* Make it possible to change from numeric/decimal property editor to slider without breaking editor

* Formatting

* Enables friendly pasting in multipletextbox

* UI API docs: Added reset rules for .close class

* UI API docs: Fixed incorrect method name

* 11331: Check property on instance if id is not set yet

* Fixed cypress tests

Co-authored-by: Bjarne Fyrstenborg <[email protected]>
Co-authored-by: Corné Strijkert <[email protected]>
Co-authored-by: Søren Gregersen <[email protected]>
Co-authored-by: Nathan Woulfe <[email protected]>
Co-authored-by: patrickdemooij9 <[email protected]>
Co-authored-by: Callum Whyte <[email protected]>
Co-authored-by: Jan Skovgaard <[email protected]>
Co-authored-by: BatJan <[email protected]>
Co-authored-by: Jan Skovgaard Olsen <[email protected]>
Co-authored-by: Søren Kottal <[email protected]>
Co-authored-by: Paul Johnson <[email protected]>
Co-authored-by: Shannon <[email protected]>
Co-authored-by: Sebastiaan Janssen <[email protected]>
Co-authored-by: Ronald Barendse <[email protected]>
Co-authored-by: Owain Jones <[email protected]>
Co-authored-by: Mole <[email protected]>
Co-authored-by: Doug Moore <[email protected]>
Co-authored-by: Moore, Douglas S <[email protected]>
Co-authored-by: Martino Gabrielli <[email protected]>
Co-authored-by: Nikolaj Geisle <[email protected]>
Co-authored-by: Mads Rasmussen <[email protected]>
Co-authored-by: Jamie Townsend <[email protected]>
Co-authored-by: Elitsa Marinovska <[email protected]>
Co-authored-by: Anders Bjerner <[email protected]>
  • Loading branch information
1 parent 9494591 commit 56595a7
Show file tree
Hide file tree
Showing 79 changed files with 4,158 additions and 21,853 deletions.
35 changes: 23 additions & 12 deletions src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Extensions;
using System.Text;

namespace Umbraco.Cms.Core.Exceptions
{
Expand Down Expand Up @@ -86,18 +87,28 @@ public InvalidCompositionException(string contentTypeAlias, string addedComposit

private static string FormatMessage(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliases, string[] propertyGroupAliases)
{
// TODO Add property group aliases to message
return addedCompositionAlias.IsNullOrWhiteSpace()
? string.Format(
"ContentType with alias '{0}' has an invalid composition " +
"and there was a conflict on the following PropertyTypes: '{1}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
contentTypeAlias, string.Join(", ", propertyTypeAliases))
: string.Format(
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
"but there was a conflict on the following PropertyTypes: '{2}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
addedCompositionAlias, contentTypeAlias, string.Join(", ", propertyTypeAliases));
var sb = new StringBuilder();

if (addedCompositionAlias.IsNullOrWhiteSpace())
{
sb.AppendFormat("Content type with alias '{0}' has an invalid composition.", contentTypeAlias);
}
else
{
sb.AppendFormat("Content type with alias '{0}' was added as a composition to content type with alias '{1}', but there was a conflict.", addedCompositionAlias, contentTypeAlias);
}

if (propertyTypeAliases.Length > 0)
{
sb.AppendFormat(" Property types must have a unique alias across all compositions, these aliases are duplicate: {0}.", string.Join(", ", propertyTypeAliases));
}

if (propertyGroupAliases.Length > 0)
{
sb.AppendFormat(" Property groups with the same alias must also have the same type across all compositions, these aliases have different types: {0}.", string.Join(", ", propertyGroupAliases));
}

return sb.ToString();
}

/// <summary>
Expand Down
8 changes: 7 additions & 1 deletion src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ private static string GetUrlSegmentSource(IContentBase content, string culture)
if (content.HasProperty(Constants.Conventions.Content.UrlName))
source = (content.GetValue<string>(Constants.Conventions.Content.UrlName, culture) ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(source))
source = content.GetCultureName(culture);
{
// If the name of a node has been updated, but it has not been published, the url should use the published name, not the current node name
// If this node has never been published (GetPublishName is null), use the unpublished name
source = (content is IContent document) && document.Edited && document.GetPublishName(culture) != null
? document.GetPublishName(culture)
: content.GetCultureName(culture);
}
return source;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,19 @@ protected override void PersistUpdatedItem(IContent entity)
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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private void RecursePropertyValues(IEnumerable<BlockItemData> blockData, Func<Gu
{
json = JToken.Parse(asString);
}
catch (Exception e)
catch (Exception)
{
// See issue https://github.com/umbraco/Umbraco-CMS/issues/10879
// We are detecting JSON data by seeing if a string is surrounded by [] or {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ protected void ValidateLocked(TItem compositionContentType)
stack.Push(c);
}

var duplicatePropertyTypeAliases = new List<string>();
var invalidPropertyGroupAliases = new List<string>();

foreach (var dependency in dependencies)
{
if (dependency.Id == compositionContentType.Id)
Expand All @@ -143,13 +146,14 @@ protected void ValidateLocked(TItem compositionContentType)
if (contentTypeDependency == null)
continue;

var duplicatePropertyTypeAliases = contentTypeDependency.PropertyTypes.Select(x => x.Alias).Intersect(propertyTypeAliases, StringComparer.InvariantCultureIgnoreCase).ToArray();
var invalidPropertyGroupAliases = contentTypeDependency.PropertyGroups.Where(x => propertyGroupAliases.TryGetValue(x.Alias, out var type) && type != x.Type).Select(x => x.Alias).ToArray();
duplicatePropertyTypeAliases.AddRange(contentTypeDependency.PropertyTypes.Select(x => x.Alias).Intersect(propertyTypeAliases, StringComparer.InvariantCultureIgnoreCase));
invalidPropertyGroupAliases.AddRange(contentTypeDependency.PropertyGroups.Where(x => propertyGroupAliases.TryGetValue(x.Alias, out var type) && type != x.Type).Select(x => x.Alias));
}

if (duplicatePropertyTypeAliases.Length == 0 && invalidPropertyGroupAliases.Length == 0)
continue;
if (duplicatePropertyTypeAliases.Count > 0 || invalidPropertyGroupAliases.Count > 0)

throw new InvalidCompositionException(compositionContentType.Alias, null, duplicatePropertyTypeAliases, invalidPropertyGroupAliases);
{
throw new InvalidCompositionException(compositionContentType.Alias, null, duplicatePropertyTypeAliases.Distinct().ToArray(), invalidPropertyGroupAliases.Distinct().ToArray());
}
}

Expand Down
34 changes: 23 additions & 11 deletions src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,12 @@ private TContentTypeDisplay CreateCompositionValidationExceptionIfInvalid<TConte
var validateAttempt = service.ValidateComposition(composition);
if (validateAttempt == false)
{
//if it's not successful then we need to return some model state for the property aliases that
// are duplicated
var invalidPropertyAliases = validateAttempt.Result.Distinct();
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidPropertyAliases);
// 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>();

AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, duplicatePropertyTypeAliases, invalidPropertyGroupAliases);

var display = UmbracoMapper.Map<TContentTypeDisplay>(composition);
//map the 'save' data on top
Expand All @@ -505,22 +507,32 @@ public IContentTypeBaseService<T> GetContentTypeService<T>()
/// 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="invalidPropertyAliases"></param>
/// <param name="duplicatePropertyTypeAliases"></param>
/// <param name="invalidPropertyGroupAliases"></param>
/// <returns></returns>
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> invalidPropertyAliases)
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> duplicatePropertyTypeAliases, IEnumerable<string> invalidPropertyGroupAliases)
where TContentTypeSave : ContentTypeSave<TPropertyType>
where TPropertyType : PropertyTypeBasic
{
foreach (var propertyAlias in invalidPropertyAliases)
foreach (var propertyTypeAlias in duplicatePropertyTypeAliases)
{
// Find the property relating to these
var property = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyAlias);
// 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));
var propertyIndex = group.Properties.IndexOf(property);
var groupIndex = contentTypeSave.Groups.IndexOf(group);

var key = $"Groups[{groupIndex}].Properties[{propertyIndex}].Alias";
ModelState.AddModelError(key, "Duplicate property aliases not allowed between compositions");
ModelState.AddModelError(key, "Duplicate property aliases aren't allowed between compositions");
}

foreach (var propertyGroupAlias in invalidPropertyGroupAliases)
{
// Find the property group relating to these
var 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");
}
}

Expand Down Expand Up @@ -552,7 +564,7 @@ private TContentTypeDisplay CreateInvalidCompositionResponseException<TContentTy
}
if (invalidCompositionException != null)
{
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidCompositionException.PropertyTypeAliases);
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidCompositionException.PropertyTypeAliases, invalidCompositionException.PropertyGroupAliases);
return CreateModelStateValidationEror<TContentTypeSave, TContentTypeDisplay>(ctId, contentTypeSave, ct);
}
return null;
Expand Down
46 changes: 46 additions & 0 deletions src/Umbraco.Web.BackOffice/Controllers/EntityController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ public EntityController(
_appCaches = appCaches ?? throw new ArgumentNullException(nameof(appCaches));
}



/// <summary>
/// Returns an Umbraco alias given a string
/// </summary>
Expand Down Expand Up @@ -288,6 +290,50 @@ public IActionResult GetUrl(Udi id, string culture = "*")
return GetUrl(intId.Result, entityType, culture);
}

/// <summary>
/// Get entity URLs by UDIs
/// </summary>
/// <param name="udis">
/// A list of UDIs to lookup items by
/// </param>
/// <param name="culture">The culture to fetch the URL for</param>
/// <returns>Dictionary mapping Udi -> Url</returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids.
/// </remarks>
[HttpGet]
[HttpPost]
public IDictionary<Udi, string> GetUrlsByUdis([FromJsonPath] Udi[] udis, string culture = null)
{
if (udis == null || udis.Length == 0)
{
return new Dictionary<Udi, string>();
}

// TODO: PMJ 2021-09-27 - Should GetUrl(Udi) exist as an extension method on UrlProvider/IUrlProvider (in v9)
string MediaOrDocumentUrl(Udi udi)
{
if (udi is not GuidUdi guidUdi)
{
return null;
}

return guidUdi.EntityType switch
{
Constants.UdiEntityType.Document => _publishedUrlProvider.GetUrl(guidUdi.Guid, culture: culture ?? ClientCulture()),
// NOTE: If culture is passed here we get an empty string rather than a media item URL WAT
Constants.UdiEntityType.Media => _publishedUrlProvider.GetMediaUrl(guidUdi.Guid, culture: null),
_ => null
};
}

return udis
.Select(udi => new {
Udi = udi,
Url = MediaOrDocumentUrl(udi)
}).ToDictionary(x => x.Udi, x => x.Url);
}

/// <summary>
/// Gets the URL of an entity
/// </summary>
Expand Down
38 changes: 31 additions & 7 deletions src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ public MemberGroupDisplay GetEmpty()
return _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(item);
}

public bool IsMemberGroupNameUnique(int id, string oldName, string newName)
{
if (newName == oldName)
return true; // name hasn't changed

var memberGroup = _memberGroupService.GetByName(newName);
if (memberGroup == null)
return true; // no member group found

return memberGroup.Id == id;
}

public ActionResult<MemberGroupDisplay> PostSave(MemberGroupSave saveModel)
{

Expand All @@ -130,16 +142,28 @@ public ActionResult<MemberGroupDisplay> PostSave(MemberGroupSave saveModel)
return NotFound();
}

memberGroup.Name = saveModel.Name;
_memberGroupService.Save(memberGroup);
if (IsMemberGroupNameUnique(memberGroup.Id, memberGroup.Name, saveModel.Name))
{
memberGroup.Name = saveModel.Name;
_memberGroupService.Save(memberGroup);

MemberGroupDisplay display = _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(memberGroup);
MemberGroupDisplay display = _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(memberGroup);

display.AddSuccessNotification(
_localizedTextService.Localize("speechBubbles","memberGroupSavedHeader"),
string.Empty);
display.AddSuccessNotification(
_localizedTextService.Localize("speechBubbles", "memberGroupSavedHeader"),
string.Empty);

return display;
return display;
}
else
{
var display = _umbracoMapper.Map<IMemberGroup, MemberGroupDisplay>(memberGroup);
display.AddErrorNotification(
_localizedTextService.Localize("speechBubbles", "memberGroupNameDuplicate"),
string.Empty);

return display;
}
}
}
}
Loading

0 comments on commit 56595a7

Please sign in to comment.