Skip to content

Commit 8064718

Browse files
Allow pages to add custom stylesheets and scripts
1 parent 4431f42 commit 8064718

38 files changed

+399
-104
lines changed

API/Content/IPageAdditionBuilder.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace GenHTTP.Api.Content
2+
{
3+
4+
public interface IPageAdditionBuilder<out T>
5+
{
6+
7+
T AddScript(string path, bool asynchronous = false);
8+
9+
T AddStyle(string path);
10+
11+
}
12+
13+
}

API/Content/PageAdditionBuilder.cs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using GenHTTP.Api.Infrastructure;
2+
3+
namespace GenHTTP.Api.Content
4+
{
5+
6+
public class PageAdditionBuilder : IBuilder<PageAdditions?>, IPageAdditionBuilder<PageAdditionBuilder>
7+
{
8+
private PageAdditions? _Additions;
9+
10+
public PageAdditionBuilder AddScript(string path, bool asynchronous = false)
11+
{
12+
EnsureAdditions().Scripts.Add(new(path, asynchronous));
13+
return this;
14+
}
15+
16+
public PageAdditionBuilder AddStyle(string path)
17+
{
18+
EnsureAdditions().Styles.Add(new(path));
19+
return this;
20+
}
21+
22+
private PageAdditions EnsureAdditions() => _Additions ?? (_Additions = PageAdditions.Create());
23+
24+
public PageAdditions? Build() => _Additions;
25+
26+
}
27+
28+
}

API/Content/PageAdditions.cs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Collections.Generic;
2+
3+
using GenHTTP.Api.Content.Websites;
4+
5+
namespace GenHTTP.Api.Content
6+
{
7+
8+
public record PageAdditions
9+
(
10+
11+
List<StyleReference> Styles,
12+
13+
List<ScriptReference> Scripts
14+
15+
)
16+
{
17+
18+
public static PageAdditions Create() => new(new(), new());
19+
20+
}
21+
22+
}

API/Content/Templating/TemplateModel.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Threading.Tasks;
2-
32
using GenHTTP.Api.Protocol;
3+
using static System.Net.WebRequestMethods;
44

