Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSOE-818: Upgrade to Orchard Core 2.0 #67

Merged
merged 71 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
bd4d3a8
Update NuGet versions to pre-release.
sarahelsaig Feb 22, 2024
b65a2ed
Merge remote-tracking branch 'origin/dev' into issue/OSOE-795
sarahelsaig Feb 22, 2024
23120d2
Fix compilation errors.
sarahelsaig Feb 23, 2024
0e09cc0
Remove "using Newtonsoft.Json"
sarahelsaig Feb 24, 2024
4f0d042
Fix compilation errors.
sarahelsaig Feb 24, 2024
81086dd
Remove remaining Newtonsoft referenes.
sarahelsaig Feb 24, 2024
fc86fd8
unusing
sarahelsaig Feb 25, 2024
a27c336
Update OC to latest (because of bug fix for WorkflowTypeStep)
sarahelsaig Mar 3, 2024
0f19df1
Update OC preview version.
sarahelsaig Mar 7, 2024
cbc63b1
Update OC package
sarahelsaig Mar 13, 2024
c068c37
Replace AdminRouteAttribute with AdminAttribute.
sarahelsaig Mar 18, 2024
7b0bd5a
Use JObject.FromObject instead of JsonSerializer.SerializeToNode wher…
sarahelsaig Apr 2, 2024
5d86e62
Update OC preview version.
sarahelsaig Apr 23, 2024
346271e
Bring over the deleted API POST method.
sarahelsaig Apr 23, 2024
d611f81
unusing
sarahelsaig Apr 23, 2024
8f1623f
Update OC package version.
sarahelsaig Apr 27, 2024
6300c3a
Update OC versions
sarahelsaig May 4, 2024
dff4b43
Update OC to the latest preview.
sarahelsaig May 7, 2024
00dd65e
Remove strange leftover `var`.
sarahelsaig May 10, 2024
98a4d9d
Update Lombiq.JsonEditor/Controllers/AdminController.cs
sarahelsaig May 12, 2024
4a93107
Update Lombiq.JsonEditor/Models/JsonEditorOptions.cs
sarahelsaig May 12, 2024
1741997
Merge pull request #59 from Lombiq/issue/OSOE-795
dministro May 15, 2024
d476199
Merge remote-tracking branch 'origin/dev' into task/system-text-json-…
sarahelsaig May 28, 2024
8612482
Update OC preview version.
sarahelsaig May 28, 2024
3eaeddc
Update OC preview version.
sarahelsaig May 30, 2024
fc17d30
Update OC preview version.
sarahelsaig Jun 5, 2024
0dd3850
Merge remote-tracking branch 'origin/dev' into task/system-text-json-…
sarahelsaig Jun 14, 2024
9757e72
Merge branch 'task/system-text-json-migration' into issue/OCC-245
sarahelsaig Jun 14, 2024
10233cd
Update OC version.
sarahelsaig Jun 18, 2024
47442c6
Update OC version.
sarahelsaig Jun 19, 2024
5bda5ed
Update OC preview version.
sarahelsaig Jun 21, 2024
a52d3f2
Update OC preview version.
sarahelsaig Jul 11, 2024
89f47c2
Fix package consolidation.
sarahelsaig Jul 11, 2024
f2c6f7e
Update HL version.
sarahelsaig Jul 13, 2024
c4cd9c2
Update UITT version.
sarahelsaig Jul 13, 2024
235ef73
Use CreateModelMaybeAsync.
sarahelsaig Jul 14, 2024
b5642be
Update HL nuget version.
sarahelsaig Jul 16, 2024
a2b3d78
Update all UITT Nuget versions.
sarahelsaig Jul 16, 2024
906937f
Update package version.
sarahelsaig Jul 16, 2024
0df3957
Update OC preview versions.
sarahelsaig Jul 24, 2024
ee7b78c
Update HL preview version.
sarahelsaig Jul 26, 2024
a8c0687
Update UITT preview version.
sarahelsaig Jul 26, 2024
ba17525
Update HL preview.
sarahelsaig Jul 27, 2024
d750474
Update UITT version.
sarahelsaig Jul 27, 2024
831d9e3
Update UITT preview version.
sarahelsaig Jul 27, 2024
3e18ca0
NuGet.config
sarahelsaig Jul 27, 2024
619d9f4
Update HL nuget version.
sarahelsaig Jul 27, 2024
599e197
Update HL.
sarahelsaig Jul 27, 2024
eb3de95
Merge pull request #63 from Lombiq/issue/OCC-245
Piedone Jul 28, 2024
98183ab
Update OC preview version.
sarahelsaig Jul 29, 2024
d221ff9
Update OC preview versions.
sarahelsaig Jul 30, 2024
197e437
Update OC preview version.
sarahelsaig Aug 6, 2024
f591fb6
Post-merge fixups.
sarahelsaig Aug 7, 2024
4c2b92c
Use CreateModelAsync where applicable.
sarahelsaig Aug 7, 2024
70bbdcb
Update HL alpha version.
sarahelsaig Aug 7, 2024
bb0129a
Update UITT alpha version.
sarahelsaig Aug 7, 2024
7d1f928
Update OC preview version.
sarahelsaig Aug 7, 2024
e94dc79
Update OC preview.
sarahelsaig Aug 8, 2024
2c4a698
Update new breaking changes.
sarahelsaig Aug 8, 2024
e24d926
Merge pull request #66 from Lombiq/issue/OSOE-893
sarahelsaig Aug 9, 2024
828c1c1
Update HL alpha version.
sarahelsaig Aug 9, 2024
bc2439b
Update UITT alpha version.
sarahelsaig Aug 9, 2024
ee8370d
Update OC preview version.
sarahelsaig Aug 20, 2024
dfba5ad
Update OC
sarahelsaig Aug 21, 2024
e800541
Update OC preview version.
sarahelsaig Aug 23, 2024
14c8a0e
Use OC release version.
sarahelsaig Sep 22, 2024
f69db25
Update HL preview.
sarahelsaig Sep 23, 2024
50249a2
Update HL.
sarahelsaig Sep 23, 2024
5c0fc72
Seal INavigationProvider, IAsyncResultFilter, IPermissionProvider, St…
sarahelsaig Sep 24, 2024
9fea0f3
Update HL NuGet version.
sarahelsaig Sep 25, 2024
cbd15b4
Update UITT version.
sarahelsaig Sep 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Atata;
using Lombiq.Tests.UI.Extensions;
using Lombiq.Tests.UI.Services;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using Shouldly;
using System.Text.Json.Nodes;
using System.Threading.Tasks;

