Predefined pages
Certain pages in the page tree are not typical content pages. An example of this would be a news page that is automatically generated from structured data. In Comet, we call such pages "Predefined pages".
Basic idea
Predefined pages are implemented using a dedicated document type in the page tree.
This document type has a single type
field.
The type represents the predefined page that should be rendered.
For instance, if the type "News" is selected in the Admin, the news page should be rendered at the corresponding position in the page tree.
Adding predefined pages to a project
This section shows the changes necessary to support predefined pages in a Comet project.
API
The following steps are required in the API:
- Creation of the predefined pages module
- Creation of the migration
- Registration of the
PredefinedPagesModule
- Registration of the
PredefinedPage
document type
Creation of the predefined pages module
Create the module in src/documents/predefined-pages
.
To do this, copy the corresponding folder from the Demo project.
The following files should be copied:
predefined-pages.module.ts
: The modulepredefined-pages.resolver.ts
: The GraphQL resolver with the required operationsentities/predefined-page.entity.ts
: The entitydto/predefined-page.input.ts
: The input
Creation of the migration
Execute the command npm --prefix api run mikro-orm:migration:generate
to create a migration for the new entity.
Ensure the database is running (npx dev-pm start docker
).
The created migration should look like this:
import { Migration } from "@mikro-orm/migrations";
export class Migration20220701145254 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table "PredefinedPage" ("id" uuid not null, "createdAt" timestamp with time zone not null, "updatedAt" timestamp with time zone not null, "type" text null);',
);
this.addSql(
'alter table "PredefinedPage" add constraint "PredefinedPage_pkey" primary key ("id");',
);
}
}
Registration of the PredefinedPagesModule
Register the newly created PredefinedPagesModule
in the AppModule
:
+ import { PredefinedPagesModule } from "./documents/predefined-pages/predefined-pages.module";
@Module({})
export class AppModule {
static forRoot(config: Config): DynamicModule {
return {
module: AppModule,
imports: [
/* ... */,
+ PredefinedPagesModule,
],
};
}
}
Registration of the PredefinedPage
document type
Register the newly created PredefinedPage
document type in the PageTreeModule
:
+ import { PredefinedPage } from "./documents/predefined-pages/entities/predefined-page.entity";
import { PredefinedPagesModule } from "./documents/predefined-pages/predefined-pages.module";
@Module({})
export class AppModule {
static forRoot(config: Config): DynamicModule {
return {
module: AppModule,
imports: [
/* ... */,
PageTreeModule.forRoot({
PageTreeNode: PageTreeNode,
Documents: [
Page,
Link,
+ PredefinedPage
],
Scope: PageTreeNodeScope,
}),
],
};
}
}
Admin
The following steps are required in the Admin:
- Creation of the predefined pages module
- Registration of the
PredefinedPage
document type
Creation of the predefined pages module
Create the module in src/documents/predefinedPages
.
To do this, copy the corresponding folder from the Demo project.
The following files should be copied:
EditPredefinedPage.tsx
: The editing componentEditPredefinedPage.gql.ts
: The GraphQL operations for the edit componentPredefinedPage.tsx
: The document typePredefinedPageLabels
: The labels for the predefined page types
Registration of the PredefinedPage
document type
Add the newly created document type to the page tree document types:
import { Link } from "@src/documents/links/Link";
import { Page } from "@src/documents/pages/Page";
+ import { PredefinedPage } from "@src/documents/predefinedPages/PredefinedPage";
export const pageTreeDocumentTypes: Record<string, DocumentInterface<any, any>> = {
Page,
Link,
+ PredefinedPage
};
Site
The following steps are required in the Site:
- Creation of the predefined pages module
- Adding the predefined pages to the middleware
- Adding the predefined page document type to the
PageLink
Creation of the predefined pages module
Create the module in src/documents/predefinedPages
.
To do this, copy the corresponding folder from the Demo project.
The following files should be copied:
predefinedPagePaths.ts
: The paths where the predefined pages are located in the code
Adding the predefined pages to the middleware
We use a combination of a redirect and a rewrite in the middleware to ensure that a predefined page can be viewed at the expected location in the page tree.
For example, consider a Comet website that is available in English and German.
For the German site, the news page should be located at https://example.com/de/aktuelles
.
However, in code, the news page is located at /app/[language]/news
, resulting in https://example.com/de/news
.
To ensure that the page can be viewed at the expected location, we need the following redirect and rewrite:
- A rewrite from
/de/aktuelles
to/de/news
(to ensure that the page can be viewed at the expected location) - A redirect from
/de/news
to/de/aktuelles
(to avoid duplicate content)
Perform the following changes in the middleware:
Copy the predefined pages helper functions from the Demo project
dangerSupport for filtering by document type in the
paginatedPageTreeNodes
query has been added in version 7.4.0. Make sure your project uses Comet 7.4.0 or later.Add the rewrite and redirect to the middleware:
+ import { getPredefinedPageRedirect, getPredefinedPageRewrite } from "./middleware/predefinedPages";
export async function middleware(request: NextRequest) {
/* ... */
+ const predefinedPageRedirect = await getPredefinedPageRedirect(scope, pathname);
+
+ if (predefinedPageRedirect) {
+ return NextResponse.redirect(new URL(predefinedPageRedirect, request.url), 307);
+ }
+
+ const predefinedPageRewrite = await getPredefinedPageRewrite(scope, pathname);
+
+ if (predefinedPageRewrite) {
+ return NextResponse.rewrite(new URL(predefinedPageRewrite, request.url));
+ }
return NextResponse.next();
}
Adding the predefined page document type to the PageLink
Add the newly created document type to the PageLink
component for links to work correctly:
function PageLink({ page, children, className: passedClassName, activeClassName, }: Props): JSX.Element | null {
/* ... */
if (page.documentType === "Link") {
/* ... */
} else if (page.documentType === "Page") {
/* ... */
+ } else if (page.documentType === "PredefinedPage") {
+ return (
+ <Link href={`/${page.scope.language}${page.path}`} className={className}>
+ {children}
+ </Link>
+ );
}
}
Now, all the necessary changes should have been made to the project. Add a new page tree node with the document type "Predefined Page" to test whether it works correctly in the Site.
Adding a new predefined page type
If you want to add another predefined page type (e.g., products), make the following changes:
API: Add the new type to the GraphQL enum:
src/documents/predefined-pages/entities/predefined-page.entity.tsexport enum PredefinedPageType {
News = "News",
+ Products = "Products",
}Admin: Add the new type to the labels:
src/documents/predefinedPages/predefinedPageLabels.tsxexport const predefinedPageLabels: Record<GQLPredefinedPageType, ReactNode> = {
News: <FormattedMessage id="predefinedPages.news" defaultMessage="News" />,
+ Products: <FormattedMessage id="predefinedPages.products" defaultMessage="Products" />,
};Site: Add the new type to the code paths:
src/documents/predefinedPages/predefinedPagePaths.tsexport const predefinedPagePaths = {
News: "/news",
+ Products: "/products",
};