55
namespace GenHTTP.Api.Content.Templating
66
{
@@ -19,6 +19,12 @@ public sealed class TemplateModel : AbstractModel
1919
/// </summary>
2020
public ContentInfo Meta { get; }
2121

22+
/// <summary>
23+
/// Additional references to styles or scripts to be included
24+
/// when rendering the template.
25+
/// </summary>
26+
public PageAdditions? Additions { get; }
27+
2228
/// <summary>
2329
/// The HTML content to be rendered within the template.
2430
/// </summary>
@@ -34,11 +40,14 @@ public sealed class TemplateModel : AbstractModel
3440
/// <param name="request">The request which caused this call</param>
3541
/// <param name="handler">The handler responsible to render the response</param>
3642
/// <param name="pageInfo">Information about the page to be rendered</param>
43+
/// <param name="additions">Additional references to required scripts or styles</param>
3744
/// <param name="content">The content to be rendered within the template</param>
38-
public TemplateModel(IRequest request, IHandler handler, ContentInfo pageInfo, string content) : base(request, handler)
45+
public TemplateModel(IRequest request, IHandler handler, ContentInfo pageInfo, PageAdditions? additions, string content) : base(request, handler)
3946
{
4047
Content = content;
4148

49+
Additions = additions;
50+
4251
Meta = pageInfo;
4352

4453
if (string.IsNullOrEmpty(Meta.Title))

Engine/Infrastructure/CoreRouter.cs

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Text;
45
using System.Threading.Tasks;
56

67
using GenHTTP.Api.Content;
@@ -9,6 +10,7 @@
910

1011
using GenHTTP.Modules.IO;
1112
using GenHTTP.Modules.Placeholders;
13+
using GenHTTP.Modules.Basics;
1214

1315
namespace GenHTTP.Engine.Infrastructure
1416
{
@@ -51,6 +53,7 @@ internal CoreRouter(IHandlerBuilder content, IEnumerable<IConcernBuilder> concer
5153
Content = Concerns.Chain(this, concerns, (p) => content.Build(p));
5254

5355
Template = Placeholders.Template<TemplateModel>(Resource.FromAssembly("Template.html"))
56+
.AddRenderer("additions", (model, a) => RenderAdditions(model, (PageAdditions?)a))
5457
.Build();
5558

5659
ErrorRenderer = Placeholders.Template<ErrorModel>(Resource.FromAssembly(development ? "ErrorStacked.html" : "Error.html"))
@@ -86,6 +89,26 @@ public async ValueTask PrepareAsync()
8689

8790
public ValueTask RenderAsync(TemplateModel model, Stream target) => Template.RenderAsync(model, target);
8891

92+
private string RenderAdditions(IModel model, PageAdditions? additions)
93+
{
94+
var builder = new StringBuilder();
95+
96+
if (additions != null)
97+
{
98+
foreach (var style in additions.Styles)
99+
{
100+
builder.AppendLine($"<link rel=\"stylesheet\" href=\"{model.Handler.Route(model.Request, style.Path)}\" />");
101+
}
102+
103+
foreach (var script in additions.Scripts)
104+
{
105+
builder.AppendLine($"<script src=\"{model.Handler.Route(model.Request, script.Path)}\"></script>");
106+
}
107+
}
108+
109+
return builder.ToString();
110+
}
111+
89112
#endregion
90113

91114
}

Engine/Resources/Template.html

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070

7171
</style>
7272

73+
[additions]
74+
7375
</head>
7476

7577
<body>

Modules/Authentication.Web/Concern/WebAuthenticationConcern.cs

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public sealed class WebAuthenticationConcern : IConcern, IRootPathAppender, IHan
2727

2828
private IHandler SetupHandler { get; }
2929

30+
private IHandler ResourceHandler { get; }
31+
3032
#endregion
3133

3234
#region Initialization
@@ -42,6 +44,8 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
4244

4345
LoginHandler = integration.LoginHandler.Build(this);
4446
SetupHandler = integration.SetupHandler.Build(this);
47+
48+
ResourceHandler = integration.ResourceHandler.Build(this);
4549
}
4650

4751
#endregion
@@ -56,6 +60,13 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
5660
{
5761
var segment = request.Target.Current;
5862

63+
if (segment?.Value == Integration.ResourceRoute)
64+
{
65+
request.Target.Advance();
66+
67+
return await ResourceHandler.HandleAsync(request).ConfigureAwait(false);
68+
}
69+
5970
if (await Integration.CheckSetupRequired(request).ConfigureAwait(false))
6071
{
6172
if (segment?.Value != Integration.SetupRoute)
@@ -161,6 +172,11 @@ public void Append(PathBuilder path, IRequest request, IHandler? child = null)
161172
{
162173
path.Preprend(Integration.SetupRoute);
163174
}
175+
176+
if (child == ResourceHandler)
177+
{
178+
path.Preprend(Integration.ResourceRoute);
179+
}
164180
}
165181

166182
public IHandler? Find(string segment)
@@ -180,6 +196,11 @@ public void Append(PathBuilder path, IRequest request, IHandler? child = null)
180196
return SetupHandler;
181197
}
182198

199+
if (segment == "{web-auth-resources}")
200+
{
201+
return ResourceHandler;
202+
}
203+
183204
return null;
184205
}
185206

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using GenHTTP.Api.Content;
2+
using GenHTTP.Api.Content.Templating;
3+
4+
using GenHTTP.Modules.IO;
5+
using GenHTTP.Modules.Razor;
6+
7+
namespace GenHTTP.Modules.Authentication.Web.Controllers
8+
{
9+
10+
public class BaseController
11+
{
12+
13+
protected IHandlerBuilder RenderAccountEntry(string title)
14+
{
15+
return ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new BasicModel(r, h))
16+
.AddStyle("{web-auth-resources}/style.css")
17+
.Title(title);
18+
}
19+
20+
}
21+
22+
}

Modules/Authentication.Web/Controllers/LoginController.cs

+6-14
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,28 @@
33

44
using GenHTTP.Api.Content;
55
using GenHTTP.Api.Content.Authentication;
6-
using GenHTTP.Api.Content.Templating;
76
using GenHTTP.Api.Protocol;
7+
88
using GenHTTP.Modules.Basics;
99
using GenHTTP.Modules.Controllers;
10-
using GenHTTP.Modules.IO;
11-
using GenHTTP.Modules.Razor;
1210