namespace Lombiq.JsonEditor.Tests.UI.Extensions;
Expand Down Expand Up @@ -139,7 +139,7 @@ private static async Task TestTreeStyleModeAsync(this UITestContext context)
private static void TestCodeStyleMode(this UITestContext context)
{
// This field is hidden, but its content reflects what's in the editor.
var editorContent = JObject
var editorContent = JsonNode
.Parse(context.Get(By.XPath($"//input[@class='jsonEditor__input']").OfAnyVisibility())
.GetValue());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' == 'true'">
<PackageReference Include="Lombiq.Tests.UI" Version="10.0.1" />
<PackageReference Include="Lombiq.Tests.UI" Version="10.0.2-alpha.5.osoe-818" />
</ItemGroup>

<ItemGroup>
Expand Down
126 changes: 94 additions & 32 deletions Lombiq.JsonEditor/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
using AngleSharp.Common;
using Lombiq.HelpfulLibraries.OrchardCore.Contents;
using Lombiq.HelpfulLibraries.OrchardCore.DependencyInjection;
using Lombiq.HelpfulLibraries.OrchardCore.Mvc;
using Lombiq.HelpfulLibraries.OrchardCore.Validation;
using Lombiq.JsonEditor.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using OrchardCore.Admin;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Handlers;
using OrchardCore.ContentManagement.Metadata;
using OrchardCore.Contents;
using OrchardCore.Contents.Controllers;
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.Layout;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.DisplayManagement.Title;
using OrchardCore.Title.ViewModels;
using System;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Settings;
using System.Threading.Tasks;

namespace Lombiq.JsonEditor.Controllers;

public class AdminController : Controller
{
private static readonly JsonMergeSettings _updateJsonMergeSettings = new()
{
MergeArrayHandling = MergeArrayHandling.Replace,
};

private readonly IAuthorizationService _authorizationService;
private readonly IContentManager _contentManager;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly ILayoutAccessor _layoutAccessor;
private readonly INotifier _notifier;
private readonly IPageTitleBuilder _pageTitleBuilder;
private readonly IShapeFactory _shapeFactory;
private readonly Lazy<ApiController> _contentApiControllerLazy;
private readonly IStringLocalizer<AdminController> T;
private readonly IHtmlLocalizer<AdminController> H;

Expand All @@ -43,8 +50,7 @@ public AdminController(
INotifier notifier,
IPageTitleBuilder pageTitleBuilder,
IShapeFactory shapeFactory,
IOrchardServices<AdminController> services,
Lazy<ApiController> contentApiControllerLazy)
IOrchardServices<AdminController> services)
{
_authorizationService = services.AuthorizationService.Value;
_contentManager = services.ContentManager.Value;
Expand All @@ -53,12 +59,11 @@ public AdminController(
_notifier = notifier;
_pageTitleBuilder = pageTitleBuilder;
_shapeFactory = shapeFactory;
_contentApiControllerLazy = contentApiControllerLazy;
T = services.StringLocalizer.Value;
H = services.HtmlLocalizer.Value;
}

[AdminRoute("Contents/ContentItems/{contentItemId}/Edit/Json")]
[Admin("Contents/ContentItems/{contentItemId}/Edit/Json")]
public async Task<IActionResult> Edit(string contentItemId)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
Expand All @@ -80,7 +85,7 @@ await _contentManager.GetAsync(contentItemId, VersionOptions.Latest) is not { }
await _layoutAccessor.AddShapeToZoneAsync("Title", titleShape);

var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType);
return View(new EditContentItemViewModel(contentItem, definition, JsonConvert.SerializeObject(contentItem)));
return View(new EditContentItemViewModel(contentItem, definition, JsonSerializer.Serialize(contentItem)));
}

