Merge branch 'temp8-3658-contextual-info-app' into temp8
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Tests.Routing
|
||||
{
|
||||
[TestFixture]
|
||||
public class GetContentUrlsTests : UrlRoutingTestBase
|
||||
{
|
||||
private IUmbracoSettingsSection _umbracoSettings;
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
|
||||
//generate new mock settings and assign so we can configure in individual tests
|
||||
_umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings();
|
||||
SettingsForTests.ConfigureSettings(_umbracoSettings);
|
||||
}
|
||||
|
||||
private ILocalizedTextService GetTextService()
|
||||
{
|
||||
var textService = Mock.Of<ILocalizedTextService>(
|
||||
x => x.Localize("content/itemNotPublished",
|
||||
It.IsAny<CultureInfo>(),
|
||||
It.IsAny<IDictionary<string, string>>()) == "content/itemNotPublished");
|
||||
return textService;
|
||||
}
|
||||
|
||||
private ILocalizationService GetLangService(params string[] isoCodes)
|
||||
{
|
||||
var allLangs = isoCodes
|
||||
.Select(CultureInfo.GetCultureInfo)
|
||||
.Select(culture => new Language(culture.Name)
|
||||
{
|
||||
CultureName = culture.DisplayName,
|
||||
IsDefault = true,
|
||||
IsMandatory = true
|
||||
}).ToArray();
|
||||
|
||||
var langService = Mock.Of<ILocalizationService>(x => x.GetAllLanguages() == allLangs);
|
||||
return langService;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Content_Not_Published()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
var content = MockedContent.CreateBasicContent(contentType);
|
||||
content.Id = 1046; //fixme: we are using this ID only because it's built into the test XML published cache
|
||||
content.Path = "-1,1046";
|
||||
|
||||
var umbContext = GetUmbracoContext("http://localhost:8000");
|
||||
var publishedRouter = CreatePublishedRouter(Container,
|
||||
contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(Logger) }));
|
||||
var urls = content.GetContentUrls(publishedRouter,
|
||||
umbContext,
|
||||
GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService,
|
||||
Logger).ToList();
|
||||
|
||||
Assert.AreEqual(1, urls.Count);
|
||||
Assert.AreEqual("content/itemNotPublished", urls[0].Text);
|
||||
Assert.IsFalse(urls[0].IsUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Invariant_Root_Content_Published_No_Domains()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
var content = MockedContent.CreateBasicContent(contentType);
|
||||
content.Id = 1046; //fixme: we are using this ID only because it's built into the test XML published cache
|
||||
content.Path = "-1,1046";
|
||||
content.Published = true;
|
||||
|
||||
var umbContext = GetUmbracoContext("http://localhost:8000",
|
||||
urlProviders: new []{ new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) });
|
||||
var publishedRouter = CreatePublishedRouter(Container,
|
||||
contentFinders:new ContentFinderCollection(new[]{new ContentFinderByUrl(Logger) }));
|
||||
var urls = content.GetContentUrls(publishedRouter,
|
||||
umbContext,
|
||||
GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService,
|
||||
Logger).ToList();
|
||||
|
||||
Assert.AreEqual(1, urls.Count);
|
||||
Assert.AreEqual("/home/", urls[0].Text);
|
||||
Assert.AreEqual("en-US", urls[0].Culture);
|
||||
Assert.IsTrue(urls[0].IsUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Invariant_Child_Content_Published_No_Domains()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
var parent = MockedContent.CreateBasicContent(contentType);
|
||||
parent.Id = 1046; //fixme: we are using this ID only because it's built into the test XML published cache
|
||||
parent.Name = "home";
|
||||
parent.Path = "-1,1046";
|
||||
parent.Published = true;
|
||||
var child = MockedContent.CreateBasicContent(contentType);
|
||||
child.Name = "sub1";
|
||||
child.Id = 1173; //fixme: we are using this ID only because it's built into the test XML published cache
|
||||
child.Path = "-1,1046,1173";
|
||||
child.Published = true;
|
||||
|
||||
var umbContext = GetUmbracoContext("http://localhost:8000",
|
||||
urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) });
|
||||
var publishedRouter = CreatePublishedRouter(Container,
|
||||
contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(Logger) }));
|
||||
var urls = child.GetContentUrls(publishedRouter,
|
||||
umbContext,
|
||||
GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService,
|
||||
Logger).ToList();
|
||||
|
||||
Assert.AreEqual(1, urls.Count);
|
||||
Assert.AreEqual("/home/sub1/", urls[0].Text);
|
||||
Assert.AreEqual("en-US", urls[0].Culture);
|
||||
Assert.IsTrue(urls[0].IsUrl);
|
||||
}
|
||||
|
||||
//TODO: We need a lot of tests here, the above was just to get started with being able to unit test this method
|
||||
// * variant URLs without domains assigned, what happens?
|
||||
// * variant URLs with domains assigned, but also having more languages installed than there are domains/cultures assigned
|
||||
// * variant URLs with an ancestor culture unpublished
|
||||
// * invariant URLs with ancestors as variants
|
||||
// * ... probably a lot more
|
||||
|
||||
}
|
||||
}
|
||||
@@ -418,8 +418,8 @@ namespace Umbraco.Tests.Routing
|
||||
foreach (var x in result) Console.WriteLine(x);
|
||||
|
||||
Assert.AreEqual(2, result.Length);
|
||||
Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/"));
|
||||
Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/"));
|
||||
Assert.AreEqual(result[0].Text, "http://domain1b.com/en/1001-1-1/");
|
||||
Assert.AreEqual(result[1].Text, "http://domain1a.com/en/1001-1-1/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
|
||||
var urlHelper = new Mock<IUrlProvider>();
|
||||
urlHelper.Setup(provider => provider.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlProviderMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns("/hello/world/1234");
|
||||
.Returns(UrlInfo.Url("/hello/world/1234"));
|
||||
|
||||
var membershipHelper = new MembershipHelper(umbCtx, Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>());
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Tests.Testing.TestingTests
|
||||
|
||||
var urlProviderMock = new Mock<IUrlProvider>();
|
||||
urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlProviderMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns("/hello/world/1234");
|
||||
.Returns(UrlInfo.Url("/hello/world/1234"));
|
||||
var urlProvider = urlProviderMock.Object;
|
||||
|
||||
var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.VariationContextAccessor);
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
|
||||
<Compile Include="PublishedContent\SolidPublishedSnapshot.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheTests.cs" />
|
||||
<Compile Include="Routing\GetContentUrlsTests.cs" />
|
||||
<Compile Include="Services\ContentServicePublishBranchTests.cs" />
|
||||
<Compile Include="Services\ContentServiceTagsTests.cs" />
|
||||
<Compile Include="Services\ContentTypeServiceVariantsTests.cs" />
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Umbraco.Tests.Web
|
||||
var testUrlProvider = new Mock<IUrlProvider>();
|
||||
testUrlProvider
|
||||
.Setup(x => x.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlProviderMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => "/my-test-url");
|
||||
.Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url"));
|
||||
|
||||
var globalSettings = SettingsForTests.GenerateMockGlobalSettings();
|
||||
|
||||
|
||||
+66
-42
@@ -10,6 +10,8 @@
|
||||
var auditTrailLoaded = false;
|
||||
var labels = {};
|
||||
scope.publishStatus = [];
|
||||
scope.currentVariant = null;
|
||||
scope.currentUrls = [];
|
||||
|
||||
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
scope.allowChangeDocumentType = false;
|
||||
@@ -17,6 +19,18 @@
|
||||
|
||||
function onInit() {
|
||||
|
||||
// set currentVariant
|
||||
scope.currentVariant = _.find(scope.node.variants, (v) => v.active);
|
||||
|
||||
// find the urls for the currently selected language
|
||||
if(scope.node.variants.length > 1) {
|
||||
// nodes with variants
|
||||
scope.currentUrls = _.filter(scope.node.urls, (url) => scope.currentVariant.language.culture === url.culture);
|
||||
} else {
|
||||
// invariant nodes
|
||||
scope.currentUrls = scope.node.urls;
|
||||
}
|
||||
|
||||
// if there are any infinite editors open we are in infinite editing
|
||||
scope.isInfiniteMode = editorService.getNumberOfEditors() > 0 ? true : false;
|
||||
|
||||
@@ -34,7 +48,10 @@
|
||||
"content_publishedPendingChanges",
|
||||
"content_notCreated",
|
||||
"prompt_unsavedChanges",
|
||||
"prompt_doctypeChangeWarning"
|
||||
"prompt_doctypeChangeWarning",
|
||||
"general_history",
|
||||
"auditTrails_historyIncludingVariants",
|
||||
"content_itemNotPublished"
|
||||
];
|
||||
|
||||
localizationService.localizeMany(keys)
|
||||
@@ -46,8 +63,22 @@
|
||||
labels.notCreated = data[4];
|
||||
labels.unsavedChanges = data[5];
|
||||
labels.doctypeChangeWarning = data[6];
|
||||
labels.notPublished = data[9];
|
||||
|
||||
scope.historyLabel = scope.node.variants && scope.node.variants.length === 1 ? data[7] : data[8];
|
||||
|
||||
setNodePublishStatus();
|
||||
|
||||
setNodePublishStatus(scope.node);
|
||||
if (scope.currentUrls.length === 0) {
|
||||
if (scope.node.id > 0) {
|
||||
//it's created but not published
|
||||
scope.currentUrls.push({ text: labels.notPublished, isUrl: false });
|
||||
}
|
||||
else {
|
||||
//it's new
|
||||
scope.currentUrls.push({ text: labels.notCreated, isUrl: false })
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -55,6 +86,9 @@
|
||||
"id": scope.node.id
|
||||
};
|
||||
|
||||
// make sure dates are formatted to the user's locale
|
||||
formatDatesToLocal();
|
||||
|
||||
// get available templates
|
||||
scope.availableTemplates = scope.node.allowedTemplates;
|
||||
|
||||
@@ -218,12 +252,13 @@
|
||||
|
||||
function setAuditTrailLogTypeColor(auditTrail) {
|
||||
angular.forEach(auditTrail, function (item) {
|
||||
|
||||
switch (item.logType) {
|
||||
case "Publish":
|
||||
case "PublishVariant":
|
||||
item.logTypeColor = "success";
|
||||
break;
|
||||
case "Unpublish":
|
||||
case "UnpublishVariant":
|
||||
case "Delete":
|
||||
item.logTypeColor = "danger";
|
||||
break;
|
||||
@@ -233,51 +268,40 @@
|
||||
});
|
||||
}
|
||||
|
||||
function setNodePublishStatus(node) {
|
||||
function setNodePublishStatus() {
|
||||
|
||||
scope.publishStatus = [];
|
||||
scope.status = {};
|
||||
|
||||
// deleted node
|
||||
if (node.trashed === true) {
|
||||
scope.publishStatus.push({
|
||||
label: labels.deleted,
|
||||
color: "danger"
|
||||
});
|
||||
if (scope.node.trashed === true) {
|
||||
scope.status.color = "danger";
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.variants) {
|
||||
for (var i = 0; i < node.variants.length; i++) {
|
||||
|
||||
var variant = node.variants[i];
|
||||
|
||||
var status = {
|
||||
culture: variant.language ? variant.language.culture : null
|
||||
};
|
||||
|
||||
if (variant.state === "NotCreated") {
|
||||
status.label = labels.notCreated;
|
||||
status.color = "gray";
|
||||
}
|
||||
else if (variant.state === "Draft") {
|
||||
// draft node
|
||||
status.label = labels.unpublished;
|
||||
status.color = "gray";
|
||||
}
|
||||
else if (variant.state === "Published") {
|
||||
// published node
|
||||
status.label = labels.published;
|
||||
status.color = "success";
|
||||
}
|
||||
else if (variant.state === "PublishedPendingChanges") {
|
||||
// published node with pending changes
|
||||
status.label = labels.publishedPendingChanges;
|
||||
status.color = "success";
|
||||
}
|
||||
|
||||
scope.publishStatus.push(status);
|
||||
}
|
||||
// variant status
|
||||
if (scope.currentVariant.state === "NotCreated") {
|
||||
// not created
|
||||
scope.status.color = "gray";
|
||||
}
|
||||
else if (scope.currentVariant.state === "Draft") {
|
||||
// draft node
|
||||
scope.status.color = "gray";
|
||||
}
|
||||
else if (scope.currentVariant.state === "Published") {
|
||||
// published node
|
||||
scope.status.color = "success";
|
||||
}
|
||||
else if (scope.currentVariant.state === "PublishedPendingChanges") {
|
||||
// published node with pending changes
|
||||
scope.status.color = "success";
|
||||
}
|
||||
}
|
||||
|
||||
function formatDatesToLocal() {
|
||||
// get current backoffice user and format dates
|
||||
userService.getCurrentUser().then(function (currentUser) {
|
||||
scope.currentVariant.createDateFormatted = dateHelper.getLocalDate(scope.currentVariant.createDate, currentUser.locale, 'LLL');
|
||||
});
|
||||
}
|
||||
|
||||
// load audit trail and redirects when on the info tab
|
||||
@@ -303,7 +327,7 @@
|
||||
auditTrailLoaded = false;
|
||||
loadAuditTrail();
|
||||
loadRedirectUrls();
|
||||
setNodePublishStatus(scope.node);
|
||||
setNodePublishStatus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
<div class="umb-package-details__main-content">
|
||||
|
||||
<umb-box ng-if="node.urls" data-element="node-info-urls">
|
||||
<umb-box ng-if="currentUrls" data-element="node-info-urls">
|
||||
<umb-box-header title-key="general_links"></umb-box-header>
|
||||
<umb-box-content class="block-form">
|
||||
<ul class="nav nav-stacked" style="margin-bottom: 0;">
|
||||
<li ng-repeat="url in node.urls">
|
||||
<li ng-repeat="url in currentUrls">
|
||||
<span ng-if="url.isUrl">
|
||||
|
||||
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<a href="{{url.text}}" target="_blank">
|
||||
<i class="icon icon-window-popin"></i>
|
||||
<span>{{url.text}}</span>
|
||||
</a>
|
||||
<span ng-if="url.culture" style="font-size: 13px; color: #cccccc; margin-left: 20px;">{{url.culture}}</span>
|
||||
|
||||
</span>
|
||||
<div ng-if="!url.isUrl" style="margin-top: 4px;">
|
||||
<span>{{url.text}}</span>
|
||||
<span ng-if="url.culture" style="font-size: 13px; color: #cccccc; margin-left: 20px;">{{url.culture}}</span>
|
||||
|
||||
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<em>{{url.text}}</em>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -43,7 +47,8 @@
|
||||
<umb-box data-element="node-info-history">
|
||||
|
||||
<umb-box-header
|
||||
title-key="general_history">
|
||||
title="{{historyLabel}}">
|
||||
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="outline"
|
||||
@@ -62,8 +67,9 @@
|
||||
<umb-load-indicator ng-show="loadingAuditTrail"></umb-load-indicator>
|
||||
|
||||
<div ng-show="auditTrail.length === 0" style="padding: 10px;">
|
||||
<umb-empty-state position="center"
|
||||
size="small">
|
||||
<umb-empty-state
|
||||
position="center"
|
||||
size="small">
|
||||
<localize key="content_noChanges"></localize>
|
||||
</umb-empty-state>
|
||||
</div>
|
||||
@@ -96,7 +102,6 @@
|
||||
class="history-item__badge"
|
||||
size="xs"
|
||||
color="{{item.logTypeColor}}">
|
||||
|
||||
<localize key="auditTrails_small{{ item.logType }}">{{ item.logType }}</localize>
|
||||
</umb-badge>
|
||||
<span>
|
||||
@@ -130,16 +135,13 @@
|
||||
<umb-box-content class="block-form">
|
||||
|
||||
<umb-control-group data-element="node-info-status" label="@general_status">
|
||||
<div ng-repeat="status in publishStatus" style="margin-bottom: 5px;">
|
||||
<span ng-show="status.culture"><em>{{status.culture}}: </em></span>
|
||||
<umb-badge size="xs" color="{{status.color}}">
|
||||
{{status.label}}
|
||||
</umb-badge>
|
||||
</div>
|
||||
<umb-badge size="xs" color="{{status.color}}">
|
||||
<umb-variant-state variant="currentVariant"></umb-variant-state>
|
||||
</umb-badge>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group ng-show="node.id !== 0" data-element="node-info-create-date" label="@template_createdDate">
|
||||
{{node.createDateFormatted}} <localize key="general_by">by</localize> {{ node.owner.name }}
|
||||
{{currentVariant.createDateFormatted}}
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group data-element="node-info-document-type" label="@content_documentType">
|
||||
|
||||
@@ -147,8 +147,9 @@
|
||||
<key alias="atViewingFor">Viewing for</key>
|
||||
<key alias="delete">Content deleted</key>
|
||||
<key alias="unpublish">Content unpublished</key>
|
||||
<key alias="publish">Content saved and Published</key>
|
||||
<key alias="publishvariant">Content saved and published for languages: %0% </key>
|
||||
<key alias="unpublishvariant">Content unpublished for languages: %0% </key>
|
||||
<key alias="publish">Content published</key>
|
||||
<key alias="publishvariant">Content published for languages: %0% </key>
|
||||
<key alias="save">Content saved</key>
|
||||
<key alias="savevariant">Content saved for languages: %0%</key>
|
||||
<key alias="move">Content moved</key>
|
||||
@@ -165,10 +166,12 @@
|
||||
<key alias="smallSaveVariant">Save</key>
|
||||
<key alias="smallDelete">Delete</key>
|
||||
<key alias="smallUnpublish">Unpublish</key>
|
||||
<key alias="smallUnpublishVariant">Unpublish</key>
|
||||
<key alias="smallRollBack">Rollback</key>
|
||||
<key alias="smallSendToPublish">Send To Publish</key>
|
||||
<key alias="smallSendToPublishVariant">Send To Publish</key>
|
||||
<key alias="smallSort">Sort</key>
|
||||
<key alias="historyIncludingVariants">History (all variants)</key>
|
||||
</area>
|
||||
<area alias="changeDocType">
|
||||
<key alias="changeDocTypeInstruction">To change the document type for the selected content, first select from the list of valid types for this location.</key>
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Umbraco.Web.Routing
|
||||
#region GetUrl
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
{
|
||||
return null; // we have nothing to say
|
||||
}
|
||||
@@ -51,13 +51,14 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
public IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
{
|
||||
var node = umbracoContext.ContentCache.GetById(id);
|
||||
if (node == null) return Enumerable.Empty<string>();
|
||||
if (node == null)
|
||||
yield break;
|
||||
|
||||
if (!node.HasProperty(Constants.Conventions.Content.UrlAlias))
|
||||
return Enumerable.Empty<string>();
|
||||
yield break;
|
||||
|
||||
var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper);
|
||||
|
||||
@@ -82,20 +83,19 @@ namespace Umbraco.Web.Routing
|
||||
// the content finder may work, depending on the 'current' culture,
|
||||
// but there's no way we can return something meaningful here
|
||||
if (varies)
|
||||
return Enumerable.Empty<string>();
|
||||
yield break;
|
||||
|
||||
var umbracoUrlName = node.Value<string>(Constants.Conventions.Content.UrlAlias);
|
||||
if (string.IsNullOrWhiteSpace(umbracoUrlName))
|
||||
return Enumerable.Empty<string>();
|
||||
yield break;
|
||||
|
||||
var path = "/" + umbracoUrlName;
|
||||
var uri = new Uri(path, UriKind.Relative);
|
||||
return new[] { UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString() };
|
||||
yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// some domains: one url per domain, which is "<domain>/<alias>"
|
||||
var result = new List<string>();
|
||||
foreach(var domainUri in domainUris)
|
||||
{
|
||||
// if the property is invariant, get the invariant value, url is "<domain>/<invariant-alias>"
|
||||
@@ -108,14 +108,13 @@ namespace Umbraco.Web.Routing
|
||||
? node.Value<string>(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name)
|
||||
: node.Value<string>(Constants.Conventions.Content.UrlAlias);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(umbracoUrlName))
|
||||
{
|
||||
var path = "/" + umbracoUrlName;
|
||||
var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
|
||||
result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(umbracoUrlName))
|
||||
continue;
|
||||
|
||||
var path = "/" + umbracoUrlName;
|
||||
var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
|
||||
yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString(), domainUri.Culture.Name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <summary>
|
||||
/// This will return the URL that is returned by the assigned custom <see cref="IPublishedContent"/> if this is a custom route
|
||||
/// </summary>
|
||||
public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
{
|
||||
if (umbracoContext?.PublishedRequest?.PublishedContent == null) return null;
|
||||
if (umbracoContext.HttpContext?.Request?.RequestContext?.RouteData?.DataTokens == null) return null;
|
||||
@@ -26,13 +26,15 @@ namespace Umbraco.Web.Routing
|
||||
//NOTE: This looks like it might cause an infinite loop because PublishedContentBase.Url calls into UmbracoContext.Current.UrlProvider.GetUrl which calls back into the IUrlProvider pipeline
|
||||
// but the specific purpose of this is that a developer is using their own IPublishedContent that returns a specific Url and doesn't go back into the UrlProvider pipeline.
|
||||
//TODO: We could put a try/catch here just in case, else we could do some reflection checking to see if the implementation is PublishedContentBase and the Url property is not overridden.
|
||||
return content.Id == umbracoContext.PublishedRequest.PublishedContent.Id
|
||||
? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture)
|
||||
: null;
|
||||
return UrlInfo.Url(
|
||||
content.Id == umbracoContext.PublishedRequest.PublishedContent.Id
|
||||
? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture)
|
||||
: null,
|
||||
culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This always returns null because this url provider is used purely to deal with Umbraco custom routes with
|
||||
/// This always returns an empty result because this url provider is used purely to deal with Umbraco custom routes with
|
||||
/// UmbracoVirtualNodeRouteHandler, we really only care about the normal URL so that RedirectToCurrentUmbracoPage() works
|
||||
/// with SurfaceControllers
|
||||
/// </summary>
|
||||
@@ -40,9 +42,9 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="id"></param>
|
||||
/// <param name="current"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
public IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
{
|
||||
return null;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Web.Routing
|
||||
#region GetUrl
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
public virtual UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
|
||||
{
|
||||
if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current));
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Routing
|
||||
return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture);
|
||||
}
|
||||
|
||||
internal string GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture)
|
||||
internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(route))
|
||||
{
|
||||
@@ -58,7 +58,9 @@ namespace Umbraco.Web.Routing
|
||||
: domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current, culture);
|
||||
|
||||
// assemble the url from domainUri (maybe null) and path
|
||||
return AssembleUrl(domainUri, path, current, mode).ToString();
|
||||
var url = AssembleUrl(domainUri, path, current, mode).ToString();
|
||||
|
||||
return UrlInfo.Url(url, culture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -76,10 +78,11 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public virtual IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
public virtual IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
|
||||
{
|
||||
var node = umbracoContext.ContentCache.GetById(id);
|
||||
if (node == null) return Enumerable.Empty<string>();
|
||||
if (node == null)
|
||||
yield break;
|
||||
|
||||
var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper);
|
||||
|
||||
@@ -94,13 +97,14 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
// no domains = exit
|
||||
if (domainUris ==null)
|
||||
return Enumerable.Empty<string>();
|
||||
yield break;
|
||||
|
||||
var result = new List<string>();
|
||||
foreach (var d in domainUris)
|
||||
{
|
||||
var culture = d?.Culture?.Name;
|
||||
|
||||
//although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok
|
||||
var route = umbracoContext.ContentCache.GetRouteById(id, d?.Culture?.Name);
|
||||
var route = umbracoContext.ContentCache.GetRouteById(id, culture);
|
||||
if (route == null) continue;
|
||||
|
||||
//need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned)
|
||||
@@ -109,9 +113,8 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path));
|
||||
uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings);
|
||||
result.Add(uri.ToString());
|
||||
yield return UrlInfo.Url(uri.ToString(), culture);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -146,7 +149,7 @@ namespace Umbraco.Web.Routing
|
||||
uri = new Uri(path, UriKind.Relative);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("mode");
|
||||
throw new ArgumentOutOfRangeException(nameof(mode));
|
||||
}
|
||||
}
|
||||
else // a domain was found
|
||||
@@ -169,7 +172,7 @@ namespace Umbraco.Web.Routing
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("mode");
|
||||
throw new ArgumentOutOfRangeException(nameof(mode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="culture">The culture, or null.</param>
|
||||
/// <returns>The domain and its uri, if any, that best matches the specified uri and culture, else null.</returns>
|
||||
/// <remarks>
|
||||
/// <para>f at least a domain is set on the node then the method returns the domain that
|
||||
/// <para>If at least a domain is set on the node then the method returns the domain that
|
||||
/// best matches the specified uri and culture, else it returns null.</para>
|
||||
/// <para>If culture is null, uses the default culture for the installation instead.</para>
|
||||
/// fixme not exactly - if culture is !null, we MUST have a value for THAT culture, else we use default as hint
|
||||
/// <para>If culture is null, uses the default culture for the installation instead. Otherwise,
|
||||
/// will try with the specified culture, else return null.</para>
|
||||
/// </remarks>
|
||||
internal DomainAndUri DomainForNode(int nodeId, Uri current, string culture = null)
|
||||
{
|
||||
@@ -49,13 +49,9 @@ namespace Umbraco.Web.Routing
|
||||
return null;
|
||||
|
||||
// else filter
|
||||
var domainAndUri = SelectDomain(domains, current, culture, _domainCache.DefaultCulture,
|
||||
// it could be that none apply (due to culture)
|
||||
return SelectDomain(domains, current, culture, _domainCache.DefaultCulture,
|
||||
(cdomainAndUris, ccurrent, cculture, cdefaultCulture) => _siteDomainHelper.MapDomain(cdomainAndUris, ccurrent, cculture, cdefaultCulture));
|
||||
|
||||
if (domainAndUri == null)
|
||||
throw new Exception("DomainForUri returned null.");
|
||||
|
||||
return domainAndUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,13 +198,12 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
private static IReadOnlyCollection<DomainAndUri> SelectByCulture(IReadOnlyCollection<DomainAndUri> domainsAndUris, string culture, string defaultCulture)
|
||||
{
|
||||
// we try our best to match cultures, but may end with a bogus domain
|
||||
|
||||
if (culture != null) // try the supplied culture
|
||||
{
|
||||
var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(culture)).ToList();
|
||||
if (cultureDomains.Count > 0) return cultureDomains;
|
||||
|
||||
// if a culture is supplied, we *want* a url for that culture, else fail
|
||||
throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\".");
|
||||
}
|
||||
|
||||
if (defaultCulture != null) // try the defaultCulture culture
|
||||
@@ -224,13 +219,12 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
DomainAndUri domainAndUri;
|
||||
|
||||
// we try our best to match cultures, but may end with a bogus domain
|
||||
|
||||
if (culture != null) // try the supplied culture
|
||||
{
|
||||
domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture));
|
||||
if (domainAndUri != null) return domainAndUri;
|
||||
|
||||
// if a culture is supplied, we *want* a url for that culture, else fail
|
||||
throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\".");
|
||||
}
|
||||
|
||||
if (defaultCulture != null) // try the defaultCulture culture
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing
|
||||
/// when no culture is specified, the current culture.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current);
|
||||
UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
@@ -37,6 +37,6 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current);
|
||||
IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
@@ -6,13 +7,25 @@ namespace Umbraco.Web.Routing
|
||||
/// Represents infos for a url.
|
||||
/// </summary>
|
||||
[DataContract(Name = "urlInfo", Namespace = "")]
|
||||
public class UrlInfo
|
||||
public class UrlInfo : IEquatable<UrlInfo>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="UrlInfo"/> instance representing a true url.
|
||||
/// </summary>
|
||||
public static UrlInfo Url(string text, string culture = null) => new UrlInfo(text, true, culture);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="UrlInfo"/> instance representing a message.
|
||||
/// </summary>
|
||||
public static UrlInfo Message(string text, string culture = null) => new UrlInfo(text, false, culture);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlInfo"/> class.
|
||||
/// </summary>
|
||||
public UrlInfo(string text, bool isUrl, string culture)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(text));
|
||||
IsUrl = isUrl;
|
||||
Text = text;
|
||||
Culture = culture;
|
||||
@@ -38,13 +51,47 @@ namespace Umbraco.Web.Routing
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="UrlInfo"/> instance representing a true url.
|
||||
/// Checks equality
|
||||
/// </summary>
|
||||
public static UrlInfo Url(string text, string culture = null) => new UrlInfo(text, true, culture);
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Compare both culture and Text as invariant strings since URLs are not case sensitive, nor are culture names within Umbraco
|
||||
/// </remarks>
|
||||
public bool Equals(UrlInfo other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return string.Equals(Culture, other.Culture, StringComparison.InvariantCultureIgnoreCase) && IsUrl == other.IsUrl && string.Equals(Text, other.Text, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="UrlInfo"/> instance representing a message.
|
||||
/// </summary>
|
||||
public static UrlInfo Message(string text, string culture = null) => new UrlInfo(text, false, culture);
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((UrlInfo) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (Culture != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(Culture) : 0);
|
||||
hashCode = (hashCode * 397) ^ IsUrl.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ (Text != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(Text) : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(UrlInfo left, UrlInfo right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UrlInfo left, UrlInfo right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, content, mode, culture, current))
|
||||
.FirstOrDefault(u => u != null);
|
||||
return url ?? "#"; // legacy wants this
|
||||
return url?.Text ?? "#"; // legacy wants this
|
||||
}
|
||||
|
||||
internal string GetUrlFromRoute(int id, string route, string culture)
|
||||
@@ -210,7 +210,7 @@ namespace Umbraco.Web.Routing
|
||||
var provider = _urlProviders.OfType<DefaultUrlProvider>().FirstOrDefault();
|
||||
var url = provider == null
|
||||
? route // what else?
|
||||
: provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture);
|
||||
: provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text;
|
||||
return url ?? "#";
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace Umbraco.Web.Routing
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// <para>The results depend on the current url.</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(int id)
|
||||
public IEnumerable<UrlInfo> GetOtherUrls(int id)
|
||||
{
|
||||
return GetOtherUrls(id, _umbracoContext.CleanedUmbracoUrl);
|
||||
}
|
||||
@@ -243,12 +243,9 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(int id, Uri current)
|
||||
public IEnumerable<UrlInfo> GetOtherUrls(int id, Uri current)
|
||||
{
|
||||
// providers can return null or an empty list or a non-empty list, be prepared
|
||||
var urls = _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, id, current) ?? Enumerable.Empty<string>());
|
||||
|
||||
return urls;
|
||||
return _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, id, current));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -5,7 +5,6 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using LightInject;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
@@ -18,7 +17,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Use when displaying Urls. If errors occur when generating the Urls, they will show in the list.</para>
|
||||
/// <para>Contains all the Urls that we can figure out (based upon domains, etc).</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<UrlInfo> GetContentUrls(this IContent content,
|
||||
public static IEnumerable<UrlInfo> GetContentUrls(this IContent content,
|
||||
PublishedRouter publishedRouter,
|
||||
UmbracoContext umbracoContext,
|
||||
ILocalizationService localizationService,
|
||||
@@ -34,25 +33,71 @@ namespace Umbraco.Web.Routing
|
||||
if (contentService == null) throw new ArgumentNullException(nameof(contentService));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
var urls = new List<UrlInfo>();
|
||||
|
||||
if (content.Published == false)
|
||||
{
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/itemNotPublished")));
|
||||
return urls;
|
||||
yield return UrlInfo.Message(textService.Localize("content/itemNotPublished"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
// build a list of urls, for the back-office
|
||||
// which will contain
|
||||
// - the 'main' urls, which is what .Url would return, for each culture
|
||||
// - the 'other' urls we know (based upon domains, etc)
|
||||
//
|
||||
// need to work on each culture.
|
||||
// on invariant trees, each culture return the same thing
|
||||
// but, we don't know if the tree to this content is invariant
|
||||
// need to work through each installed culture:
|
||||
// on invariant nodes, each culture returns the same url segment but,
|
||||
// we don't know if the branch to this content is invariant, so we need to ask
|
||||
// for URLs for all cultures.
|
||||
// and, not only for those assigned to domains in the branch, because we want
|
||||
// to show what GetUrl() would return, for every culture.
|
||||
|
||||
var urls = new HashSet<UrlInfo>();
|
||||
var cultures = localizationService.GetAllLanguages().Select(x => x.IsoCode).ToList();
|
||||
|
||||
//get all URLs for all cultures
|
||||
//in a HashSet, so de-duplicates too
|
||||
foreach (var cultureUrl in GetContentUrlsByCulture(content, cultures, publishedRouter, umbracoContext, contentService, textService, logger))
|
||||
{
|
||||
urls.Add(cultureUrl);
|
||||
}
|
||||
|
||||
//return the real urls first, then the messages
|
||||
foreach (var urlGroup in urls.GroupBy(x => x.IsUrl).OrderByDescending(x => x.Key))
|
||||
{
|
||||
//in some cases there will be the same URL for multiple cultures:
|
||||
// * The entire branch is invariant
|
||||
// * If there are less domain/cultures assigned to the branch than the number of cultures/languages installed
|
||||
|
||||
foreach (var dUrl in urlGroup.DistinctBy(x => x.Text.ToUpperInvariant()).OrderBy(x => x.Text).ThenBy(x => x.Culture))
|
||||
yield return dUrl;
|
||||
}
|
||||
|
||||
// get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless.
|
||||
// for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them.
|
||||
foreach (var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture))
|
||||
if (urls.Add(otherUrl)) //avoid duplicates
|
||||
yield return otherUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return a <see cref="UrlInfo"/> for each culture for the content while detecting collisions/errors
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="cultures"></param>
|
||||
/// <param name="publishedRouter"></param>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="contentService"></param>
|
||||
/// <param name="textService"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<UrlInfo> GetContentUrlsByCulture(IContent content,
|
||||
IEnumerable<string> cultures,
|
||||
PublishedRouter publishedRouter,
|
||||
UmbracoContext umbracoContext,
|
||||
IContentService contentService,
|
||||
ILocalizedTextService textService,
|
||||
ILogger logger)
|
||||
{
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
// if content is variant, and culture is not published, skip
|
||||
@@ -76,53 +121,26 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
// deal with 'could not get the url'
|
||||
case "#":
|
||||
HandleCouldNotGetUrl(content, culture, urls, contentService, textService);
|
||||
yield return HandleCouldNotGetUrl(content, culture, contentService, textService);
|
||||
break;
|
||||
|
||||
// deal with exceptions
|
||||
case "#ex":
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/getUrlException"), culture));
|
||||
yield return UrlInfo.Message(textService.Localize("content/getUrlException"), culture);
|
||||
break;
|
||||
|
||||
// got a url, deal with collisions, add url
|
||||
default:
|
||||
if (!DetectCollision(content, url, urls, culture, umbracoContext, publishedRouter, textService)) // detect collisions, etc
|
||||
urls.Add(UrlInfo.Url(url, culture));
|
||||
if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, out var urlInfo)) // detect collisions, etc
|
||||
yield return urlInfo;
|
||||
else
|
||||
yield return UrlInfo.Url(url, culture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare for de-duplication
|
||||
var durl = new Dictionary<string, List<UrlInfo>>();
|
||||
var dmsg = new Dictionary<string, List<UrlInfo>>();
|
||||
foreach (var url in urls)
|
||||
{
|
||||
var d = url.IsUrl ? durl : dmsg;
|
||||
if (!d.TryGetValue(url.Text, out var l))
|
||||
d[url.Text] = l = new List<UrlInfo>();
|
||||
l.Add(url);
|
||||
}
|
||||
|
||||
// deduplicate, order urls first then messages, concatenate cultures (hide if 'all')
|
||||
var ret = new List<UrlInfo>();
|
||||
foreach (var (text, infos) in durl)
|
||||
ret.Add(UrlInfo.Url(text, infos.Count == cultures.Count ? null : string.Join(", ", infos.Select(x => x.Culture))));
|
||||
foreach (var (text, infos) in dmsg)
|
||||
ret.Add(UrlInfo.Message(text, infos.Count == cultures.Count ? null : string.Join(", ", infos.Select(x => x.Culture))));
|
||||
|
||||
// get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless.
|
||||
// for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them.
|
||||
// also, we are not dealing with cultures at all - that will have to wait
|
||||
foreach(var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id))
|
||||
{
|
||||
if (urls.Any(x => x.IsUrl && x.Text == otherUrl)) continue;
|
||||
ret.Add(UrlInfo.Url(otherUrl));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void HandleCouldNotGetUrl(IContent content, string culture, List<UrlInfo> urls, IContentService contentService, ILocalizedTextService textService)
|
||||
private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, ILocalizedTextService textService)
|
||||
{
|
||||
// document has a published version yet its url is "#" => a parent must be
|
||||
// unpublished, walk up the tree until we find it, and report.
|
||||
@@ -134,16 +152,16 @@ namespace Umbraco.Web.Routing
|
||||
while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture)));
|
||||
|
||||
if (parent == null) // oops, internal error
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture));
|
||||
return UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture);
|
||||
|
||||
else if (!parent.Published) // totally not published
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] { parent.Name }), culture));
|
||||
return UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] {parent.Name}), culture);
|
||||
|
||||
else // culture not published
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] { parent.Name }), culture));
|
||||
return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture);
|
||||
}
|
||||
|
||||
private static bool DetectCollision(IContent content, string url, List<UrlInfo> urls, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService)
|
||||
private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo)
|
||||
{
|
||||
// test for collisions on the 'main' url
|
||||
var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute);
|
||||
@@ -152,9 +170,11 @@ namespace Umbraco.Web.Routing
|
||||
var pcr = publishedRouter.CreateRequest(umbracoContext, uri);
|
||||
publishedRouter.TryRouteRequest(pcr);
|
||||
|
||||
urlInfo = null;
|
||||
|
||||
if (pcr.HasPublishedContent == false)
|
||||
{
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture));
|
||||
urlInfo = UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -173,7 +193,7 @@ namespace Umbraco.Web.Routing
|
||||
l.Reverse();
|
||||
var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")";
|
||||
|
||||
urls.Add(UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture));
|
||||
urlInfo = UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user