1311
namespace GenHTTP.Modules.Authentication.Web.Controllers
1412
{
15-
16-
public class LoginController
13+
14+
public class LoginController : BaseController
1715
{
1816

1917
private Func<IRequest, string, string, ValueTask<IUser?>> PerformLogin { get; }
2018

21-
public LoginController(Func<IRequest, string, string, ValueTask<IUser?>> performLogin)
19+
public LoginController(Func<IRequest, string, string, ValueTask<IUser?>> performLogin)
2220
{
2321
PerformLogin = performLogin;
2422
}
2523

2624
public IHandlerBuilder Index()
2725
{
2826
// ToDo: already logged in
29-
return RenderLogin();
27+
return RenderAccountEntry("Login");
3028
}
3129

3230
[ControllerAction(RequestMethod.POST)]
@@ -42,16 +40,10 @@ public async Task<IHandlerBuilder> Index(string user, string password, IRequest
4240
}
4341
else
4442
{
45-
return RenderLogin();
43+
return RenderAccountEntry("Login");
4644
}
4745
}
4846

49-
private static IHandlerBuilder RenderLogin()
50-
{
51-
return ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new BasicModel(r, h))
52-
.Title("Login");
53-
}
54-
5547
}
5648

5749
}

Modules/Authentication.Web/Controllers/SetupController.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
namespace GenHTTP.Modules.Authentication.Web.Controllers
1414
{
1515

16-
public sealed class SetupController
16+
public sealed class SetupController : BaseController
1717
{
1818

1919
private Func<IRequest, string, string, ValueTask> PerformSetup { get; }
@@ -25,8 +25,7 @@ public SetupController(Func<IRequest, string, string, ValueTask> performSetup)
2525

2626
public IHandlerBuilder Index()
2727
{
28-
return ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new BasicModel(r, h))
29-
.Title("Setup");
28+
return RenderAccountEntry("Setup");
3029
}
3130

3231
[ControllerAction(RequestMethod.POST)]

Modules/Authentication.Web/GenHTTP.Modules.Authentication.Web.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
</PropertyGroup>
3434

3535
<ItemGroup>
36+
<None Remove="Resources\style.css" />
37+
</ItemGroup>
38+
39+
<ItemGroup>
40+
<EmbeddedResource Include="Resources\style.css" />
3641
<EmbeddedResource Include="Views\EnterAccount.cshtml" />
3742
</ItemGroup>
3843

Modules/Authentication.Web/ISimpleWebAuthIntegration.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
using GenHTTP.Api.Content.Authentication;
1+
using System.Threading.Tasks;
2+
3+
using GenHTTP.Api.Content.Authentication;
24
using GenHTTP.Api.Protocol;
3-
using System;
4-
using System.Collections.Generic;
5-
using System.Linq;
6-
using System.Text;
7-
using System.Threading.Tasks;
85

96
namespace GenHTTP.Modules.Authentication.Web
107
{
@@ -18,6 +15,8 @@ public interface ISimpleWebAuthIntegration
1815

1916
string LoginRoute { get => "login"; }
2017

18+
string ResourceRoute { get => "auth-resources"; }
19+
2120
ValueTask<bool> CheckSetupRequired(IRequest request);
2221

2322
ValueTask PerformSetup(IRequest request, string username, string password);

Modules/Authentication.Web/IWebAuthIntegration.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
using GenHTTP.Api.Content;
1+
using System.Threading.Tasks;
2+
3+
using GenHTTP.Api.Content;
24
using GenHTTP.Api.Content.Authentication;
35
using GenHTTP.Api.Protocol;
4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
96

107
namespace GenHTTP.Modules.Authentication.Web
118
{
@@ -23,6 +20,10 @@ public interface IWebAuthIntegration
2320

2421
string LoginRoute { get => "login"; }
2522

23+
IHandlerBuilder ResourceHandler { get; }
24+
25+
string ResourceRoute { get => "auth-resources"; }
26+
2627
ValueTask<bool> CheckSetupRequired(IRequest request);
2728

2829
ValueTask<IUser?> VerifyTokenAsync(string sessionToken);

0 commit comments

Comments
 (0)