[ValidateAntiForgeryToken]
Expand All @@ -96,7 +101,7 @@ public async Task<IActionResult> EditPost(

if (string.IsNullOrWhiteSpace(contentItemId) ||
string.IsNullOrWhiteSpace(json) ||
JsonConvert.DeserializeObject<ContentItem>(json) is not { } contentItem)
JsonSerializer.Deserialize<ContentItem>(json) is not { } contentItem)
{
return NotFound();
}
Expand Down Expand Up @@ -136,34 +141,91 @@ public async Task<IActionResult> EditPost(
private Task<bool> CanEditAsync(ContentItem contentItem) =>
_authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem);

private async Task<IActionResult> UpdateContentAsync(ContentItem contentItem, bool isDraft)
private Task<IActionResult> UpdateContentAsync(ContentItem contentItem, bool isDraft) =>
PostContentAsync(contentItem, isDraft);

private static bool IsContinue(string submitString) =>
submitString?.EndsWithOrdinalIgnoreCase("AndContinue") == true;

private static string GetName(ContentItem contentItem) =>
string.IsNullOrWhiteSpace(contentItem.DisplayText)
? contentItem.ContentType
: $"\"{contentItem.DisplayText}\"";

// Based on the OrchardCore.Contents.Controllers.ApiController.Post action that was deleted in
// https://github.com/OrchardCMS/OrchardCore/commit/d524386b2f792f35773324ae482247e80a944266 to replace with minimal
// APIs that can't be reused the same way.
private async Task<IActionResult> PostContentAsync(ContentItem model, bool draft)
{
// The Content API Controller requires the AccessContentApi permission. As this isn't an external API request it
// doesn't make sense to require this permission. So we create a temporary claims principal and explicitly grant
// the permission.
var currentUser = User;
HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(User.Claims.Concat(Permissions.AccessContentApi)));
// It is really important to keep the proper method calls order with the ContentManager
// so that all event handlers gets triggered in the right sequence.

if (await _contentManager.GetAsync(model.ContentItemId, VersionOptions.DraftRequired) is { } contentItem)
{
if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem))
{
return this.ChallengeOrForbid("Api");
}

contentItem.Merge(model, _updateJsonMergeSettings);

