Skip to content

Commit bc695ba

Browse files
authored
Merge pull request #6921 from umbraco/cms/release/15.4
Umbraco 15.4 release documentation updates
2 parents e6e88da + f9a42bf commit bc695ba

File tree

5 files changed

+109
-18
lines changed

5 files changed

+109
-18
lines changed

15/umbraco-cms/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@
362362
* [Determining if an entity is new](reference/notifications/determining-new-entity.md)
363363
* [MediaService Notifications Example](reference/notifications/mediaservice-notifications.md)
364364
* [MemberService Notifications Example](reference/notifications/memberservice-notifications.md)
365+
* [UserService Notifications Example](reference/notifications/userservice-notifications.md)
365366
* [Sending Allowed Children Notification](reference/notifications/sendingallowedchildrennotifications.md)
366367
* [Umbraco Application Lifetime Notifications](reference/notifications/umbracoapplicationlifetime-notifications.md)
367368
* [EditorModel Notifications](reference/notifications/editormodel-notifications/README.md)

15/umbraco-cms/reference/content-type-filters.md

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: Describes how to use Content Type Filters to restrict the allowed content options available to editors.
2+
description: Describes how to use Allowed Content Type Filters to restrict the allowed content options available to editors.
33
---
44

55
# Filtering Allowed Content Types
@@ -23,27 +23,45 @@ There are two methods you can implement:
2323
* One for filtering the content types allowed at the content root
2424
* One for the content types allowed below a given parent node.
2525

26-
If you don't want to filter using one of the two approaches, you can return the provided collection unmodified.
26+
If you don't want to filter using one of the two approaches, you can omit the implementation of that method. The default implementation will return the provided collection unmodified.
2727

2828
### Example Use Case
2929

30-
The following example shows a typical use case. Often websites will have a "Home Page" Document Type which is created at the root. Normally, only one of these is required. You can enforce that using the following Content Type Filter.
30+
The following example shows an illustrative but also typical use case. Often websites will have a "Home Page" Document Type which is created at the root. Normally, only one of these is required. You can enforce that using the following Content Type Filter.
3131

32-
The code below is querying the existing content available at the root. Normally you can create a "Home Page" here, but if one already exists that option is removed:
32+
The code below is querying the existing content available at the root. Normally you can create a "Home Page" here, but if one already exists that option is removed.
33+
34+
It then shows how to limit the allowed children by only permitting a single "Landing Page" under the "Home Page" Document Type.
3335

