Skip to content

Commit 004d6a2

Browse files
committed
Serverside generated preview URLs
1 parent da8c036 commit 004d6a2

24 files changed

+260
-86
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Umbraco.Cms.Api.Management.Factories;
5+
using Umbraco.Cms.Api.Management.ViewModels.Document;
6+
using Umbraco.Cms.Core.Models;
7+
using Umbraco.Cms.Core.Services;
8+
9+
namespace Umbraco.Cms.Api.Management.Controllers.Document;
10+
11+
[ApiVersion("1.0")]
12+
public class DocumentPreviewUrlController : DocumentControllerBase
13+
{
14+
private readonly IContentService _contentService;
15+
private readonly IDocumentUrlFactory _documentUrlFactory;
16+
17+
public DocumentPreviewUrlController(
18+
IContentService contentService,
19+
IDocumentUrlFactory documentUrlFactory)
20+
{
21+
_contentService = contentService;
22+
_documentUrlFactory = documentUrlFactory;
23+
}
24+
25+
[MapToApiVersion("1.0")]
26+
[HttpGet("preview-urls")]
27+
[ProducesResponseType(typeof(IEnumerable<DocumentUrlInfoResponseModel>), StatusCodes.Status200OK)]
28+
public async Task<IActionResult> GetPreviewUrls([FromQuery(Name = "id")] HashSet<Guid> ids)
29+
{
30+
IEnumerable<IContent> items = _contentService.GetByIds(ids);
31+
32+
return Ok(await _documentUrlFactory.CreatePreviewUrlSetsAsync(items));
33+
}
34+
}

src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ namespace Umbraco.Cms.Api.Management.Factories;
77
public class DocumentUrlFactory : IDocumentUrlFactory
88
{
99
private readonly IPublishedUrlInfoProvider _publishedUrlInfoProvider;
10+
private readonly UrlProviderCollection _urlProviders;
1011

11-
12-
public DocumentUrlFactory(IPublishedUrlInfoProvider publishedUrlInfoProvider)
13-
=> _publishedUrlInfoProvider = publishedUrlInfoProvider;
12+
public DocumentUrlFactory(IPublishedUrlInfoProvider publishedUrlInfoProvider, UrlProviderCollection urlProviders)
13+
{
14+
_publishedUrlInfoProvider = publishedUrlInfoProvider;
15+
_urlProviders = urlProviders;
16+
}
1417

1518
public async Task<IEnumerable<DocumentUrlInfo>> CreateUrlsAsync(IContent content)
1619
{
1720
ISet<UrlInfo> urlInfos = await _publishedUrlInfoProvider.GetAllAsync(content);
18-
19-
return urlInfos
20-
.Where(urlInfo => urlInfo.IsUrl)
21-
.Select(urlInfo => new DocumentUrlInfo { Culture = urlInfo.Culture, Url = urlInfo.Text })
22-
.ToArray();
21+
return CreateDocumentUrlInfos(urlInfos);
2322
}
2423

2524
public async Task<IEnumerable<DocumentUrlInfoResponseModel>> CreateUrlSetsAsync(IEnumerable<IContent> contentItems)
@@ -34,4 +33,22 @@ public async Task<IEnumerable<DocumentUrlInfoResponseModel>> CreateUrlSetsAsync(
3433

3534
return documentUrlInfoResourceSets;
3635
}
36+
37+
public Task<IEnumerable<DocumentUrlInfoResponseModel>> CreatePreviewUrlSetsAsync(IEnumerable<IContent> contentItems)
38+
{
39+
DocumentUrlInfoResponseModel[] documentUrlInfoResourceSets = contentItems.Select(content =>
40+
{
41+
IEnumerable<UrlInfo> previewUrls = _urlProviders.SelectMany(provider => provider.GetPreviewUrls(content));
42+
return new DocumentUrlInfoResponseModel(content.Key, CreateDocumentUrlInfos(previewUrls));
43+
})
44+
.ToArray();
45+
46+
return Task.FromResult<IEnumerable<DocumentUrlInfoResponseModel>>(documentUrlInfoResourceSets);
47+
}
48+
49+
private IEnumerable<DocumentUrlInfo> CreateDocumentUrlInfos(IEnumerable<UrlInfo> urlInfos)
50+
=> urlInfos
51+
.Where(urlInfo => urlInfo.Url is not null)
52+
.Select(urlInfo => new DocumentUrlInfo { Culture = urlInfo.Culture, Url = urlInfo.Url!.ToString(), Message = urlInfo.Message, IsExternal = urlInfo.IsExternal })
53+
.ToArray();
3754
}

