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-795: Upgrade to latest OC preview to test System.Text.Json #63

Merged
merged 13 commits into from
May 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.8.2" />
<PackageReference Include="OrchardCore.Contents" Version="1.8.2" />
<PackageReference Include="OrchardCore.Module.Targets" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.Contents" Version="2.0.0-preview-18200" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Atata;
using Lombiq.Tests.UI.Extensions;
using Lombiq.Tests.UI.Services;
using OpenQA.Selenium;
using Shouldly;
using System.Threading.Tasks;

namespace Lombiq.ContentEditors.Tests.UI.Extensions;
Expand All @@ -12,7 +14,7 @@ public static Task EnableContentEditorsSamplesFeatureAsync(this UITestContext co

public static async Task<UITestContext> TestDemoContentItemAsyncEditorAsync(this UITestContext context)
{
await context.GoToRelativeUrlAsync("/Admin/ContentItemAsyncEditor/EmployeeAsyncEditorProvider/Employee");
await context.GoToAdminRelativeUrlAsync("/ContentItemAsyncEditor/EmployeeAsyncEditorProvider/Employee");

context.Exists(By.XPath("//label[. = 'Name']"));
context.Exists(By.XPath("//*[contains(@class, 'asyncEditor__groupLink') and contains(., 'Personal Details')]"));
Expand All @@ -24,9 +26,11 @@ public static async Task<UITestContext> TestDemoContentItemAsyncEditorAsync(this
await context.FillInWithRetriesAsync(By.Id("AsyncEditorEmployeePart_Position_Text"), "CEO");
await context.FillInWithRetriesAsync(By.Id("AsyncEditorEmployeePart_Office_Text"), "Budapest");
await context.ClickReliablyOnAsync(By.ClassName("asyncEditor__submitAction"));
context.Exists(
By.XPath(
"//*[contains(@class, 'asyncEditor__message') and contains(., 'Editor has been successfully submitted.')]"));

context.Exists(By.ClassName("asyncEditor__messages"));
var errorMessage = context.Get(By.ClassName("asyncEditor__error").Safely());
errorMessage?.Text?.ShouldBeNullOrWhiteSpace(errorMessage.GetAttribute("data-error-json"));
context.Get(By.ClassName("asyncEditor__message")).Text.Trim().ShouldBe("Editor has been successfully submitted.");

return context;
}
Expand Down
46 changes: 29 additions & 17 deletions Lombiq.ContentEditors/Assets/Scripts/async-editor/async-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,42 @@ class AsyncEditorApiClient {
this.contentType = parameters.contentType;
}

async fetchEditor(callback, contentId, editorGroup, nextEditorGroup, requestOptions, raiseEvent) {
try {
const response = await fetch(this.createUrl(contentId, editorGroup, nextEditorGroup), requestOptions);
const data = await response.json();
const success = data.type !== 'Error';

callback(success, data);
if (!success) return;

if (raiseEvent) {
const submittedEditorEvent = new CustomEvent('asyncEditorSubmittedEditor', {
bubbles: true,
cancelable: true,
detail: { asyncEditor: window.asyncEditor },
});
document.dispatchEvent(submittedEditorEvent);
}
}
catch (error) {
callback(false, error);
}
}

loadEditor(contentId, editorGroup, callback) {
return fetch(this.createUrl(contentId, editorGroup))
.then((response) => response.json())
.then((data) => callback(true, data))
.catch((error) => callback(false, error));
return this.fetchEditor(callback, contentId, editorGroup);
}

submitEditor(contentId, editorGroup, nextEditorGroup, formData, callback) {
return fetch(this.createUrl(contentId, editorGroup, nextEditorGroup), {
const requestOptions = {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
body: new URLSearchParams(formData),
})
.then((response) => response.json())
.then((data) => callback(true, data))
.then(() => {
const submittedEditorEvent = new CustomEvent('asyncEditorSubmittedEditor', {
bubbles: true,
cancelable: true,
detail: { asyncEditor: window.asyncEditor },
});
document.dispatchEvent(submittedEditorEvent);
})
.catch((error) => callback(false, error));
};
return this.fetchEditor(callback, contentId, editorGroup, nextEditorGroup, requestOptions, true);
}

createUrl(contentId, editorGroup, nextEditorGroup) {
Expand Down Expand Up @@ -67,6 +77,7 @@ window.asyncEditor.editor = {
api: null,
message: '',
errorText: '',
errorJson: '',
contentId: '',
editorHtml: '',
validationSummaryHtml: '',
Expand Down Expand Up @@ -166,6 +177,7 @@ window.asyncEditor.editor = {
if (shouldUpdateQuery) self.updateQuery();
}
else {
self.errorJson = JSON.stringify({ error: data, string: data.toString() });
self.errorText = self.defaultErrorText;
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Lombiq.ContentEditors.Constants;
using Lombiq.ContentEditors.Models;
using Lombiq.ContentEditors.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using OrchardCore.ContentManagement;
using OrchardCore.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -44,32 +46,42 @@ public async Task<ActionResult<RenderedAsyncEditorGroupRequest>> Get([FromQuery]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Post([FromQuery] SubmitAsyncEditorRequest request)
{
var provider = GetProvider(request.ProviderName);
if (provider == null) return NotFound();
try
{
var provider = GetProvider(request.ProviderName);
if (provider == null) return Fail($"Couldn't get the provider \"{request.ProviderName}\".");

var item = await _contentManager.GetOrCreateAsync(request.ContentId, request.ContentType, VersionOptions.Latest);
if (item == null) return NotFound();
var item = await _contentManager.GetOrCreateAsync(request.ContentId, request.ContentType, VersionOptions.Latest);
if (item == null) return Fail($"Couldn't find the content item \"{request.ContentId}\".");

var context = PopulateContext(request, item);
if (!await provider.CanRenderEditorGroupAsync(context)) return NotFound();
var context = PopulateContext(request, item);
if (!await provider.CanRenderEditorGroupAsync(context))
{
return Fail($"The editor group of provider \"{request.ProviderName}\" can't be rendered.");
}

var result = await provider.UpdateEditorAsync(context);
if (!result.ModelState.IsValid ||
string.IsNullOrEmpty(request.NextEditorGroup) ||
request.NextEditorGroup == request.EditorGroup)
{
var result = await provider.UpdateEditorAsync(context);
if (!result.ModelState.IsValid ||
string.IsNullOrEmpty(request.NextEditorGroup) ||
request.NextEditorGroup == request.EditorGroup)
{
return await AsyncEditorResultAsync(
context,
provider,
renderedEditor: await result.RenderedEditorShapeFactory(),
message: result.Message);
}

var nextEditorContext = PopulateContext(request, item, request.NextEditorGroup);
return await AsyncEditorResultAsync(
context,
!await provider.CanRenderEditorGroupAsync(nextEditorContext) ? context : nextEditorContext,
provider,
renderedEditor: await result.RenderedEditorShapeFactory(),
message: result.Message);
}

var nextEditorContext = PopulateContext(request, item, request.NextEditorGroup);
return await AsyncEditorResultAsync(
!await provider.CanRenderEditorGroupAsync(nextEditorContext) ? context : nextEditorContext,
provider,
message: result.Message);
catch (Exception exception)
{
return Fail(exception);
}
}

private IAsyncEditorProvider<ContentItem> GetProvider(string name) =>
Expand Down Expand Up @@ -114,4 +126,14 @@ private async Task<ViewResult> AsyncEditorResultAsync(
Message = message,
});
}

/// <summary>
/// Provides serialized JSON information during local development, but ony a generic error message in production.
/// It's useful to always return JSON, regardless whether the request succeeded or failed.
/// </summary>
private JsonResult Fail(object data) => Json(new
{
Type = "Error",
Content = HttpContext.IsDevelopmentAndLocalhost() ? data : "Something went wrong.",
});
}
14 changes: 7 additions & 7 deletions Lombiq.ContentEditors/Lombiq.ContentEditors.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.8.2" />
<PackageReference Include="OrchardCore.ContentFields" Version="1.8.2" />
<PackageReference Include="OrchardCore.ContentManagement" Version="1.8.2" />
<PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="1.8.2" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.8.2" />
<PackageReference Include="OrchardCore.Media" Version="1.8.2" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.8.2" />
<PackageReference Include="OrchardCore.Module.Targets" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.ContentFields" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.ContentManagement" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.Media" Version="2.0.0-preview-18200" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="2.0.0-preview-18200" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public virtual async Task<AsyncEditorUpdateResult> UpdateEditorAsync(AsyncEditor

await _contentManager.CreateOrUpdateAsync(context.Content);

if (!(await DescribeEditorGroupsAsync()).First(group => @group.Name == context.EditorGroup).IsPublishGroup)
if (!(await DescribeEditorGroupsAsync()).First(group => group.Name == context.EditorGroup).IsPublishGroup)
{
return CreateUpdateResult(editorShape, _updateModelAccessor.ModelUpdater.ModelState);
}
Expand Down Expand Up @@ -144,9 +144,9 @@ protected virtual AsyncEditorGroupDescriptor<ContentItem> DescribeEditorGroup(
DisplayText = displayText,
IsPublishGroup = isPublishGroup,
IsAccessibleAsync = isAccessibleFactory ??
(async (context) => await CanRenderEditorGroupAsync(context, name)),
(async context => await CanRenderEditorGroupAsync(context, name)),
IsFilledAsync = isFilledFactory ??
(async (context) => await IsEditorGroupFilledAsync(context, name)),
(async context => await IsEditorGroupFilledAsync(context, name)),
};

protected virtual void AddEditorShapeAlternates(AsyncEditorContext<ContentItem> context, IShape editorShape)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<div class="asyncEditor__progress progress-bar" role="progressbar" :style="'width: ' + progress + '%'" :aria-valuenow="progress" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="asyncEditor__messages">
<div class="asyncEditor__error p-3 rounded bg-danger text-light mb-3" v-if="errorText">{{ errorText }}</div>
<div class="asyncEditor__message p-3 rounded bg-success text-light mb-3" v-if="message">{{ message }}</div>
<div class="asyncEditor__error p-3 rounded bg-danger text-light mb-3" v-if="errorText" :data-error-json="errorJson">{{ errorText }}</div>
<div class="asyncEditor__message p-3 rounded bg-success text-light mb-3" v-else-if="message">{{ message }}</div>
<div class="asyncEditor__validationSummary p-3 rounded bg-danger text-light mb-3" v-if="validationSummaryHtml" v-html="validationSummaryHtml"></div>
</div>
</div>
Expand Down