await _contentManager.UpdateAsync(contentItem);
var result = await _contentManager.ValidateAsync(contentItem);
if (CheckContentValidationResult(result) is { } problem) return problem;
}
else
{
if (string.IsNullOrEmpty(model.ContentType) || await _contentDefinitionManager.GetTypeDefinitionAsync(model.ContentType) == null)
{
return BadRequest();
}

contentItem = await _contentManager.NewAsync(model.ContentType);
contentItem.Owner = User.FindFirstValue(ClaimTypes.NameIdentifier);

try
if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, contentItem))
{
return this.ChallengeOrForbid("Api");
}

contentItem.Merge(model);

var result = await _contentManager.UpdateValidateAndCreateAsync(contentItem, VersionOptions.Draft);
if (CheckContentValidationResult(result) is { } problem) return problem;
}

if (draft)
{
// Here the API controller is called directly. The behavior is the same as if we sent a POST request using an
// HTTP client (except the permission bypass above), but it's faster and more resource-efficient.
var contentApiController = _contentApiControllerLazy.Value;
contentApiController.ControllerContext.HttpContext = HttpContext;
return await contentApiController.Post(contentItem, isDraft);
await _contentManager.SaveDraftAsync(contentItem);
}
finally
else
{
// Ensure that the original claims principal is restored, just in case.
HttpContext.User = currentUser;
await _contentManager.PublishAsync(contentItem);
}

return Ok(contentItem);
}

private static bool IsContinue(string submitString) =>
submitString?.EndsWithOrdinalIgnoreCase("AndContinue") == true;
private ActionResult CheckContentValidationResult(ContentValidateResult result)
{
if (!result.Succeeded)
{
// Add the validation results to the ModelState to present the errors as part of the response.
result.AddValidationErrorsToModelState(ModelState);
}

private static string GetName(ContentItem contentItem) =>
string.IsNullOrWhiteSpace(contentItem.DisplayText)
? contentItem.ContentType
: $"\"{contentItem.DisplayText}\"";
// We check the model state after calling all handlers because they trigger WF content events so, even they are not
// intended to add model errors (only drivers), a WF content task may be executed inline and add some model errors.
if (!ModelState.IsValid)
{
return ValidationProblem(new ValidationProblemDetails(ModelState)
{
Title = T["One or more validation errors occurred."],
Detail = string.Join(", ", ModelState.Values.SelectMany(state => state.Errors.Select(error => error.ErrorMessage))),
Status = (int)HttpStatusCode.BadRequest,
});
}

return null;
}
}
28 changes: 6 additions & 22 deletions Lombiq.JsonEditor/Drivers/JsonFieldDisplayDriver.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using Lombiq.HelpfulLibraries.Common.Utilities;
using Lombiq.JsonEditor.Fields;
using Lombiq.JsonEditor.ViewModels;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OrchardCore.ContentManagement.Display.ContentDisplay;
using OrchardCore.ContentManagement.Display.Models;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using System.Threading.Tasks;

Expand Down Expand Up @@ -36,15 +35,13 @@ public override IDisplayResult Edit(JsonField field, BuildFieldEditorContext con
model.PartFieldDefinition = context.PartFieldDefinition;
});