src/Umbraco.Cms.Api.Management/Factories/IDocumentUrlFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ public interface IDocumentUrlFactory
88
Task<IEnumerable<DocumentUrlInfo>> CreateUrlsAsync(IContent content);
99

1010
Task<IEnumerable<DocumentUrlInfoResponseModel>> CreateUrlSetsAsync(IEnumerable<IContent> contentItems);
11+
12+
Task<IEnumerable<DocumentUrlInfoResponseModel>> CreatePreviewUrlSetsAsync(IEnumerable<IContent> contentItems);
1113
}

src/Umbraco.Cms.Api.Management/Factories/MediaUrlFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public IEnumerable<MediaUrlInfo> CreateUrls(IMedia media) =>
4040
{
4141
Culture = null,
4242
Url = _absoluteUrlBuilder.ToAbsoluteUrl(mediaUrl).ToString(),
43+
IsExternal = false,
4344
})
4445
.ToArray();
4546

src/Umbraco.Cms.Api.Management/Factories/ReziseImageUrlFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ private IEnumerable<MediaUrlInfo> CreateThumbnailUrls(IEnumerable<string> urls,
5353
{
5454
Culture = null,
5555
Url = _absoluteUrlBuilder.ToAbsoluteUrl(url).ToString(),
56+
IsExternal = false,
5657
};
5758
}
5859

@@ -76,6 +77,7 @@ private IEnumerable<MediaUrlInfo> CreateThumbnailUrls(IEnumerable<string> urls,
7677
{
7778
Culture = null,
7879
Url = _absoluteUrlBuilder.ToAbsoluteUrl(relativeUrl).ToString(),
80+
IsExternal = false,
7981
};
8082
}
8183
}

src/Umbraco.Cms.Api.Management/OpenApi.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9982,6 +9982,58 @@
99829982
]
99839983
}
99849984
},
9985+
"/umbraco/management/api/v1/document/preview-urls": {
9986+
"get": {
9987+
"tags": [
9988+
"Document"
9989+
],
9990+
"operationId": "GetDocumentPreviewUrls",
9991+
"parameters": [
9992+
{
9993+
"name": "id",
9994+
"in": "query",
9995+
"schema": {
9996+
"uniqueItems": true,
9997+
"type": "array",
9998+
"items": {
9999+
"type": "string",
10000+
"format": "uuid"
10001+
}
10002+
}
10003+
}
10004+
],
10005+
"responses": {
10006+
"200": {
10007+
"description": "OK",
10008+
"content": {
10009+
"application/json": {
10010+
"schema": {
10011+
"type": "array",
10012+
"items": {
10013+
"oneOf": [
10014+
{
10015+
"$ref": "#/components/schemas/DocumentUrlInfoResponseModel"
10016+
}
10017+
]
10018+
}
10019+
}
10020+
}
10021+
}
10022+
},
10023+
"401": {
10024+
"description": "The resource is protected and requires an authentication token"
10025+
},
10026+
"403": {
10027+
"description": "The authenticated user does not have access to this resource"
10028+
}
10029+
},
10030+
"security": [
10031+
{
10032+
"Backoffice-User": [ ]
10033+
}
10034+
]
10035+
}
10036+
},
998510037
"/umbraco/management/api/v1/document/sort": {
998610038
"put": {
998710039
"tags": [
@@ -38950,6 +39002,8 @@
3895039002
"DocumentUrlInfoModel": {
3895139003
"required": [
3895239004
"culture",
39005+
"isExternal",
39006+
"message",
3895339007
"url"
3895439008
],
3895539009
"type": "object",
@@ -38960,6 +39014,13 @@
3896039014
},
3896139015
"url": {
3896239016
"type": "string"
39017+
},
39018+
"isExternal": {
39019+
"type": "boolean"
39020+
},
39021+
"message": {
39022+
"type": "string",
39023+
"nullable": true
3896339024
}
3896439025
},
3896539026
"additionalProperties": false
@@ -41111,6 +41172,7 @@
4111141172
"MediaUrlInfoModel": {
4111241173
"required": [
4111341174
"culture",
41175+
"isExternal",
4111441176
"url"
4111541177
],
4111641178
"type": "object",
@@ -41121,6 +41183,9 @@
4112141183
},
4112241184
"url": {
4112341185
"type": "string"
41186+
},
41187+
"isExternal": {
41188+
"type": "boolean"
4112441189
}
4112541190
},
4112641191
"additionalProperties": false

src/Umbraco.Cms.Api.Management/ViewModels/Content/ContentUrlInfoBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ public abstract class ContentUrlInfoBase
55
public required string? Culture { get; init; }
66

77
public required string Url { get; init; }
8+
9+
public required bool IsExternal { get; init; }
810
}

