add the possibility to edit the template for the forgot password email

Signed-off-by: Pierre Noël <petersg83@gmail.com>
This commit is contained in:
Pierre Noël 2020-06-29 11:27:18 +02:00 committed by Alexandre Bodin
parent bfadebdf2c
commit e000432027
9 changed files with 195 additions and 39 deletions

View File

@ -223,6 +223,7 @@ module.exports = {
'/v3.x/admin-panel/customization',
'/v3.x/admin-panel/custom-webpack-config',
'/v3.x/admin-panel/deploy',
'/v3.x/admin-panel/forgot-password',
],
},
{

View File

@ -0,0 +1,48 @@
# Forgot Password Email
## Customize forgot password email
You may want to customize the forgot password email.
You can do it by providing your own template (formatted as a [lodash template](https://lodash.com/docs/4.17.15#template)).
The template will be compiled with the following variables: `url`, `user.email`, `user.username`, `user.firstname`, `user.lastname`.
### Example
**Path -** `./config/servers.js`
```js
const forgotPasswordTemplate = require('./email-templates/forgot-password');
module.exports = ({ env }) => ({
// ...
admin: {
// ...
forgotPassword: {
from: 'support@mywebsite.fr',
replyTo: 'support@mywebsite.fr',
emailTemplate: forgotPasswordTemplate,
},
// ...
},
// ...
});
```
**Path -** `./config/email-templates/forgot-password.js`
```js
const subject = `Reset password`;
const html = `<p>Hi <%= user.firstname %></p>
<p>Sorry you lost your password. You can click here to reset it: <%= url %></p>`;
const text = `Hi <%= user.firstname %>
Sorry you lost your password. You can click here to reset it: <%= url %>`;
module.exports = {
subject,
text,
html,
};
```

View File

@ -167,22 +167,25 @@ module.exports = ({ env }) => ({
```
**Available options**
| Property | Description | Type | Default |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ----------- |
| `host` | Host name | string | `localhost` |
| `port` | Port on which the server should be running. | integer | `1337` |
| `emitErrors` | Enable errors to be emitted to `koa` when they happen in order to attach custom logic or use error reporting services. | boolean | `false` |
| `url` | Url of the server. Enable proxy support such as Apache or Nginx, example: `https://mywebsite.com/api`. The url can be relative, if so, it is used with `http://${host}:${port}` as the base url. | string | `''` |
| `cron` | Cron configuration (powered by [`node-schedule`](https://github.com/node-schedule/node-schedule)) | Object | |
| `cron.enabled` | Enable or disable CRON tasks to schedule jobs at specific dates. | boolean | `false` |
| `admin` | Admin panel configuration | Object | |
| `admin.url` | Url of your admin panel. Default value: `/admin`. Note: If the url is relative, it will be concatenated with `url`. | string | `/admin` |
| `admin.autoOpen` | Enable or disabled administration opening on start. | boolean | `true` |
| `admin.watchIgnoreFiles` | Add custom files that should not be watched during development. See more [here](https://github.com/paulmillr/chokidar#path-filtering) (property `ignored`). | Array(string) | `[]` |
| `admin.host` | Use a different host for the admin panel. Only used along with `strapi develop --watch-admin` | string | `localhost` |
| `admin.port` | Use a different port for the admin panel. Only used along with `strapi develop --watch-admin` | string | `8000` |
| `admin.serveAdminPanel` | If false, the admin panel won't be served. Note: the `index.html` will still be served, see [defaultIndex option](./middlewares#global-middlewares) | boolean | `true` |
| Property | Description | Type | Default |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `host` | Host name | string | `localhost` |
| `port` | Port on which the server should be running. | integer | `1337` |
| `emitErrors` | Enable errors to be emitted to `koa` when they happen in order to attach custom logic or use error reporting services. | boolean | `false` |
| `url` | Url of the server. Enable proxy support such as Apache or Nginx, example: `https://mywebsite.com/api`. The url can be relative, if so, it is used with `http://${host}:${port}` as the base url. | string | `''` |
| `cron` | Cron configuration (powered by [`node-schedule`](https://github.com/node-schedule/node-schedule)) | Object | |
| `cron.enabled` | Enable or disable CRON tasks to schedule jobs at specific dates. | boolean | `false` |
| `admin` | Admin panel configuration | Object | |
| `admin.url` | Url of your admin panel. Default value: `/admin`. Note: If the url is relative, it will be concatenated with `url`. | string | `/admin` |
| `admin.autoOpen` | Enable or disabled administration opening on start. | boolean | `true` |
| `admin.watchIgnoreFiles` | Add custom files that should not be watched during development. See more [here](https://github.com/paulmillr/chokidar#path-filtering) (property `ignored`). | Array(string) | `[]` |
| `admin.host` | Use a different host for the admin panel. Only used along with `strapi develop --watch-admin` | string | `localhost` |
| `admin.port` | Use a different port for the admin panel. Only used along with `strapi develop --watch-admin` | string | `8000` |
| `admin.serveAdminPanel` | If false, the admin panel won't be served. Note: the `index.html` will still be served, see [defaultIndex option](./middlewares#global-middlewares) | boolean | `true` |
| `admin.forgotPassword` | Settings to customize the forgot password email (see more here: [Forgot Password Email](../admin-panel/forgot-password)) | Object | {} |
| `admin.forgotPassword.emailTemplate` | Email template as defined in [email plugin](../plugins/email#create-an-email-from-a-template-fillemailoptions) | Object | [Default template](https://github.com/strapi/strapi/tree/master/packages/strapi-admin/config/email-templates/forgot-password.js) |
| `admin.forgotPassword.from` | Sender mail address | string | Default value defined in your [provider configuration](../plugins/email#configure-your-provider) |
| `admin.forgotPassword.replyTo` | Default address or addresses the receiver is asked to reply to | string | Default value defined in your [provider configuration](../plugins/email#configure-your-provider) |
## Functions

View File

@ -4,9 +4,13 @@ Thanks to the plugin `Email`, you can send email from your server or externals p
## Programmatic usage
### Send an email - `.send()`
In your custom controllers or services you may want to send email.
By using the following function, Strapi will use the configured provider to send an email.
**Example**
```js
await strapi.plugins['email'].services.email.send({
to: 'paulbocuse@strapi.io',
@ -20,6 +24,40 @@ await strapi.plugins['email'].services.email.send({
});
```
### Send an email using a template - `.sendTemplatedEmail()`
When you send an email, you will most likely want to build it from a template you wrote.
The email plugin provides the service `sendTemplatedEmail` that compile the email and then sends it. The function have the following params:
| param | description | type | default |
| --------------- | ------------------------------------------------------------------------------------------------------------------------ | ------ | ------- |
| `emailOptions` | Object that contains email options (`to`, `from`, `replyTo`, `cc`, `bcc`) except `subject`, `text` and `html` | object | `{}` |
| `emailTemplate` | Object that contains `subject`, `text` and `html` as [lodash string templates](https://lodash.com/docs/4.17.15#template) | object | `{}` |
| `data` | Object that contains the data used to compile the templates | object | `{}` |
**Example**
```js
const emailTemplate = {
subject: 'Welcome <%= user.firstname %>',
text: `Welcome on mywebsite.fr!
Your account is now linked with: <%= user.email %>.`,
html: `<h1>Welcome on mywebsite.fr!</h1>
<p>Your account is now linked with: <%= user.email %>.<p>`,
},
await strapi.plugins.email.services.email.sendTemplatedEmail(
{
to: user.email,
// from: is not specified, so it's the defaultFrom that will be used instead
},
emailTemplate,
{
user: _.pick(user, ['username', 'email', 'firstname', 'lastname']),
},
);
```
## Configure the plugin
### Install the provider you want

View File

@ -0,0 +1,23 @@
const subject = `Reset password`;
const html = `<p>We heard that you lost your password. Sorry about that!</p>
<p>But dont worry! You can use the following link to reset your password:</p>
<p><%= url %></p>
<p>Thanks.</p>`;
const text = `We heard that you lost your password. Sorry about that!
But dont worry! You can use the following link to reset your password:
<%= url %>
Thanks.`;
module.exports = {
subject,
text,
html,
};

View File

@ -0,0 +1,7 @@
const forgotPasswordTemplate = require('./email-templates/forgot-password');
module.exports = {
forgotPassword: {
emailTemplate: forgotPasswordTemplate,
},
};

View File

@ -1,6 +1,8 @@
'use strict';
const bcrypt = require('bcryptjs');
const _ = require('lodash');
const { getAbsoluteAdminUrl } = require('strapi-utils');
/**
* hashes a password
@ -43,15 +45,6 @@ const checkCredentials = async ({ email, password }) => {
return [null, user];
};
const resetEmailTemplate = url => `
<p>We heard that you lost your password. Sorry about that!</p>
<p>But dont worry! You can use the following link to reset your password:</p>
<p>${url}</p>
<p>Thanks.</p>`;
/**
* Send an email to the user if it exists or do nothing
* @param {Object} param params
@ -67,17 +60,23 @@ const forgotPassword = async ({ email } = {}) => {
const resetPasswordToken = strapi.admin.services.token.createToken();
await strapi.admin.services.user.updateById(user.id, { resetPasswordToken });
const url = `${strapi.config.admin.url}/auth/reset-password?code=${resetPasswordToken}`;
const body = resetEmailTemplate(url);
// Send an email to the admin.
return strapi.plugins['email'].services.email
.send({
to: user.email,
subject: 'Reset password',
text: body,
html: body,
})
const url = `${getAbsoluteAdminUrl(
strapi.config
)}/auth/reset-password?code=${resetPasswordToken}`;
return strapi.plugins.email.services.email
.sendTemplatedEmail(
{
to: user.email,
from: strapi.config.get('server.admin.forgotPassword', {}).from,
replyTo: strapi.config.get('server.admin.forgotPassword', {}).replyTo,
},
strapi.config.get('server.admin.forgotPassword', {}).emailTemplate,
{
url,
user: _.pick(user, ['email', 'firstname', 'lastname', 'username']),
}
)
.catch(err => {
// log error server side but do not disclose it to the user to avoid leaking informations
strapi.log.error(err);

View File

@ -1,12 +1,43 @@
'use strict';
const _ = require('lodash');
const getProviderConfig = () => {
return strapi.plugins.email.config;
};
const send = async options => {
return strapi.plugins.email.provider.send(options);
};
/**
* fill subject, text and html using lodash template
* @param {object} emailOptions - to, from and replyto...
* @param {object} emailTemplate - object containing attributes to fill
* @param {object} data - data used to fill the template
* @returns {{ subject, text, subject }}
*/
const sendTemplatedEmail = (emailOptions = {}, emailTemplate = {}, data = {}) => {
const attributes = ['subject', 'text', 'html'];
const missingAttributes = _.difference(attributes, Object.keys(emailTemplate));
if (missingAttributes.length > 0) {
throw new Error(
`Following attributes are missing from your email template : ${missingAttributes.join(', ')}`
);
}
const templatedAttributes = {};
for (let attribute of attributes) {
if (emailTemplate[attribute]) {
templatedAttributes[attribute] = _.template(emailTemplate[attribute])(data);
}
}
return strapi.plugins.email.provider.send({ ...emailOptions, ...templatedAttributes });
};
module.exports = {
getProviderConfig,
async send(options) {
return strapi.plugins.email.provider.send(options);
},
send,
sendTemplatedEmail,
};

View File

@ -1,6 +1,7 @@
'use strict';
const _ = require('lodash');
const findPackagePath = require('../load/package-path');
const loadFiles = require('../load/load-files');
const loadConfig = require('../load/load-config-files');
@ -8,13 +9,18 @@ const loadConfig = require('../load/load-config-files');
const mergeRoutes = (a, b, key) =>
_.isArray(a) && _.isArray(b) && key === 'routes' ? a.concat(b) : undefined;
module.exports = async () => {
module.exports = async strapi => {
const adminPath = findPackagePath('strapi-admin');
const [files, config] = await Promise.all([
loadFiles(adminPath, '!(config|node_modules|test|ee|scripts)/*.*(js|json)'),
loadConfig(adminPath),
]);
// set admin config in strapi.config.server.admin
const userAdminConfig = strapi.config.get('server.admin');
strapi.config.set('server.admin', _.merge(config.config, userAdminConfig));
// load ee files if they exist
let eeFiles = {};
let eeConfig = {};
if (process.env.STRAPI_DISABLE_EE !== 'true') {