Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bb946a5
MetaData Config & Render calls configured
robearlam Nov 28, 2024
3c023f6
Configured metadata render method to correctly render page using new …
robearlam Dec 6, 2024
5a1d70d
Moved GQL logic out of LC.CLient and back into Pages logic. Introduce…
robearlam Dec 11, 2024
1c8752e
Wired up Placeholder and Rendering chromes
robearlam Dec 16, 2024
bd0dcde
Metadata editing now working
robearlam Jan 22, 2025
2b99fc6
Wired up EditingDictionaryQuery requests
robearlam Jan 29, 2025
5f34722
Fixed failing unit tests
robearlam Jan 31, 2025
5494e8e
Added UnitTests for EditingScriptsTagHelper
robearlam Feb 3, 2025
d007e5b
Added UnitTests for Pages GQL Factory, and RenderingEngine TagHelpers
robearlam Feb 4, 2025
b92f31d
ComponentName is now persisted in rendering collection, to be returne…
robearlam Feb 5, 2025
01a9228
Added UnitTests for Pages Config Middleware
robearlam Feb 5, 2025
5be1780
Added Unit Tests for Pages Setup Render Middleware call
robearlam Feb 5, 2025
4ea37ae
Added UniTests for GraphQLEditingServiceHandler
robearlam Feb 10, 2025
ddb0716
Added UniTests for EditingChromes being added to response for Pages r…
robearlam Feb 17, 2025
328561f
Restructured test for Pages config middleware
robearlam Feb 17, 2025
c2851cb
Fixed bug with ImageTagHelper and ViewCompponent
robearlam Mar 4, 2025
4d1c1dd
Moved PagesSetupMiddleware in a Controller, other small fixes
robearlam Mar 14, 2025
9e5608e
Moved log messages into Resources RESX
robearlam Mar 14, 2025
fb2f772
Added Resources RESX to Pages project
robearlam Mar 14, 2025
d0938ba
Refactored PlaceHolder processing to use Stack approach instead of re…
robearlam Mar 14, 2025
d380f8e
Improved logic controlling when Pages middleware executes
robearlam Mar 16, 2025
4570f44
Moved Dictionary functionality out of PagesGQLHandler into dedicated …
robearlam Mar 25, 2025
fa5202e
Removed GraphQlClient used for EditingHandler, added GraphQLClient wi…
robearlam Mar 26, 2025
f8eee28
Changed to no longer persist component names for predicate registrati…
robearlam Mar 26, 2025
ac8a933
Code TidyUp, fixed all warnings
robearlam Mar 26, 2025
1af1068
Removed ID property from EdtiableField as didnt need to be added
robearlam Mar 26, 2025
af13261
Created Integration tests for PagesSetupController
robearlam Mar 28, 2025
8109f8b
Added Integration test for succesful call to get editing layout with …
robearlam Apr 1, 2025
0a08769
Added csproj missing from previous commit
robearlam Apr 1, 2025
17e0b09
Improved logic for when to enable editing of Fields in MetaData editi…
robearlam Apr 1, 2025
21f0e77
Added handling for /config route OPTIONS request
robearlam Apr 7, 2025
4371c24
Fixed correct component naming for ViewComponents and PartialViews
robearlam Apr 7, 2025
17e6f29
Aligned empty placeholder classname with jss
robearlam Apr 22, 2025
8fd259f
Fixed issue with rendering of empty nested placeholders due to incorr…
robearlam Apr 22, 2025
0bc23b7
Style refactor
IvanLieckens May 26, 2025
64cb3d7
Fixed incorrect error being thrown from Config call
robearlam May 26, 2025
8cfecbf
Changes from PR Review
robearlam May 27, 2025
7e2b893
Moved MetaData Queries back from constants to improve readability
robearlam May 27, 2025
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
14 changes: 14 additions & 0 deletions Sitecore.AspNetCore.SDK.sln
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM
.github\ISSUE_TEMPLATE\Question.yml = .github\ISSUE_TEMPLATE\Question.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sitecore.AspNetCore.SDK.Pages", "src\Sitecore.AspNetCore.SDK.Pages\Sitecore.AspNetCore.SDK.Pages.csproj", "{F33DC6F7-83F8-41CE-852C-8279D1266139}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sitecore.AspNetCore.SDK.Pages.Tests", "tests\Sitecore.AspNetCore.SDK.Pages.Tests\Sitecore.AspNetCore.SDK.Pages.Tests.csproj", "{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -186,6 +190,14 @@ Global
{100C07C6-C68D-469F-9F15-139CB48CB7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{100C07C6-C68D-469F-9F15-139CB48CB7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{100C07C6-C68D-469F-9F15-139CB48CB7F0}.Release|Any CPU.Build.0 = Release|Any CPU
{F33DC6F7-83F8-41CE-852C-8279D1266139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F33DC6F7-83F8-41CE-852C-8279D1266139}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F33DC6F7-83F8-41CE-852C-8279D1266139}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F33DC6F7-83F8-41CE-852C-8279D1266139}.Release|Any CPU.Build.0 = Release|Any CPU
{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -221,6 +233,8 @@ Global
{100C07C6-C68D-469F-9F15-139CB48CB7F0} = {BDE3D3B9-8291-4AE9-B8DA-868CEBCBDC4D}
{1706E43D-AC19-4FBB-9BFB-18A8B195580A} = {5FE82369-DEF2-4136-B74F-6E86DB91050E}
{24CCC156-046B-4600-9DB0-FC3269A18747} = {5FE82369-DEF2-4136-B74F-6E86DB91050E}
{F33DC6F7-83F8-41CE-852C-8279D1266139} = {75482B5D-21E2-4DBE-BE78-657ECF0D409F}
{55601B5C-5D9C-66E5-801D-E5D5EA0E29D6} = {BDE3D3B9-8291-4AE9-B8DA-868CEBCBDC4D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2E4F7126-B772-42CB-8F90-93B221ED0A72}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace Sitecore.AspNetCore.SDK.LayoutService.Client.Response.Model;

/// <summary>
/// Class used to define an items datasource information.
/// </summary>
public class DataSource
{
/// <summary>
/// Gets or sets the Id.
/// </summary>
public string Id { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Language.
/// </summary>
public string Language { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Revision.
/// </summary>
public string Revision { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Version.
/// </summary>
public int Version { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,22 @@ public class EditableField<TValue>
[DataMember(Name = "editable")]
[JsonPropertyName("editable")]
public string EditableMarkup { get; set; } = string.Empty;

/// <summary>
/// Gets or Sets the id of the Field.
/// </summary>
[DataMember(Name = "Id")]
[JsonPropertyName("Id")]
public string Id { get; set; } = string.Empty;

/// <inheritdoc />
public EditableChrome? OpeningChrome { get; set; }

/// <inheritdoc />
public EditableChrome? ClosingChrome { get; set; }

/// <summary>
/// Gets or Sets the MetaSata for the Field.
/// </summary>
public MetaData? MetaData { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,15 @@ public interface IEditableField : IField
/// Gets or sets the HTML markup for this <see cref="IField"/> when editing.
/// </summary>
public string EditableMarkup { get; set; }

/// <summary>
/// Gets or sets the EditableChrome used to render the opening chrome for this field.
/// </summary>
public EditableChrome? OpeningChrome { get; set; }

/// <summary>
/// Gets or sets the EditableChrome used to render the closing chrome for this field.
/// </summary>

public EditableChrome? ClosingChrome { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Sitecore.AspNetCore.SDK.LayoutService.Client.Response.Model;

/// <summary>
/// Class used to define an items metadata information.
/// </summary>
public class MetaData
{
/// <summary>
/// Gets or sets the title.
/// </summary>
public string Title { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the FieldId.
/// </summary>
public string FieldId { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the FieldType.
/// </summary>
public string FieldType { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the RawValue.
/// </summary>
public string RawValue { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the DataSource.
/// </summary>
public DataSource? DataSource { get; set; }

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Sitecore.AspNetCore.SDK.LayoutService.Client.Response.Model;
/// that contains a value that can be edited using wrapped HTML markup.
/// </summary>
/// <typeparam name="TValue">The value type.</typeparam>
public class WrappedEditableField<TValue> : Field<TValue>, IWrappedEditableField
public class WrappedEditableField<TValue> : EditableField<TValue>, IWrappedEditableField
{
/// <inheritdoc />
[DataMember(Name = "editableFirstPart")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Sitecore.AspNetCore.SDK.Pages.Configuration;

/// <summary>
/// Marker service used to identify when Pages services have been registered.
/// </summary>
internal class PagesMarkerService;
32 changes: 32 additions & 0 deletions src/Sitecore.AspNetCore.SDK.Pages/Configuration/PagesOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Sitecore.AspNetCore.SDK.Pages.Configuration;

/// <summary>
/// The options to configure the Pages middleware.
/// </summary>
public class PagesOptions
{
/// <summary>
/// Gets or sets the config endpoint for Pages MetaData mode.
/// </summary>
public string? ConfigEndpoint { get; set; } = "/api/editing/config";

/// <summary>
/// Gets or sets the render endpoint for Pages MetaData mode.
/// </summary>
public string? RenderEndpoint { get; set; } = "/api/editing/render";

/// <summary>
/// Gets or sets the valid editing origin for all editing requests.
/// </summary>
public string? ValidEditingOrigin { get; set; } = "https://pages.sitecorecloud.io";

/// <summary>
/// Gets or sets the valid origins for the head to run under.
/// </summary>
public string? ValidOrigins { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Editing Secret.
/// </summary>
public string? EditingSecret { get; set; } = string.Empty;
}
30 changes: 30 additions & 0 deletions src/Sitecore.AspNetCore.SDK.Pages/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Sitecore.AspNetCore.SDK.Pages
{
/// <summary>
/// Class used to stored constants referenced throughout the Pages project.
/// </summary>
public static class Constants
{
/// <summary>
/// Class used to hold the names of different Layout Clients used by the Pages project.
/// </summary>
public static class LayoutClients
{
/// <summary>
/// Name of the Pages Editing Layout Client.
/// </summary>
public const string Pages = "pages";
}

/// <summary>
/// Class used to hold the names of the different tag helpers defined in the Pages project.
/// </summary>
public static class SitecoreTagHelpers
{
/// <summary>
/// The HTML tag used to render the Editing Scripts tag helper.
/// </summary>
public const string EditScriptsHtmlTag = "sc-editingscripts";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Sitecore.AspNetCore.SDK.LayoutService.Client.Extensions;
using Sitecore.AspNetCore.SDK.LayoutService.Client.Interfaces;
using Sitecore.AspNetCore.SDK.LayoutService.Client.Request;
using Sitecore.AspNetCore.SDK.LayoutService.Client.Serialization;
using Sitecore.AspNetCore.SDK.Pages.Configuration;
using Sitecore.AspNetCore.SDK.Pages.GraphQL;
using Sitecore.AspNetCore.SDK.Pages.Middleware;
using Sitecore.AspNetCore.SDK.Pages.Request.Handlers.GraphQL;
using Sitecore.AspNetCore.SDK.RenderingEngine.Configuration;
using Sitecore.AspNetCore.SDK.RenderingEngine.Extensions;
using Sitecore.AspNetCore.SDK.RenderingEngine.Interfaces;

namespace Sitecore.AspNetCore.SDK.Pages.Extensions;

/// <summary>
/// Configuration helpers for Pages functionality.
/// </summary>
public static class PagesAppConfigurationExtensions
{
/// <summary>
/// Registers the Sitecore Experience Editor middleware into the <see cref="IApplicationBuilder"/>.
/// </summary>
/// <param name="app">The instance of the <see cref="IApplicationBuilder"/> to extend.</param>
/// <returns>The <see cref="IApplicationBuilder"/> so that additional calls can be chained.</returns>
public static IApplicationBuilder UseSitecorePages(this IApplicationBuilder app)
{
ArgumentNullException.ThrowIfNull(app);

object? experienceEditorMarker = app.ApplicationServices.GetService(typeof(PagesMarkerService));
if (experienceEditorMarker != null)
{
app.UseMiddleware<PageSetupMiddleware>();
app.UseMiddleware<PagesRenderMiddleware>();
}

return app;
}

/// <summary>
/// Adds the Sitecore Experience Editor support services to the <see cref="IServiceCollection" />.
/// </summary>
/// <param name="serviceBuilder">The <see cref="ISitecoreRenderingEngineBuilder" /> to add services to.</param>
/// <param name="contextId">The ContextId for the environment being used.</param>
/// <param name="options">Configures the <see cref="PagesOptions" /> options.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static ISitecoreRenderingEngineBuilder WithSitecorePages(this ISitecoreRenderingEngineBuilder serviceBuilder, string contextId, Action<PagesOptions>? options = null)
{
ArgumentNullException.ThrowIfNull(serviceBuilder);

IServiceCollection services = serviceBuilder.Services;
if (services.Any(s => s.ServiceType == typeof(PagesMarkerService)))
{
return serviceBuilder;
}

services.AddSingleton<PagesMarkerService>();
services.AddSingleton<IGraphQLClientFactory>(new GraphQLClientFactory(contextId));

if (options != null)
{
services.Configure(options);
}

services.Configure((Action<RenderingEngineOptions>)(renderingOptions =>
{
renderingOptions.MapToRequest((httpRequest, layoutRequest) =>
{
MapRequest(httpRequest, layoutRequest, "mode");
MapRequest(httpRequest, layoutRequest, "sc_itemid");
MapRequest(httpRequest, layoutRequest, "sc_version");
MapRequest(httpRequest, layoutRequest, "sc_lang");
MapRequest(httpRequest, layoutRequest, "sc_site");
MapRequest(httpRequest, layoutRequest, "sc_layoutKind");
MapRequest(httpRequest, layoutRequest, "secret");
MapRequest(httpRequest, layoutRequest, "tenant_id");
MapRequest(httpRequest, layoutRequest, "route");
Comment thread
robearlam marked this conversation as resolved.
Outdated
});
}));

return serviceBuilder;
}

/// <summary>
/// Registers an HTTP request handler for the Sitecore layout service client.
/// </summary>
/// <param name="builder">The <see cref="ISitecoreLayoutClientBuilder"/> to configure.</param>
/// <returns>The <see cref="ILayoutRequestHandlerBuilder{HttpLayoutRequestHandler}"/> so that additional calls can be chained.</returns>

public static ISitecoreLayoutClientBuilder AddSitecorePagesHandler(
this ISitecoreLayoutClientBuilder builder)
{
string name = Constants.LayoutClients.Pages;
builder.AddHandler(name, sp
=> ActivatorUtilities.CreateInstance<GraphQLEditingServiceHandler>(
sp,
sp.GetRequiredService<IGraphQLClientFactory>(),
sp.GetRequiredService<ISitecoreLayoutSerializer>(),
sp.GetRequiredService<ILogger<GraphQLEditingServiceHandler>>()));

return builder;
}

private static void MapRequest(HttpRequest httpRequest, SitecoreLayoutRequest layoutRequest, string paramName)
{
if (httpRequest.Query == null || !httpRequest.Query.ContainsKey(paramName))
{
return;
}

string[]? modeQueryValue = httpRequest.Query[paramName];
if (modeQueryValue == null)
{
return;
}

layoutRequest.AddHeader(paramName, modeQueryValue);
}
}
34 changes: 34 additions & 0 deletions src/Sitecore.AspNetCore.SDK.Pages/GraphQL/GraphQLClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using GraphQL.Client.Abstractions;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.SystemTextJson;
using Sitecore.AspNetCore.SDK.GraphQL.Extensions;

namespace Sitecore.AspNetCore.SDK.Pages.GraphQL;

/// <summary>
/// GraphQLClientFactory used to generate instances of of GraphQLClients authenticated using a ContextId.
/// <param name="contextId">The contextId for the envionment being used.</param>
/// </summary>
public class GraphQLClientFactory(string contextId)
: IGraphQLClientFactory
{
private readonly string contextId = contextId ?? throw new ArgumentNullException(nameof(contextId));

/// <inheritdoc />
public IGraphQLClient GenerateClient(Uri? uri, string layoutKind, bool editMode)
{
uri ??= new Uri("https://edge-platform.sitecorecloud.io/v1/content/api/graphql/v1");
uri = uri.AddQueryString("sitecoreContextId", contextId)!;

GraphQLHttpClient client = new(uri, new SystemTextJsonSerializer());
client.HttpClient.DefaultRequestHeaders.Add("sc_layoutKind", layoutKind);
client.HttpClient.DefaultRequestHeaders.Add("sc_editmode", editMode.ToString());
return client;
}

/// <inheritdoc />
public IGraphQLClient GenerateClient(string layoutKind, bool editMode)
{
return GenerateClient(null, layoutKind, editMode);
}
}
Loading