src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentUrlInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
44

5-
public sealed class DocumentUrlInfo : ContentUrlInfoBase
5+
public class DocumentUrlInfo : ContentUrlInfoBase
66
{
7+
public required string? Message { get; init; }
78
}

src/Umbraco.Core/Events/AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public async Task HandleAsync(ContentPublishedNotification notification, Cancell
108108
EventMessages eventMessages = _eventMessagesFactory.Get();
109109
foreach (var culture in successfulCultures)
110110
{
111-
if (urls.Where(u => u.Culture == culture || culture == "*").All(u => u.IsUrl is false))
111+
if (urls.Where(u => u.Culture == culture || culture == "*").All(u => u.Url is null))
112112
{
113113
eventMessages.Add(new EventMessage("Content published", "The document does not have a URL, possibly due to a naming collision with another document. More details can be found under Info.", EventMessageType.Warning));
114114

src/Umbraco.Core/Routing/AliasUrlProvider.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.Extensions.Options;
22
using Umbraco.Cms.Core.Configuration.Models;
3+
using Umbraco.Cms.Core.Models;
34
using Umbraco.Cms.Core.Models.PublishedContent;
45
using Umbraco.Cms.Core.Services.Navigation;
56
using Umbraco.Cms.Core.Web;
@@ -120,7 +121,7 @@ public IEnumerable<UrlInfo> GetOtherUrls(int id, Uri current)
120121
{
121122
var path = "/" + alias;
122123
var uri = new Uri(path, UriKind.Relative);
123-
yield return UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString());
124+
yield return UrlInfo.FromUri(_uriUtility.UriFromUmbraco(uri, _requestConfig));
124125
}
125126
}
126127
else
@@ -152,16 +153,21 @@ public IEnumerable<UrlInfo> GetOtherUrls(int id, Uri current)
152153
{
153154
var path = "/" + alias;
154155
var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Authority), path));
155-
yield return UrlInfo.Url(
156-
_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString(),
157-
domainUri.Culture);
156+
yield return UrlInfo.FromUri(_uriUtility.UriFromUmbraco(uri, _requestConfig), domainUri.Culture);
158157
}
159158
}
160159
}
161160
}
162161

163162
#endregion
164163

164+
#region GetPreviewUrls
165+
166+
/// <inheritdoc />
167+
public IEnumerable<UrlInfo> GetPreviewUrls(IContent content) => [];
168+
169+
#endregion
170+
165171
#region Utilities
166172

167173
private string CombinePaths(string path1, string path2)

0 commit comments

Comments
 (0)