public override async Task<IDisplayResult> UpdateAsync(JsonField field, IUpdateModel updater, UpdateFieldEditorContext context)
public override async Task<IDisplayResult> UpdateAsync(JsonField field, UpdateFieldEditorContext context)
{
var model = new EditJsonFieldViewModel();
var model = await context.CreateModelAsync<EditJsonFieldViewModel>(Prefix);

if (!await updater.TryUpdateModelAsync(model, Prefix)) return await EditAsync(field, context);

if (!TryParse(model.Value))
if (JsonHelpers.ValidateJsonIfNotNull(model.Value) == false)
{
updater.ModelState.AddModelError(Prefix, T["The input isn't a valid JSON entity."]);
context.Updater.ModelState.AddModelError(Prefix, T["The input isn't a valid JSON entity."]);
}
else
{
Expand All @@ -53,17 +50,4 @@ public override async Task<IDisplayResult> UpdateAsync(JsonField field, IUpdateM

return await EditAsync(field, context);
}

private static bool TryParse(string value)
{
try
{
JObject.Parse(value);
return true;
}
catch (JsonException)
{
return false;
}
}
}
14 changes: 7 additions & 7 deletions Lombiq.JsonEditor/Lombiq.JsonEditor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.8.0" />
<PackageReference Include="OrchardCore.Contents" Version="1.8.0" />
<PackageReference Include="OrchardCore.ContentManagement" Version="1.8.0" />
<PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="1.8.0" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.8.0" />
<PackageReference Include="OrchardCore.ContentFields" Version="1.8.0" />
<PackageReference Include="OrchardCore.Module.Targets" Version="2.0.0" />
<PackageReference Include="OrchardCore.Contents" Version="2.0.0" />
<PackageReference Include="OrchardCore.ContentManagement" Version="2.0.0" />
<PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="2.0.0" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="2.0.0" />
<PackageReference Include="OrchardCore.ContentFields" Version="2.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' != 'true'">
Expand All @@ -49,7 +49,7 @@
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' == 'true'">
<PackageReference Include="Lombiq.HelpfulLibraries.OrchardCore" Version="10.0.0" />
<PackageReference Include="Lombiq.HelpfulLibraries.OrchardCore" Version="10.0.1-alpha.8.osoe-818" />
<PackageReference Include="Lombiq.NodeJs.Extensions" Version="2.1.0" />
</ItemGroup>

Expand Down
5 changes: 3 additions & 2 deletions Lombiq.JsonEditor/Models/JsonEditorOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Microsoft.AspNetCore.Mvc.Localization;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;

namespace Lombiq.JsonEditor.Models;

Expand All @@ -29,7 +29,8 @@ public class JsonEditorOptions
/// </summary>
public bool History { get; set; } = true;

[JsonProperty("mode")]
[JsonPropertyName("mode")]
[JsonInclude]
private string ModeString { get; set; } = "tree";

/// <summary>
Expand Down
6 changes: 5 additions & 1 deletion Lombiq.JsonEditor/Settings/JsonFieldSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Lombiq.HelpfulLibraries.Common.Utilities;

namespace Lombiq.JsonEditor.Settings;

public class JsonFieldSettings
public class JsonFieldSettings : ICopier<JsonFieldSettings>
{
public string JsonEditorOptions { get; set; }

public void CopyTo(JsonFieldSettings target) => target.JsonEditorOptions = JsonEditorOptions;
}
18 changes: 10 additions & 8 deletions Lombiq.JsonEditor/Settings/JsonFieldSettingsDriver.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using Lombiq.JsonEditor.Fields;
using Lombiq.JsonEditor.Models;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using OrchardCore.ContentManagement.Metadata.Builders;
using OrchardCore.ContentManagement.Metadata.Models;
using OrchardCore.ContentTypes.Editors;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;

namespace Lombiq.JsonEditor.Settings;
Expand All @@ -15,20 +18,19 @@ public class JsonFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver<J

public JsonFieldSettingsDriver(IStringLocalizer<JsonFieldSettingsDriver> stringLocalizer) => T = stringLocalizer;

public override IDisplayResult Edit(ContentPartFieldDefinition model) =>
Initialize<JsonFieldSettings>($"{nameof(JsonFieldSettings)}_Edit", model.PopulateSettings)
.Location("Content");
public override IDisplayResult Edit(ContentPartFieldDefinition model, BuildEditorContext context) =>
Initialize<JsonFieldSettings>($"{nameof(JsonFieldSettings)}_Edit", model.CopySettingsTo)
.PlaceInContent();

public override async Task<IDisplayResult> UpdateAsync(
ContentPartFieldDefinition model,
UpdatePartFieldEditorContext context)
{
var settings = new JsonFieldSettings();
await context.Updater.TryUpdateModelAsync(settings, Prefix);
var settings = await context.CreateModelAsync<JsonFieldSettings>(Prefix);

try
{
JsonConvert.DeserializeObject<JsonEditorOptions>(settings.JsonEditorOptions);
JsonNode.Parse(settings.JsonEditorOptions).ToObject<JsonEditorOptions>();
context.Builder.WithSettings(settings);
}
catch (JsonException)
Expand All @@ -38,6 +40,6 @@ public override async Task<IDisplayResult> UpdateAsync(
T["The input isn't a valid {0} object.", nameof(JsonEditorOptions)]);
}

return Edit(model);
return await EditAsync(model, context);
}
}
Loading