3436
```csharp
35-
internal class OneHomePageOnlyContentTypeFilter : IContentTypeFilter
37+
using Umbraco.Cms.Core;
38+
using Umbraco.Cms.Core.Composing;
39+
using Umbraco.Cms.Core.Models;
40+
using Umbraco.Cms.Core.Services;
41+
using Umbraco.Cms.Core.Services.Filters;
42+
43+
internal class MyContentTypeFilter : IContentTypeFilter
3644
{
45+
private const string HomePageDocTypeAlias = "homePage";
46+
private const string LandingPageDocTypeAlias = "landingPage";
47+
3748
private readonly IContentService _contentService;
49+
private readonly IContentTypeService _contentTypeService;
50+
private readonly IIdKeyMap _idKeyMap;
3851

39-
public OneHomePageOnlyContentTypeFilter(IContentService contentService) => _contentService = contentService;
52+
public MyContentTypeFilter(IContentService contentService, IContentTypeService contentTypeService, IIdKeyMap idKeyMap)
53+
{
54+
_contentService = contentService;
55+
_contentTypeService = contentTypeService;
56+
_idKeyMap = idKeyMap;
57+
}
4058

4159
public Task<IEnumerable<TItem>> FilterAllowedAtRootAsync<TItem>(IEnumerable<TItem> contentTypes)
4260
where TItem : IContentTypeComposition
4361
{
62+
// Only allow one home page at the root.
4463
var docTypeAliasesToExclude = new List<string>();
4564

46-
const string HomePageDocTypeAlias = "homePage";
4765
var docTypeAliasesAtRoot = _contentService.GetRootContent()
4866
.Select(x => x.ContentType.Alias)
4967
.Distinct()
@@ -57,12 +75,47 @@ internal class OneHomePageOnlyContentTypeFilter : IContentTypeFilter
5775
.Where(x => docTypeAliasesToExclude.Contains(x.Alias) is false));
5876
}
5977

60-
public Task<IEnumerable<ContentTypeSort>> FilterAllowedChildrenAsync(IEnumerable<ContentTypeSort> contentTypes, Guid parentKey)
61-
=> Task.FromResult(contentTypes);
78+
public Task<IEnumerable<ContentTypeSort>> FilterAllowedChildrenAsync(
79+
IEnumerable<ContentTypeSort> contentTypes,
80+
Guid parentContentTypeKey,
81+
Guid? parentContentKey)
82+
{
83+
// Only allow one landing page when under a home page.
84+
if (parentContentKey.HasValue is false)
85+
{
86+
return Task.FromResult(contentTypes);
87+
}
88+
89+
IContentType? docType = _contentTypeService.Get(parentContentTypeKey);
90+
if (docType is null || docType.Alias != HomePageDocTypeAlias)
91+
{
92+
return Task.FromResult(contentTypes);
93+
}
94+
95+
var docTypeAliasesToExclude = new List<string>();
96+
97+
Attempt<int> parentContentIdAttempt = _idKeyMap.GetIdForKey(parentContentKey.Value, UmbracoObjectTypes.Document);
98+
if (parentContentIdAttempt.Success is false)
99+
{
100+
return Task.FromResult(contentTypes);
101+
}
102+
103+
var docTypeAliasesAtFolder = _contentService.GetPagedChildren(parentContentIdAttempt.Result, 0, int.MaxValue, out _)
104+
.Select(x => x.ContentType.Alias)
105+
.Distinct()
106+
.ToList();
107+
if (docTypeAliasesAtFolder.Contains(LandingPageDocTypeAlias))
108+
{
109+
docTypeAliasesToExclude.Add(LandingPageDocTypeAlias);
110+
}
111+
112+
return Task.FromResult(contentTypes
113+
.Where(x => docTypeAliasesToExclude.Contains(x.Alias) is false));
114+
}
62115
}
63116
```
64117

65-
Content Type Filters are registered as a collection, making it possible to have more than one in the solution or an installed package.
118+
Allowed Content Type Filters are registered as a collection, making it possible to have more than one in the solution or an installed package.
66119

67120
The filters need to be registered in a composer:
68121

