Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v15/dev' into v15/feature/determ…
Browse files Browse the repository at this point in the history
…ine-urls-at-save-and-publish-time
  • Loading branch information
bergmania committed Sep 13, 2024
2 parents 77672b0 + 06c5b14 commit 01f76a8
Show file tree
Hide file tree
Showing 33 changed files with 302 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Api.Management.ViewModels.Server;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;

namespace Umbraco.Cms.Api.Management.Controllers.Server;

[ApiVersion("1.0")]
public class ConfigurationServerController : ServerControllerBase
{
private readonly SecuritySettings _securitySettings;
private readonly GlobalSettings _globalSettings;

public ConfigurationServerController(IOptions<SecuritySettings> securitySettings) => _securitySettings = securitySettings.Value;
[Obsolete("Use the constructor that accepts all arguments. Will be removed in V16.")]
public ConfigurationServerController(IOptions<SecuritySettings> securitySettings)
: this(securitySettings, StaticServiceProvider.Instance.GetRequiredService<IOptions<GlobalSettings>>())
{
}

[ActivatorUtilitiesConstructor]
public ConfigurationServerController(IOptions<SecuritySettings> securitySettings, IOptions<GlobalSettings> globalSettings)
{
_securitySettings = securitySettings.Value;
_globalSettings = globalSettings.Value;
}

[HttpGet("configuration")]
[MapToApiVersion("1.0")]
Expand All @@ -22,6 +36,7 @@ public Task<IActionResult> Configuration(CancellationToken cancellationToken)
var responseModel = new ServerConfigurationResponseModel
{
AllowPasswordReset = _securitySettings.AllowPasswordReset,
VersionCheckPeriod = _globalSettings.VersionCheckPeriod
};

return Task.FromResult<IActionResult>(Ok(responseModel));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Server;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;

namespace Umbraco.Cms.Api.Management.Controllers.Server;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.RequireAdminAccess)]
public class UpgradeCheckServerController : ServerControllerBase
{
private readonly IUpgradeService _upgradeService;
private readonly IUmbracoVersion _umbracoVersion;

public UpgradeCheckServerController(IUpgradeService upgradeService, IUmbracoVersion umbracoVersion)
{
_upgradeService = upgradeService;
_umbracoVersion = umbracoVersion;
}

[HttpGet("upgrade-check")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(UpgradeCheckResponseModel), StatusCodes.Status200OK)]
public async Task<IActionResult> UpgradeCheck(CancellationToken cancellationToken)
{
UpgradeResult upgradeResult = await _upgradeService.CheckUpgrade(_umbracoVersion.SemanticVersion);

var responseModel = new UpgradeCheckResponseModel
{
Type = upgradeResult.UpgradeType,
Comment = upgradeResult.Comment,
Url = upgradeResult.UpgradeUrl.IsNullOrWhiteSpace()
? string.Empty
: $"{upgradeResult.UpgradeUrl}?version={_umbracoVersion.Version.ToString(3)}"
};

return Ok(responseModel);
}
}
62 changes: 61 additions & 1 deletion src/Umbraco.Cms.Api.Management/OpenApi.json
Original file line number Diff line number Diff line change
Expand Up @@ -25176,6 +25176,41 @@
]
}
},
"/umbraco/management/api/v1/server/upgrade-check": {
"get": {
"tags": [
"Server"
],
"operationId": "GetServerUpgradeCheck",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/UpgradeCheckResponseModel"
}
]
}
}
}
},
"401": {
"description": "The resource is protected and requires an authentication token"
},
"403": {
"description": "The authenticated user do not have access to this resource"
}
},
"security": [
{
"Backoffice User": [ ]
}
]
}
},
"/umbraco/management/api/v1/item/static-file": {
"get": {
"tags": [
Expand Down Expand Up @@ -42733,12 +42768,17 @@
},
"ServerConfigurationResponseModel": {
"required": [
"allowPasswordReset"
"allowPasswordReset",
"versionCheckPeriod"
],
"type": "object",
"properties": {
"allowPasswordReset": {
"type": "boolean"
},
"versionCheckPeriod": {
"type": "integer",
"format": "int32"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -44801,6 +44841,26 @@
},
"additionalProperties": false
},
"UpgradeCheckResponseModel": {
"required": [
"comment",
"type",
"url"
],
"type": "object",
"properties": {
"type": {
"type": "string"
},
"comment": {
"type": "string"
},
"url": {
"type": "string"
}
},
"additionalProperties": false
},
"UpgradeSettingsResponseModel": {
"required": [
"currentState",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public class ServerConfigurationResponseModel
{
public bool AllowPasswordReset { get; set; }

public int VersionCheckPeriod { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Umbraco.Cms.Api.Management.ViewModels.Server;

public class UpgradeCheckResponseModel
{
public required string Type { get; init; }

public required string Comment { get; init; }

public required string Url { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Umbraco.Cms.Api.Management.ViewModels.Server;

[Obsolete("Not used. Will be removed in V15.")]
public class VersionResponseModel
{
[Required]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@ public async Task<UpgradeResult> CheckUpgradeAsync(SemVersion version)
{
try
{
if (_httpClient == null)
{
_httpClient = new HttpClient();
}
_httpClient ??= new HttpClient { Timeout = TimeSpan.FromSeconds(1) };

using var content = new StringContent(_jsonSerializer.Serialize(new CheckUpgradeDto(version)), Encoding.UTF8, "application/json");

_httpClient.Timeout = TimeSpan.FromSeconds(1);
using HttpResponseMessage task = await _httpClient.PostAsync(RestApiUpgradeChecklUrl, content);
var json = await task.Content.ReadAsStringAsync();
UpgradeResult? result = _jsonSerializer.Deserialize<UpgradeResult>(json);
Expand Down
4 changes: 4 additions & 0 deletions src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ protected virtual void DefinePlan()
To<V_13_0_0.ChangeWebhookUrlColumnsToNvarcharMax>("{21C42760-5109-4C03-AB4F-7EA53577D1F5}");
To<V_13_0_0.AddExceptionOccured>("{6158F3A3-4902-4201-835E-1ED7F810B2D8}");
To<V_13_3_0.AlignUpgradedDatabase>("{985AF2BA-69D3-4DBA-95E0-AD3FA7459FA7}");
To<V_13_5_0.ChangeRedirectUrlToNvarcharMax>("{CC47C751-A81B-489A-A2BC-0240245DB687}");

// To 14.0.0
To<V_14_0_0.AddPropertyEditorUiAliasColumn>("{419827A0-4FCE-464B-A8F3-247C6092AF55}");
Expand Down Expand Up @@ -92,6 +93,9 @@ protected virtual void DefinePlan()
// To 14.2.0
To<V_14_2_0.AddMissingDateTimeConfiguration>("{20ED404C-6FF9-4F91-8AC9-2B298E0002EB}");

// To 14.3.0
To<V_13_5_0.ChangeRedirectUrlToNvarcharMax>("{EEF792FC-318C-4921-9859-51EBF07A53A3}"); // Execute again, to ensure all that migrated to 14.0.0 without 13.5 will have this

// To 15.0.0
To<V_15_0_0.AddUserClientId>("{7F4F31D8-DD71-4F0D-93FC-2690A924D84B}");
To<V_15_0_0.AddKindToUser>("{1A8835EF-F8AB-4472-B4D8-D75B7C164022}");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Linq.Expressions;
using System.Text;
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Create.Column;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;

namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_13_5_0;

public class ChangeRedirectUrlToNvarcharMax : MigrationBase
{
public ChangeRedirectUrlToNvarcharMax(IMigrationContext context) : base(context)
{
}

protected override void Migrate()
{
// We don't need to run this migration for SQLite, since ntext is not a thing there, text is just text.
if (DatabaseType == DatabaseType.SQLite)
{
return;
}

string tableName = RedirectUrlDto.TableName;
string colName = "url";

// Determine the current datatype of the column within the database
string colDataType = Database.ExecuteScalar<string>($"SELECT TOP(1) CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS" +
$" WHERE TABLE_NAME = '{tableName}' AND COLUMN_NAME = '{colName}'");

// 255 is the old length, -1 indicate MAX length
if (colDataType == "255")
{
// Upgrade to MAX length
Database.Execute($"Drop Index IX_umbracoRedirectUrl_culture_hash on {Constants.DatabaseSchema.Tables.RedirectUrl}");
Database.Execute($"ALTER TABLE {tableName} ALTER COLUMN {colName} nvarchar(MAX) NOT NULL");
Database.Execute($"CREATE INDEX IX_umbracoRedirectUrl_culture_hash ON {Constants.DatabaseSchema.Tables.RedirectUrl} (urlHash, contentKey, culture, createDateUtc)");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ internal class RedirectUrlDto

[Column("url")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[SpecialDbType(SpecialDbTypes.NVARCHARMAX)]
public string Url { get; set; } = null!;

[Column("culture")]
Expand Down
6 changes: 6 additions & 0 deletions src/Umbraco.PublishedCache.NuCache/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ private class CacheValues : CacheValue

public CacheValue For(string? culture, string? segment)
{
// As noted on IPropertyValue, null value means invariant
// But as we need an actual string value to build a CompositeStringStringKey
// We need to convert null to empty
culture ??= string.Empty;
segment ??= string.Empty;

if (culture == string.Empty && segment == string.Empty)
{
return this;
Expand Down
18 changes: 10 additions & 8 deletions tests/Umbraco.Tests.AcceptanceTest/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/Umbraco.Tests.AcceptanceTest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"dependencies": {
"@umbraco/json-models-builders": "^2.0.17",
"@umbraco/playwright-testhelpers": "^2.0.0-beta.78",
"@umbraco/playwright-testhelpers": "^2.0.0-beta.82",
"camelize": "^1.0.0",
"dotenv": "^16.3.1",
"node-fetch": "^2.6.7"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test.beforeEach(async ({umbracoApi, umbracoUi}) => {
});

test.afterEach(async ({umbracoApi}) => {
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
});

Expand All @@ -44,7 +44,8 @@ test('can create content with the image cropper data type', {tag: '@smoke'}, asy
expect(contentData.variants[0].state).toBe(expectedState);
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(dataTypeName));
expect(contentData.values[0].value.src).toContain(AliasHelper.toAlias(imageFileName));
expect(contentData.values[0].value.crops).toEqual([]);
// TODO: is no longer null, we need to set an expected crops value
// expect(contentData.values[0].value.crops).toEqual([]);
expect(contentData.values[0].value.focalPoint).toEqual(defaultFocalPoint);
});

Expand All @@ -68,7 +69,8 @@ test('can publish content with the image cropper data type', {tag: '@smoke'}, as
expect(contentData.variants[0].state).toBe(expectedState);
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(dataTypeName));
expect(contentData.values[0].value.src).toContain(AliasHelper.toAlias(imageFileName));
expect(contentData.values[0].value.crops).toEqual([]);
// TODO: is no longer null, we need to set an expected crops value
// expect(contentData.values[0].value.crops).toEqual([]);
expect(contentData.values[0].value.focalPoint).toEqual(defaultFocalPoint);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test.beforeEach(async ({umbracoApi, umbracoUi}) => {
});

test.afterEach(async ({umbracoApi}) => {
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.document.ensureNameNotExists(contentName);
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ test('can add a culture', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
await umbracoUi.content.clickSaveModalButton();

// Assert
await umbracoUi.waitForTimeout(2000);
const domainsData = await umbracoApi.document.getDomains(contentId);
expect(domainsData.defaultIsoCode).toEqual(isoCode);
});
Expand Down
Loading

0 comments on commit 01f76a8

Please sign in to comment.