mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Create ChainedTemplateEngine for combining engines
This commit is contained in:
parent
8c266f09c5
commit
18a71032c0
@ -23,7 +23,22 @@
|
|||||||
"providerFactory": { "@id": "urn:solid-server:default:IdentityProviderFactory" },
|
"providerFactory": { "@id": "urn:solid-server:default:IdentityProviderFactory" },
|
||||||
"templateHandler": {
|
"templateHandler": {
|
||||||
"@type": "TemplateHandler",
|
"@type": "TemplateHandler",
|
||||||
"templateEngine": { "@type": "EjsTemplateEngine" }
|
"templateEngine": {
|
||||||
|
"comment": "Renders the specific page and embeds it into the main HTML body.",
|
||||||
|
"@type": "ChainedTemplateEngine",
|
||||||
|
"renderedName": "htmlBody",
|
||||||
|
"engines": [
|
||||||
|
{
|
||||||
|
"comment": "Will be called with specific interaction templates to generate HTML snippets.",
|
||||||
|
"@type": "EjsTemplateEngine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Will embed the result of the first engine into the main HTML template.",
|
||||||
|
"@type": "EjsTemplateEngine",
|
||||||
|
"template": "$PACKAGE_ROOT/templates/main.html.ejs",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"interactionCompleter": {
|
"interactionCompleter": {
|
||||||
"comment": "Responsible for finishing OIDC interactions.",
|
"comment": "Responsible for finishing OIDC interactions.",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"@type": "InteractionRoute",
|
"@type": "InteractionRoute",
|
||||||
"route": "^/forgotpassword/?$",
|
"route": "^/forgotpassword/?$",
|
||||||
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password.html.ejs",
|
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password.html.ejs",
|
||||||
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/email-sent.html.ejs",
|
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password-response.html.ejs",
|
||||||
"handler": {
|
"handler": {
|
||||||
"@type": "ForgotPasswordHandler",
|
"@type": "ForgotPasswordHandler",
|
||||||
"args_accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" },
|
"args_accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" },
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"@type": "InteractionRoute",
|
"@type": "InteractionRoute",
|
||||||
"route": "^/resetpassword(/[^/]*)?$",
|
"route": "^/resetpassword(/[^/]*)?$",
|
||||||
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password.html.ejs",
|
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password.html.ejs",
|
||||||
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/message.html.ejs",
|
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password-response.html.ejs",
|
||||||
"handler": {
|
"handler": {
|
||||||
"@type": "ResetPasswordHandler",
|
"@type": "ResetPasswordHandler",
|
||||||
"accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" }
|
"accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" }
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
"@id": "urn:solid-server:default:MarkdownToHtmlConverter",
|
"@id": "urn:solid-server:default:MarkdownToHtmlConverter",
|
||||||
"@type": "MarkdownToHtmlConverter",
|
"@type": "MarkdownToHtmlConverter",
|
||||||
"templateEngine": {
|
"templateEngine": {
|
||||||
"@type": "HandlebarsTemplateEngine",
|
"@type": "EjsTemplateEngine",
|
||||||
"template": "$PACKAGE_ROOT/templates/main.html.hbs"
|
"template": "$PACKAGE_ROOT/templates/main.html.ejs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -182,9 +182,9 @@ export class IdentityProviderHttpHandler extends HttpHandler {
|
|||||||
throw new BadRequestHttpError(`Unsupported request: ${request.method} ${request.url}`);
|
throw new BadRequestHttpError(`Unsupported request: ${request.method} ${request.url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleTemplateResponse(response: HttpResponse, templateFile: string, contents: NodeJS.Dict<any>):
|
private async handleTemplateResponse(response: HttpResponse, templateFile: string, contents?: NodeJS.Dict<any>):
|
||||||
Promise<void> {
|
Promise<void> {
|
||||||
await this.templateHandler.handleSafe({ response, templateFile, contents });
|
await this.templateHandler.handleSafe({ response, templateFile, contents: contents ?? {}});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,7 +6,7 @@ export type InteractionHandlerResult = InteractionResponseResult | InteractionCo
|
|||||||
|
|
||||||
export interface InteractionResponseResult<T = NodeJS.Dict<any>> {
|
export interface InteractionResponseResult<T = NodeJS.Dict<any>> {
|
||||||
type: 'response';
|
type: 'response';
|
||||||
details: T;
|
details?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InteractionCompleteResult {
|
export interface InteractionCompleteResult {
|
||||||
|
@ -34,7 +34,7 @@ export class ResetPasswordHandler extends InteractionHandler {
|
|||||||
assertPassword(password, confirmPassword);
|
assertPassword(password, confirmPassword);
|
||||||
|
|
||||||
await this.resetPassword(recordId, password);
|
await this.resetPassword(recordId, password);
|
||||||
return { type: 'response', details: { message: 'Your password was successfully reset.' }};
|
return { type: 'response' };
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
throwIdpInteractionError(error);
|
throwIdpInteractionError(error);
|
||||||
}
|
}
|
||||||
|
@ -304,6 +304,7 @@ export * from './util/locking/SingleThreadedResourceLocker';
|
|||||||
export * from './util/locking/WrappedExpiringReadWriteLocker';
|
export * from './util/locking/WrappedExpiringReadWriteLocker';
|
||||||
|
|
||||||
// Util/Templates
|
// Util/Templates
|
||||||
|
export * from './util/templates/ChainedTemplateEngine';
|
||||||
export * from './util/templates/EjsTemplateEngine';
|
export * from './util/templates/EjsTemplateEngine';
|
||||||
export * from './util/templates/HandlebarsTemplateEngine';
|
export * from './util/templates/HandlebarsTemplateEngine';
|
||||||
export * from './util/templates/TemplateEngine';
|
export * from './util/templates/TemplateEngine';
|
||||||
|
@ -23,12 +23,8 @@ export class MarkdownToHtmlConverter extends TypedRepresentationConverter {
|
|||||||
|
|
||||||
public async handle({ representation }: RepresentationConverterArgs): Promise<Representation> {
|
public async handle({ representation }: RepresentationConverterArgs): Promise<Representation> {
|
||||||
const markdown = await readableToString(representation.data);
|
const markdown = await readableToString(representation.data);
|
||||||
// Try to extract the main title for use in the <title> tag
|
|
||||||
const title = /^#+\s*([^\n]+)\n/u.exec(markdown)?.[1];
|
|
||||||
|
|
||||||
// Place the rendered Markdown into the HTML template
|
|
||||||
const htmlBody = marked(markdown);
|
const htmlBody = marked(markdown);
|
||||||
const html = await this.templateEngine.render({ htmlBody, title });
|
const html = await this.templateEngine.render({ htmlBody });
|
||||||
|
|
||||||
return new BasicRepresentation(html, representation.metadata, TEXT_HTML);
|
return new BasicRepresentation(html, representation.metadata, TEXT_HTML);
|
||||||
}
|
}
|
||||||
|
39
src/util/templates/ChainedTemplateEngine.ts
Normal file
39
src/util/templates/ChainedTemplateEngine.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import type { Template, TemplateEngine } from './TemplateEngine';
|
||||||
|
import Dict = NodeJS.Dict;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the given array of {@link TemplateEngine}s in the order they appear,
|
||||||
|
* feeding the output of one into the input of the next.
|
||||||
|
*
|
||||||
|
* The first engine will be called with the provided contents and template parameters.
|
||||||
|
* All subsequent engines will be called with no template parameter.
|
||||||
|
* Contents will still be passed along and another entry will be added for the body of the previous output.
|
||||||
|
*/
|
||||||
|
export class ChainedTemplateEngine<T extends Dict<any> = Dict<any>> implements TemplateEngine<T> {
|
||||||
|
private readonly firstEngine: TemplateEngine<T>;
|
||||||
|
private readonly chainedEngines: TemplateEngine[];
|
||||||
|
private readonly renderedName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param engines - Engines will be executed in the same order as the array.
|
||||||
|
* @param renderedName - The name of the key used to pass the body of one engine to the next.
|
||||||
|
*/
|
||||||
|
public constructor(engines: TemplateEngine[], renderedName = 'body') {
|
||||||
|
if (engines.length === 0) {
|
||||||
|
throw new Error('At least 1 engine needs to be provided.');
|
||||||
|
}
|
||||||
|
this.firstEngine = engines[0];
|
||||||
|
this.chainedEngines = engines.slice(1);
|
||||||
|
this.renderedName = renderedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async render(contents: T): Promise<string>;
|
||||||
|
public async render<TCustom = T>(contents: TCustom, template: Template): Promise<string>;
|
||||||
|
public async render<TCustom = T>(contents: TCustom, template?: Template): Promise<string> {
|
||||||
|
let body = await this.firstEngine.render(contents, template!);
|
||||||
|
for (const engine of this.chainedEngines) {
|
||||||
|
body = await engine.render({ ...contents, [this.renderedName]: body });
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<h1>Authorize</h1>
|
||||||
<html lang="en">
|
<form action="/idp/confirm" method="post">
|
||||||
<head>
|
<p class="actions"><button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button></p>
|
||||||
<meta charset="utf-8"/>
|
</form>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<title>Authorize</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Authorize</h1>
|
|
||||||
<form action="/idp/confirm" method="post">
|
|
||||||
<p class="actions"><button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button></p>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<title>Email sent</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Email sent</h1>
|
|
||||||
<form action="/idp/forgotpassword" method="post">
|
|
||||||
<p>If your account exists, an email has been sent with a link to reset your password.</p>
|
|
||||||
<p>If you do not receive your email in a couple of minutes, check your spam folder or click the link below to send another email.</p>
|
|
||||||
|
|
||||||
<input type="hidden" name="email" value="<%= email %>" />
|
|
||||||
|
|
||||||
<p class="actions"><a href="/idp/login">Back to Log In</a></p>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<p class="actions">
|
|
||||||
<button type="submit" name="submit" class="link">Send Another Email</button>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||||||
|
<h1>Email sent</h1>
|
||||||
|
<form action="/idp/forgotpassword" method="post">
|
||||||
|
<p>If your account exists, an email has been sent with a link to reset your password.</p>
|
||||||
|
<p>If you do not receive your email in a couple of minutes, check your spam folder or click the link below to send another email.</p>
|
||||||
|
|
||||||
|
<input type="hidden" name="email" value="<%= email %>" />
|
||||||
|
|
||||||
|
<p class="actions"><a href="/idp/login">Back to Log In</a></p>
|
||||||
|
|
||||||
|
<p class="actions">
|
||||||
|
<button type="submit" name="submit" class="link">Send Another Email</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
@ -1,42 +1,19 @@
|
|||||||
<!DOCTYPE html>
|
<h1>Forgot password</h1>
|
||||||
<html lang="en">
|
<form action="/idp/forgotpassword" method="post">
|
||||||
<head>
|
<%if (errorMessage) { %>
|
||||||
<meta charset="utf-8"/>
|
<p class="error"><%= errorMessage %></p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<% } %>
|
||||||
<title>Forgot password</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Forgot password</h1>
|
|
||||||
<form action="/idp/forgotpassword" method="post">
|
|
||||||
<%if (errorMessage) { %>
|
|
||||||
<p class="error"><%= errorMessage %></p>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<label for="email">Email</label>
|
<label for="email">Email</label>
|
||||||
<input id="email" type="email" name="email" autofocus>
|
<input id="email" type="email" name="email" autofocus>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p class="actions"><button type="submit" name="submit">Send recovery email</button></p>
|
<p class="actions"><button type="submit" name="submit">Send recovery email</button></p>
|
||||||
|
|
||||||
<p class="actions"><a href="/idp/login" class="link">Log in</a></p>
|
<p class="actions"><a href="/idp/login" class="link">Log in</a></p>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -1,53 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<h1>Log in</h1>
|
||||||
<html lang="en">
|
<form action="/idp/login" method="post">
|
||||||
<head>
|
<%if (errorMessage) { %>
|
||||||
<meta charset="utf-8"/>
|
<p class="error"><%= errorMessage %></p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<% } %>
|
||||||
<title>Log in</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Log in</h1>
|
|
||||||
<form action="/idp/login" method="post">
|
|
||||||
<%if (errorMessage) { %>
|
|
||||||
<p class="error"><%= errorMessage %></p>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Your account</legend>
|
<legend>Your account</legend>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<label for="email">Email</label>
|
<label for="email">Email</label>
|
||||||
<input id="email" type="email" name="email" autofocus <% if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
|
<input id="email" type="email" name="email" autofocus <% if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
<input id="password" type="password" name="password">
|
<input id="password" type="password" name="password">
|
||||||
</li>
|
</li>
|
||||||
<li class="checkbox">
|
<li class="checkbox">
|
||||||
<label><input type="checkbox" name="remember" value="yes" checked>Stay logged in</label>
|
<label><input type="checkbox" name="remember" value="yes" checked>Stay logged in</label>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p class="actions"><button type="submit" name="submit">Log in</button></p>
|
<p class="actions"><button type="submit" name="submit">Log in</button></p>
|
||||||
|
|
||||||
<ul class="actions">
|
<ul class="actions">
|
||||||
<li><a href="/idp/register" class="link">Sign up</a></li>
|
<li><a href="/idp/register" class="link">Sign up</a></li>
|
||||||
<li><a href="/idp/forgotpassword" class="link">Forgot password</a></li>
|
<li><a href="/idp/forgotpassword" class="link">Forgot password</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -1,63 +1,40 @@
|
|||||||
<!DOCTYPE html>
|
<h1>You've been signed up</h1>
|
||||||
<html lang="en">
|
<p>
|
||||||
<head>
|
<strong>Welcome to Solid.</strong>
|
||||||
<meta charset="utf-8"/>
|
We wish you an exciting experience!
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
</p>
|
||||||
<title>You are signed up</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
<% if (createPod) { %>
|
||||||
</head>
|
<h2>Your new Pod</h2>
|
||||||
<body>
|
<p>
|
||||||
<header>
|
Your new Pod is located at <a href="<%= podBaseUrl %>" class="link"><%= podBaseUrl %></a>.
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
<br>
|
||||||
<h1>Community Solid Server</h1>
|
You can store your documents and data there.
|
||||||
</header>
|
</p>
|
||||||
<main>
|
<% } %>
|
||||||
<h1>You are signed up</h1>
|
|
||||||
|
<% if (createWebId) { %>
|
||||||
|
<h2>Your new WebID</h2>
|
||||||
|
<p>
|
||||||
|
Your new WebID is <a href="<%= webId %>" class="link"><%= webId %></a>.
|
||||||
|
<br>
|
||||||
|
You can use this identifier to interact with Solid pods and apps.
|
||||||
|
</p>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (register) { %>
|
||||||
|
<h2>Your new account</h2>
|
||||||
|
<p>
|
||||||
|
Via your email address <em><%= email %></em>,
|
||||||
|
this server lets you <a href="./login">log in</a> to Solid apps
|
||||||
|
with your WebID <a href="<%= webId %>" class="link"><%= webId %></a>
|
||||||
|
</p>
|
||||||
|
<% if (!createWebId) { %>
|
||||||
<p>
|
<p>
|
||||||
<strong>Welcome to Solid.</strong>
|
You will need to add the triple
|
||||||
We wish you an exciting experience!
|
<code><%= `<${webId}> <http://www.w3.org/ns/solid/terms#oidcIssuer> <${oidcIssuer}>.`%></code>
|
||||||
|
to your existing WebID document <em><%= webId %></em>
|
||||||
|
to indicate that you trust this server as a login provider.
|
||||||
</p>
|
</p>
|
||||||
|
<% } %>
|
||||||
<% if (createPod) { %>
|
<% } %>
|
||||||
<h2>Your new Pod</h2>
|
|
||||||
<p>
|
|
||||||
Your new Pod is located at <a href="<%= podBaseUrl %>" class="link"><%= podBaseUrl %></a>.
|
|
||||||
<br>
|
|
||||||
You can store your documents and data there.
|
|
||||||
</p>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (createWebId) { %>
|
|
||||||
<h2>Your new WebID</h2>
|
|
||||||
<p>
|
|
||||||
Your new WebID is <a href="<%= webId %>" class="link"><%= webId %></a>.
|
|
||||||
<br>
|
|
||||||
You can use this identifier to interact with Solid pods and apps.
|
|
||||||
</p>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (register) { %>
|
|
||||||
<h2>Your new account</h2>
|
|
||||||
<p>
|
|
||||||
Via your email address <em><%= email %></em>,
|
|
||||||
this server lets you <a href="./login">log in</a> to Solid apps
|
|
||||||
with your WebID <a href="<%= webId %>" class="link"><%= webId %></a>
|
|
||||||
</p>
|
|
||||||
<% if (!createWebId) { %>
|
|
||||||
<p>
|
|
||||||
You will need to add the triple
|
|
||||||
<code><%= `<${webId}> <http://www.w3.org/ns/solid/terms#oidcIssuer> <${oidcIssuer}>.`%></code>
|
|
||||||
to your existing WebID document <em><%= webId %></em>
|
|
||||||
to indicate that you trust this server as a login provider.
|
|
||||||
</p>
|
|
||||||
<% } %>
|
|
||||||
<% } %>
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -1,212 +1,189 @@
|
|||||||
<!DOCTYPE html>
|
<h1>Sign up</h1>
|
||||||
<html lang="en">
|
<form action="/idp/register" method="post" id="mainForm">
|
||||||
<head>
|
<% const isBlankForm = !('email' in prefilled); %>
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<title>Register</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Sign up</h1>
|
|
||||||
<form action="/idp/register" method="post" id="mainForm">
|
|
||||||
<% const isBlankForm = !('email' in prefilled); %>
|
|
||||||
|
|
||||||
<% if (errorMessage) { %>
|
<% if (errorMessage) { %>
|
||||||
<p class="error">Error: <%= errorMessage %></p>
|
<p class="error">Error: <%= errorMessage %></p>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Your WebID</legend>
|
<legend>Your WebID</legend>
|
||||||
<p>
|
<p>
|
||||||
A <em>WebID</em> is a unique identifier for you
|
A <em>WebID</em> is a unique identifier for you
|
||||||
in the form of a URL.
|
in the form of a URL.
|
||||||
<br>
|
<br>
|
||||||
You WebID lets you log in to Solid apps
|
You WebID lets you log in to Solid apps
|
||||||
and access non-public data in Pods.
|
and access non-public data in Pods.
|
||||||
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" id="createWebIdOn" name="createWebId" value="on"<%
|
||||||
|
if (isBlankForm || prefilled.createWebId) { %> checked<% } %>>
|
||||||
|
Create a new WebID for my Pod.
|
||||||
|
</label>
|
||||||
|
<p id="createWebIdForm">
|
||||||
|
Please also create a Pod below, since your WebID will be stored there.
|
||||||
</p>
|
</p>
|
||||||
<ol>
|
</li>
|
||||||
<li class="radio">
|
<li class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" id="createWebIdOn" name="createWebId" value="on"<%
|
<input type="radio" id="createWebIdOff" name="createWebId" value=""<%
|
||||||
if (isBlankForm || prefilled.createWebId) { %> checked<% } %>>
|
if (!isBlankForm && !prefilled.createWebId) { %> checked<% } %>>
|
||||||
Create a new WebID for my Pod.
|
Use my existing WebID to access my Pod.
|
||||||
</label>
|
</label>
|
||||||
<p id="createWebIdForm">
|
<ol id="existingWebIdForm">
|
||||||
Please also create a Pod below, since your WebID will be stored there.
|
<li>
|
||||||
</p>
|
<label for="webId">Existing WebID:</label>
|
||||||
|
<input id="webId" type="text" name="webId" value="<%= prefilled.webId || '' %>">
|
||||||
</li>
|
</li>
|
||||||
<li class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" id="createWebIdOff" name="createWebId" value=""<%
|
|
||||||
if (!isBlankForm && !prefilled.createWebId) { %> checked<% } %>>
|
|
||||||
Use my existing WebID to access my Pod.
|
|
||||||
</label>
|
|
||||||
<ol id="existingWebIdForm">
|
|
||||||
<li>
|
|
||||||
<label for="webId">Existing WebID:</label>
|
|
||||||
<input id="webId" type="text" name="webId" value="<%= prefilled.webId || '' %>">
|
|
||||||
</li>
|
|
||||||
<li class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="register" name="register"<%
|
|
||||||
if (isBlankForm || prefilled.register) { %> checked<% } %>>
|
|
||||||
Use my new account to log in with this WebID.
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>Your Pod</legend>
|
|
||||||
<p>
|
|
||||||
A Pod is a place to store your data.
|
|
||||||
<br>
|
|
||||||
If you create a new WebID, you must also create a Pod to store that WebID.
|
|
||||||
</p>
|
|
||||||
<ol>
|
|
||||||
<li class="checkbox">
|
<li class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="createPod" name="createPod"<%
|
<input type="checkbox" id="register" name="register"<%
|
||||||
if (isBlankForm || prefilled.createPod) { %> checked<% } %>>
|
if (isBlankForm || prefilled.register) { %> checked<% } %>>
|
||||||
Create a new Pod with my WebID as owner.
|
Use my new account to log in with this WebID.
|
||||||
</label>
|
</label>
|
||||||
<ol id="createPodForm">
|
|
||||||
<li>
|
|
||||||
<label for="podName">Pod name:</label>
|
|
||||||
<input id="podName" type="text" name="podName" <%
|
|
||||||
if (prefilled.podName) { %> value="<%= prefilled.podName %>" <% } %>>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</fieldset>
|
</li>
|
||||||
|
</ol>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Your account</legend>
|
<legend>Your Pod</legend>
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
Choose the credentials you want to use to log in to this server in the future.
|
|
||||||
</p>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
<label for="email">Email:</label>
|
|
||||||
<input id="email" type="text" name="email" <%
|
|
||||||
if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<ol id="passwordForm">
|
|
||||||
<li>
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
<input id="password" type="password" name="password">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label for="confirmPassword">Confirm password:</label>
|
|
||||||
<input id="confirmPassword" type="password" name="confirmPassword">
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<div id="noPasswordForm" class="hidden">
|
|
||||||
<p>
|
|
||||||
Since you will be using your existing WebID setup to access your pod,
|
|
||||||
<br>
|
|
||||||
you do <em>not</em> need to set a password.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<p class="actions"><button type="submit" name="submit">Sign up</button></p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Assist the user with filling out the form by hiding irrelevant fields
|
|
||||||
(() => {
|
|
||||||
// Wire up the UI elements
|
|
||||||
const elements = {};
|
|
||||||
[
|
|
||||||
'createWebIdOn', 'createWebIdOff', 'createWebIdForm', 'existingWebIdForm', 'webId',
|
|
||||||
'createPod', 'createPodForm', 'podName',
|
|
||||||
'register', 'passwordForm', 'noPasswordForm', 'mainForm',
|
|
||||||
].forEach(id => {
|
|
||||||
elements[id] = document.getElementById(id);
|
|
||||||
elements[id].addEventListener('change', updateUI);
|
|
||||||
});
|
|
||||||
updateUI();
|
|
||||||
mainForm.classList.add('loaded');
|
|
||||||
|
|
||||||
// Updates the UI when something has changed
|
|
||||||
function updateUI({ srcElement } = {}) {
|
|
||||||
// When Pod creation is required, automatically tick the corresponding checkbox
|
|
||||||
if (elements.createWebIdOn.checked)
|
|
||||||
elements.createPod.checked = true;
|
|
||||||
elements.createPod.disabled = elements.createWebIdOn.checked;
|
|
||||||
|
|
||||||
// Hide irrelevant fields
|
|
||||||
setVisibility('createWebIdForm', elements.createWebIdOn.checked);
|
|
||||||
setVisibility('existingWebIdForm', elements.createWebIdOff.checked);
|
|
||||||
setVisibility('createPodForm', elements.createPod.checked);
|
|
||||||
setVisibility('passwordForm', elements.createWebIdOn.checked || elements.register.checked);
|
|
||||||
setVisibility('noPasswordForm', !isVisible('passwordForm'));
|
|
||||||
|
|
||||||
// If child elements have just been activated, focus on them
|
|
||||||
if (srcElement?.checked) {
|
|
||||||
switch(document.activeElement) {
|
|
||||||
case elements.createWebIdOff:
|
|
||||||
const { webId } = elements;
|
|
||||||
webId.value = webId.value.startsWith('http') ? webId.value : 'https://';
|
|
||||||
webId.focus();
|
|
||||||
break;
|
|
||||||
case elements.createPod:
|
|
||||||
elements.podName.focus();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks whether the given element is visible
|
|
||||||
function isVisible(element) {
|
|
||||||
return !(elements[element] ?? element).classList.contains('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the visibility of the given element
|
|
||||||
function setVisibility(element, visible) {
|
|
||||||
// Show or hide the element
|
|
||||||
element = elements[element] ?? element;
|
|
||||||
element.classList[visible ? 'remove' : 'add']('hidden');
|
|
||||||
|
|
||||||
// Disable children of hidden elements,
|
|
||||||
// such that the browser does not expect input for them
|
|
||||||
for (const child of getDescendants(element)) {
|
|
||||||
if ('disabled' in child)
|
|
||||||
child.disabled = !visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtains all children, grandchildren, etc. of the given element
|
|
||||||
function getDescendants(element) {
|
|
||||||
return [...element.querySelectorAll("*")];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable all elements on form submission (otherwise their value is not submitted)
|
|
||||||
elements.mainForm.addEventListener('submit', () => {
|
|
||||||
for (const child of getDescendants(elements.mainForm))
|
|
||||||
child.disabled = false;
|
|
||||||
});
|
|
||||||
elements.mainForm.addEventListener('formdata', updateUI);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
<p>
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
A Pod is a place to store your data.
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
<br>
|
||||||
|
If you create a new WebID, you must also create a Pod to store that WebID.
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
<ol>
|
||||||
</body>
|
<li class="checkbox">
|
||||||
</html>
|
<label>
|
||||||
|
<input type="checkbox" id="createPod" name="createPod"<%
|
||||||
|
if (isBlankForm || prefilled.createPod) { %> checked<% } %>>
|
||||||
|
Create a new Pod with my WebID as owner.
|
||||||
|
</label>
|
||||||
|
<ol id="createPodForm">
|
||||||
|
<li>
|
||||||
|
<label for="podName">Pod name:</label>
|
||||||
|
<input id="podName" type="text" name="podName" <%
|
||||||
|
if (prefilled.podName) { %> value="<%= prefilled.podName %>" <% } %>>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Your account</legend>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Choose the credentials you want to use to log in to this server in the future.
|
||||||
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
<input id="email" type="text" name="email" <%
|
||||||
|
if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<ol id="passwordForm">
|
||||||
|
<li>
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input id="password" type="password" name="password">
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label for="confirmPassword">Confirm password:</label>
|
||||||
|
<input id="confirmPassword" type="password" name="confirmPassword">
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div id="noPasswordForm" class="hidden">
|
||||||
|
<p>
|
||||||
|
Since you will be using your existing WebID setup to access your pod,
|
||||||
|
<br>
|
||||||
|
you do <em>not</em> need to set a password.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="actions"><button type="submit" name="submit">Sign up</button></p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Assist the user with filling out the form by hiding irrelevant fields
|
||||||
|
(() => {
|
||||||
|
// Wire up the UI elements
|
||||||
|
const elements = {};
|
||||||
|
[
|
||||||
|
'createWebIdOn', 'createWebIdOff', 'createWebIdForm', 'existingWebIdForm', 'webId',
|
||||||
|
'createPod', 'createPodForm', 'podName',
|
||||||
|
'register', 'passwordForm', 'noPasswordForm', 'mainForm',
|
||||||
|
].forEach(id => {
|
||||||
|
elements[id] = document.getElementById(id);
|
||||||
|
elements[id].addEventListener('change', updateUI);
|
||||||
|
});
|
||||||
|
updateUI();
|
||||||
|
mainForm.classList.add('loaded');
|
||||||
|
|
||||||
|
// Updates the UI when something has changed
|
||||||
|
function updateUI({ srcElement } = {}) {
|
||||||
|
// When Pod creation is required, automatically tick the corresponding checkbox
|
||||||
|
if (elements.createWebIdOn.checked)
|
||||||
|
elements.createPod.checked = true;
|
||||||
|
elements.createPod.disabled = elements.createWebIdOn.checked;
|
||||||
|
|
||||||
|
// Hide irrelevant fields
|
||||||
|
setVisibility('createWebIdForm', elements.createWebIdOn.checked);
|
||||||
|
setVisibility('existingWebIdForm', elements.createWebIdOff.checked);
|
||||||
|
setVisibility('createPodForm', elements.createPod.checked);
|
||||||
|
setVisibility('passwordForm', elements.createWebIdOn.checked || elements.register.checked);
|
||||||
|
setVisibility('noPasswordForm', !isVisible('passwordForm'));
|
||||||
|
|
||||||
|
// If child elements have just been activated, focus on them
|
||||||
|
if (srcElement?.checked) {
|
||||||
|
switch(document.activeElement) {
|
||||||
|
case elements.createWebIdOff:
|
||||||
|
const { webId } = elements;
|
||||||
|
webId.value = webId.value.startsWith('http') ? webId.value : 'https://';
|
||||||
|
webId.focus();
|
||||||
|
break;
|
||||||
|
case elements.createPod:
|
||||||
|
elements.podName.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the given element is visible
|
||||||
|
function isVisible(element) {
|
||||||
|
return !(elements[element] ?? element).classList.contains('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the visibility of the given element
|
||||||
|
function setVisibility(element, visible) {
|
||||||
|
// Show or hide the element
|
||||||
|
element = elements[element] ?? element;
|
||||||
|
element.classList[visible ? 'remove' : 'add']('hidden');
|
||||||
|
|
||||||
|
// Disable children of hidden elements,
|
||||||
|
// such that the browser does not expect input for them
|
||||||
|
for (const child of getDescendants(element)) {
|
||||||
|
if ('disabled' in child)
|
||||||
|
child.disabled = !visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains all children, grandchildren, etc. of the given element
|
||||||
|
function getDescendants(element) {
|
||||||
|
return [...element.querySelectorAll("*")];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable all elements on form submission (otherwise their value is not submitted)
|
||||||
|
elements.mainForm.addEventListener('submit', () => {
|
||||||
|
for (const child of getDescendants(elements.mainForm))
|
||||||
|
child.disabled = false;
|
||||||
|
});
|
||||||
|
elements.mainForm.addEventListener('formdata', updateUI);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
<h1>Password reset</h1>
|
||||||
|
<p>Your password was successfully reset.</p>
|
@ -1,44 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<h1>Reset password</h1>
|
||||||
<html lang="en">
|
<form method="post">
|
||||||
<head>
|
<%if (errorMessage) { %>
|
||||||
<meta charset="utf-8"/>
|
<p class="error"><%= errorMessage %></p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<% } %>
|
||||||
<title>Reset password</title>
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<h1>Reset password</h1>
|
|
||||||
<form method="post">
|
|
||||||
<%if (errorMessage) { %>
|
|
||||||
<p class="error"><%= errorMessage %></p>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<label for="password">New password</label>
|
<label for="password">New password</label>
|
||||||
<input id="password" type="password" name="password" placeholder="">
|
<input id="password" type="password" name="password" placeholder="">
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="confirmPassword">Confirm new password</label>
|
<label for="confirmPassword">Confirm new password</label>
|
||||||
<input id="confirmPassword" type="password" name="confirmPassword" placeholder="">
|
<input id="confirmPassword" type="password" name="confirmPassword" placeholder="">
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p class="actions"><button type="submit" name="submit">Reset password</button></p>
|
<p class="actions"><button type="submit" name="submit">Reset password</button></p>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<title><%= message %></title>
|
<title><%= extractTitle(htmlBody) %></title>
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<h1>Community Solid Server</h1>
|
<h1>Community Solid Server</h1>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<p><%= message %></p>
|
<%- htmlBody %>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<p>
|
<p>
|
||||||
@ -22,3 +22,10 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
<%
|
||||||
|
function extractTitle(body) {
|
||||||
|
const match = /^<h1[^>]*>([^<]*)<\/h1>/u.exec(body);
|
||||||
|
return match ? match[1] : 'Solid';
|
||||||
|
}
|
||||||
|
%>
|
@ -1,26 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
{{#if title}}
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
{{/if}}
|
|
||||||
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
|
|
||||||
<h1>Community Solid Server</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
{{{ htmlBody }}}
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
|
|
||||||
and <a href="https://www.imec-int.com/">imec</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -100,6 +100,19 @@ describe('An IdentityProviderHttpHandler', (): void => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports InteractionResponseResults without details.', async(): Promise<void> => {
|
||||||
|
request.url = '/idp/routeResponse';
|
||||||
|
request.method = 'POST';
|
||||||
|
(routes.response.handler as jest.Mocked<InteractionHandler>).handleSafe.mockResolvedValueOnce({ type: 'response' });
|
||||||
|
await expect(handler.handle({ request, response })).resolves.toBeUndefined();
|
||||||
|
expect(routes.response.handler.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(routes.response.handler.handleSafe).toHaveBeenLastCalledWith({ request, response });
|
||||||
|
expect(templateHandler.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(templateHandler.handleSafe).toHaveBeenLastCalledWith(
|
||||||
|
{ response, templateFile: routes.response.responseTemplate, contents: {}},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('calls the interactionCompleter for InteractionCompleteResults.', async(): Promise<void> => {
|
it('calls the interactionCompleter for InteractionCompleteResults.', async(): Promise<void> => {
|
||||||
request.url = '/idp/routeComplete';
|
request.url = '/idp/routeComplete';
|
||||||
request.method = 'POST';
|
request.method = 'POST';
|
||||||
|
@ -48,10 +48,7 @@ describe('A ResetPasswordHandler', (): void => {
|
|||||||
|
|
||||||
it('renders a message on success.', async(): Promise<void> => {
|
it('renders a message on success.', async(): Promise<void> => {
|
||||||
request = createPostFormRequest({ password: 'password!', confirmPassword: 'password!' }, url);
|
request = createPostFormRequest({ password: 'password!', confirmPassword: 'password!' }, url);
|
||||||
await expect(handler.handle({ request, response })).resolves.toEqual({
|
await expect(handler.handle({ request, response })).resolves.toEqual({ type: 'response' });
|
||||||
details: { message: 'Your password was successfully reset.' },
|
|
||||||
type: 'response',
|
|
||||||
});
|
|
||||||
expect(accountStore.getForgotPasswordRecord).toHaveBeenCalledTimes(1);
|
expect(accountStore.getForgotPasswordRecord).toHaveBeenCalledTimes(1);
|
||||||
expect(accountStore.getForgotPasswordRecord).toHaveBeenLastCalledWith(recordId);
|
expect(accountStore.getForgotPasswordRecord).toHaveBeenLastCalledWith(recordId);
|
||||||
expect(accountStore.deleteForgotPasswordRecord).toHaveBeenCalledTimes(1);
|
expect(accountStore.deleteForgotPasswordRecord).toHaveBeenCalledTimes(1);
|
||||||
|
@ -35,19 +35,4 @@ describe('A MarkdownToHtmlConverter', (): void => {
|
|||||||
{ htmlBody: '<p>Text <code>code</code> more text.</p>\n' },
|
{ htmlBody: '<p>Text <code>code</code> more text.</p>\n' },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses the main markdown header as title if there is one.', async(): Promise<void> => {
|
|
||||||
const markdown = '# title text\nmore text';
|
|
||||||
const representation = new BasicRepresentation(markdown, 'text/markdown', true);
|
|
||||||
const prom = converter.handle({ identifier, representation, preferences });
|
|
||||||
await expect(prom).resolves.toBeDefined();
|
|
||||||
const result = await prom;
|
|
||||||
expect(result.binary).toBe(true);
|
|
||||||
expect(result.metadata.contentType).toBe('text/html');
|
|
||||||
await expect(readableToString(result.data)).resolves.toBe('<html>');
|
|
||||||
expect(templateEngine.render).toHaveBeenCalledTimes(1);
|
|
||||||
expect(templateEngine.render).toHaveBeenLastCalledWith(
|
|
||||||
{ htmlBody: '<h1 id="title-text">title text</h1>\n<p>more text</p>\n', title: 'title text' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
39
test/unit/util/templates/ChainedTemplateEngine.test.ts
Normal file
39
test/unit/util/templates/ChainedTemplateEngine.test.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { ChainedTemplateEngine } from '../../../../src/util/templates/ChainedTemplateEngine';
|
||||||
|
import type { TemplateEngine } from '../../../../src/util/templates/TemplateEngine';
|
||||||
|
|
||||||
|
describe('A ChainedTemplateEngine', (): void => {
|
||||||
|
const contents = { title: 'myTitle' };
|
||||||
|
const template = { templateFile: '/template.tmpl' };
|
||||||
|
let engines: jest.Mocked<TemplateEngine>[];
|
||||||
|
let engine: ChainedTemplateEngine;
|
||||||
|
|
||||||
|
beforeEach(async(): Promise<void> => {
|
||||||
|
engines = [
|
||||||
|
{ render: jest.fn().mockResolvedValue('body1') },
|
||||||
|
{ render: jest.fn().mockResolvedValue('body2') },
|
||||||
|
];
|
||||||
|
|
||||||
|
engine = new ChainedTemplateEngine(engines);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if no engines are provided.', async(): Promise<void> => {
|
||||||
|
expect((): any => new ChainedTemplateEngine([])).toThrow('At least 1 engine needs to be provided.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('chains the engines.', async(): Promise<void> => {
|
||||||
|
await expect(engine.render(contents, template)).resolves.toEqual('body2');
|
||||||
|
expect(engines[0].render).toHaveBeenCalledTimes(1);
|
||||||
|
expect(engines[0].render).toHaveBeenLastCalledWith(contents, template);
|
||||||
|
expect(engines[1].render).toHaveBeenCalledTimes(1);
|
||||||
|
expect(engines[1].render).toHaveBeenLastCalledWith({ ...contents, body: 'body1' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can use a different field to pass along the body.', async(): Promise<void> => {
|
||||||
|
engine = new ChainedTemplateEngine(engines, 'different');
|
||||||
|
await expect(engine.render(contents, template)).resolves.toEqual('body2');
|
||||||
|
expect(engines[0].render).toHaveBeenCalledTimes(1);
|
||||||
|
expect(engines[0].render).toHaveBeenLastCalledWith(contents, template);
|
||||||
|
expect(engines[1].render).toHaveBeenCalledTimes(1);
|
||||||
|
expect(engines[1].render).toHaveBeenLastCalledWith({ ...contents, different: 'body1' });
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user