Contact-form plugin
QA
-
In the BO of Singapore, set the site email to a valid test mailbox -
Open the public site, go to the contact page, and send the contact form: check that the email is received -
Try to send another contact form without the recaptcha: it should fail
-
-
Follow the instructions, install a new local website, then set the site email to a valid test mailbox -
Open the public site, go to the contact page, and send the contact form: check that the email data is correct in the log
-
Implementation
Unlike the other plugins, this is not something for the BO. It is for the public site. Our plugin needs 3 new things:
- Expose assets for the public website;
- Insert tags in the
<head>of liquid templates; - Expose a public API.
Expose assets for the public website
New hook
In packages/plugin-lib/types/backend-plugin-types.d.ts, the PluginApi interface, add:
setPublicAssetsDirectory(directory: string): void;
In plugin-loader.ts, the Plugin interface:
- declare the return value
: voidon other methods when missing - rename
urlPathtopublicDirName - add:
publicAssetsDir?: string;
In plugin-api.ts, the createPluginApi function must have a new parameter plugin: Plugin, and then a new API must be created for each plugin.
Save the directory in plugin.publicAssetsDir. The last call of setPublicAssetsDirectory overwrites previous calls.
The public assets middleware
In static-middlewares.ts:
- rename
themeAssetsMiddlewaretopublicAssetsMiddleware - append
|| !["GET", "HEAD"].includes(req.method)in the firstifofpublicAssetsMiddleware - in the same file, replace
req.method !== "GET"by!["GET", "HEAD"].includes(req.method)(there are 2 occurrences)
Now, in publicAssetsMiddleware:
- take example on
site-appand add:else if (path.startsWith("/assets/plugin/")) - in the
ifimplementation, take thepluginPublicDirNamein/assets/plugin/-pluginPublicDirName-here-/... - get the
plugininstance insiteContext.pluginsusingpluginPublicDirNameas the key. If there isn't, it's a 404. - assign
plugin.publicAssetsDirtobaseDir. If there isn't, it's a 404. - for
pathPrefix:
pathPrefix = `/assets/plugin/${plugin.publicDirName}/${plugin.plugin.version}/`;
Insert tags in the <head> of liquid templates
New hook
In packages/plugin-lib/types/backend-plugin-types.d.ts, the PluginApi interface, add:
addHeadTag(...htmlTags: string[]): void;
In site-context.types.ts, in SiteContext, just after plugins, add:
headTags: string[];
In create-site-context.ts, add the new headTags with an empty array.
In plugin-api.ts, implement addHeadTag by pushing the content into siteContext.headTags.
Use in the headTags liquid filter
In head-tags-filter.ts, at the end of the current implementation, append the content of siteContext.headTags.
Expose a public API
New hook
In packages/plugin-lib/types/backend-plugin-types.d.ts, the PluginApi interface, add:
setPublicApiHandler(handler: PublicApiHandler): void;
With:
import type { Request, Response } from "express";
// ...
export type PublicApiHandler = (pluginSiteContext: PluginSiteContext, req: Request, res: Response, relativePath: string) => Promise<void> | void;
Notice: PluginSiteContext is implemented in #84 (closed) . I suggest that we add it in the Plugin interface (plugin-loader.ts).
In plugin-loader.ts, the Plugin interface, add:
publicApiHandler?: PublicApiHandler;
In plugin-api.ts, in createPluginApi: save the handler in plugin.publicApiHandler. The last call of setPublicApiHandler overwrites previous calls.
A new middleware
In app/server/src/express/, add a new file public-api-middlewares.ts. Copy the publicAssetsMiddleware function and its imports. Then modify the code:
- The function is named
pluginPublicApiMiddleware - It doesn't check
req.method(all HTTP methods are allowed) - the path must begin with
/api/plugin/(or callnext()and return) - take the
pluginPublicDirNamein/api/plugin/-pluginPublicDirName-here-/... - get the
plugininstance insiteContext.pluginsusingpluginPublicDirNameas the key. If there isn't, it's a 404. - if
plugin.publicApiHandleris undefined, then it's a 404. - take the
relativePathin/api/plugin/:pluginPublicDirName/:relativePath - call
await plugin.publicApiHandler(pluginSiteContext, req, res, relativePath)
Search where is registered publicAssetsMiddleware and register publicApiMiddleware in the application, the same way.
Implement the plugin
Create a plugin
Create a plugin @paroicms/plugin-contact-form in a new directory plugins/contact-form/.
Extract the UI from Site-App
- Move the content of the
app/site-app/src/contact-form/folder in the plugin. - Take the
"contactForm"section in the locales too - Ensure that the Site-App can be still compiled
Configure a SolidJS application in the new plugin directory and build it.
In plugin.cjs:
- use
api.setPublicAssetsDirectoryin order to expose the bundled SolidJS application. - use
api.addHeadTagin order to insert<script>and<link>(CSS file) tags to the themes
Extract the controller
Move the directory app/server/src/modules/public-api/mail into the plugin directory.
In public-api-controller.ts, search submit-contact-form. Delete this function.
In plugin.cjs, use api.setPublicApiHandler in order to replace the controller.
Our moved code will need several things:
- Create a new TS sub-project with a
tsconfig.backend.jsonlike in this ticket: #84 (closed) -
getSiteFieldValueandsendMailmust be added inPluginSiteContext
Last things
Add "@paroicms/plugin-contact-form" in the site-schema.json and the package.json of Seoul and Singapore.