@@ -72,7 +125,7 @@ public class MyComposer : IComposer
72125
public void Compose(IUmbracoBuilder builder)
73126
{
74127
builder.ContentTypeFilters()
75-
.Append<OneHomePageOnlyContentTypeFilter>();
128+
.Append<MyContentTypeFilter>();
76129
}
77130
}
78131
```

15/umbraco-cms/reference/notifications/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,6 @@ Below you can find some articles with some examples using Notifications.
285285
* [Hot vs. cold restarts](hot-vs-cold-restarts.md)
286286
* [MediaService Notifications](mediaservice-notifications.md)
287287
* [MemberService Notifications](memberservice-notifications.md)
288+
* [UserService Notifications](userservice-notifications.md)
288289
* [Sending Allowed Children Notification](sendingallowedchildrennotifications.md)
289290
* [Umbraco Application Lifetime Notifications](umbracoapplicationlifetime-notifications.md)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description: Example of how to use a UserService Notification
3+
---
4+
5+
# UserService Notifications
6+
7+
The UserService implements `IUserService` and provides access to operations involving `IUser`.
8+
9+
The following example illustrates how to cancel the password reset feature for a given set of users.
10+
11+
## Usage
12+
13+
```csharp
14+
using Umbraco.Cms.Core.Events;
15+
using Umbraco.Cms.Core.Notifications;
16+
17+
namespace MySite;
18+
19+
public class UserPasswordResettingNotificationHandler : INotificationHandler<UserPasswordResettingNotification>
20+
{
21+
public void Handle(UserPasswordResettingNotification notification)
22+
{
23+
if (notification.User.Name?.Contains("Eddie") ?? false)
24+
{
25+
notification.CancelOperation(new EventMessage("fail", "Can't reset password for users with name containing 'Eddie'"));
26+
}
27+
}
28+
}
29+
```

15/umbraco-cms/reference/routing/request-pipeline/outbound-pipeline.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,30 @@ To explain things we will use the following content tree:
1616

1717
## 1. Create segments
1818

19-
When the URL is constructed, Umbraco will convert every node in the tree into a segment. Each published Content item has a corresponding URL segment.
19+
When the URL is constructed, Umbraco will convert every document node in the tree into a segment. Each published document has a corresponding URL segment.
2020

2121
In our example "Our Products" will become "our-products" and "Swibble" will become "swibble".
2222

2323
The segments are created by the "Url Segment provider"
2424

2525
### Url Segment Provider
2626

27-
The DI container of an Umbraco implementation contains a collection of `UrlSegmentProviders`. This collection is populated during Umbraco boot up. Umbraco ships with a 'DefaultUrlSegmentProvider' - but custom implementations can be added to the collection.
27+
The DI container of an Umbraco implementation contains a collection of `UrlSegmentProviders`. This collection is populated during Umbraco start up. Umbraco ships with a `DefaultUrlSegmentProvider` and custom implementations can be added to the collection.
2828

29-
When the `GetUrlSegment` extension method is called for a content item + culture combination, each registered `IUrlSegmentProvider` in the collection is executed in 'collection order'. This continues until a particular `UrlSegmentProvider` returns a segment value for the content, and no further `UrlSegmentProviders` in the collection will be executed. If no segment is returned by any provider in the collection a `DefaultUrlSegmentProvider` will be used to create a segment. This ensures that a segment is always created, like when a default provider is removed from a collection without a new one being added.
29+
When the segments are requested for a document and culture combination, each registered `IUrlSegmentProvider` in the collection is executed in _collection order_. Each provider can provide a segment for the document and culture or return `null`.
30+
31+
Each URL segment provider is configured either to terminate after providing a segment or to allow other segment providers to be executed. When a terminating provider returns a segment value for the document and culture, no further `UrlSegmentProviders` in the collection will be executed.
32+
33+
If the provider does not terminate, other providers can also return segments. In this way, multiple segments can be returned for a single document and culture combination. Along with the use of custom `IUrlProvider` and `IContentFinder` instances, considerable flexibility in the generated URLs can be achieved.
34+
35+
If no segment is returned by any provider in the collection, a `DefaultUrlSegmentProvider` will be used to create a segment. This ensures that a segment is always created, even when a default provider has been removed from the collection without a new one being added.
3036

3137
To create a new Url Segment Provider, implement the following interface:
3238

3339
```csharp
3440
public interface IUrlSegmentProvider
3541
{
42+
bool AllowAdditionalSegments => false;
3643
string GetUrlSegment(IContentBase content, string? culture = null);
3744
}
3845
```
@@ -59,7 +66,7 @@ public class ProductPageUrlSegmentProvider : IUrlSegmentProvider
5966
{
6067
_provider = new DefaultUrlSegmentProvider(stringHelper);
6168
}
62-
69+
6370
public string GetUrlSegment(IContentBase content, string? culture = null)
6471
{
6572
// Only apply this rule for product pages
@@ -259,7 +266,7 @@ public class ProductPageUrlProvider : NewDefaultUrlProvider
259266
{
260267
return null;
261268
}
262-
269+
263270
// Only apply this to product pages
264271
if (content.ContentType.Alias == "productPage")
265272
{
@@ -270,7 +277,7 @@ public class ProductPageUrlProvider : NewDefaultUrlProvider
270277
{
271278
return null;
272279
}
273-
280+
274281
if (!defaultUrlInfo.IsUrl)
275282
{
276283
// This is a message (eg published but not visible because the parent is unpublished or similar)
@@ -453,7 +460,7 @@ using Umbraco.Cms.Core.Composing;
453460
namespace RoutingDocs.SiteDomainMapping;
454461

455462
public class AddSiteComposer : ComponentComposer<SiteDomainMapperComponent>
456-
{
463+
{
457464
}
458465
```
459466

0 commit comments

Comments
 (0)