diff --git a/functions/send-invite-email/invite-en.html b/functions/send-invite-email/invite-en.html
new file mode 100644
index 0000000..a399098
--- /dev/null
+++ b/functions/send-invite-email/invite-en.html
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{#i18n 'email.happy' }}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{#i18n 'email.hello'}}Hi, there! 🙂{{/i18n}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{#i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}}
+
+
+
+
+
+
+
+ {{#i18n 'email.why'}}This email was sent to you because your email address was entered to participate in the vote on the subject:{{/i18n}}
+
+ {{title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}}
+
+ %recipient.urlVote%
+
+
+
+
+
+
+
+ {{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}}
+
+ %recipient.urlResult%
+
+
+
+
+
+
+ {{#i18n 'email.bye'}}Good vote{{/i18n}}, {{#i18n 'common.mieuxvoter'}}Mieux Voter{{/i18n}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{#i18n email.why }}You received this email because someone invited you to vote.{{/i18n}}
+
+
+
+
+
+
+ {{#i18n mieuxvoter }}Mieux Voter{{/i18n}} - app@mieuxvoter.fr
+
+
+
+
+
+
+
+
diff --git a/functions/send-invite-email/invite-en.txt b/functions/send-invite-email/invite-en.txt
new file mode 100644
index 0000000..f4b8f60
--- /dev/null
+++ b/functions/send-invite-email/invite-en.txt
@@ -0,0 +1,19 @@
+{{i18n 'email.hello'}}Hi there! 🙂{{i18n}}
+
+{{i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{i18n}}
+
+{{i18n 'email.why'}}This email was sent to you because your email was filled out to participate in the vote on the subject:{{i18n}}
+
+{{ title }}
+
+{{i18n 'email.linkVote' }}The link for the vote is as follows:{{i18n}}
+
+%recipient.urlVote%
+
+{{i18n 'email.linkResult' }}The link that will give you the results when they are available is as follows:{{i18n}}
+
+%recipient.urlResult%
+
+{{i18n 'email.bye'}}Good vote{{i18n}}
+
+{{i18n 'common.mieuxvoter'}}Mieux Voter{{i18n}}
diff --git a/functions/send-invite-email/invite-fr.html b/functions/send-invite-email/invite-fr.html
new file mode 100644
index 0000000..d0f614b
--- /dev/null
+++ b/functions/send-invite-email/invite-fr.html
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.
+
+
+
+
+
+ Vous avez été invité·e à participer à l'élection suivante :
+
+
+
+
+
+
+
+
+
+
+
+
+ Si le lien ne fonctionne pas, vous pouvez le copier et le coller dans la barre de navigation de votre navigateur.
+
+ %recipient.urlVote%
+
+
+
+
+
+
+
+ A la fin de l'élection, vous pourrez accéder aux résultats en cliquant sur ce lien :
+
+ %recipient.urlResult%
+
+
+
+
+
+
+ Bon vote, Mieux Voter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Vous avez été invité·e à participer à l'élection suivante
+
+
+
+
+
+ Mieux Voter - app@mieuxvoter.fr
+
+
+
+
+
+
+
+
diff --git a/functions/send-invite-email/invite-fr.txt b/functions/send-invite-email/invite-fr.txt
new file mode 100644
index 0000000..9ec0f3e
--- /dev/null
+++ b/functions/send-invite-email/invite-fr.txt
@@ -0,0 +1,17 @@
+Bonjour ! 🙂
+
+Vous avez été invité·e à participer à l'élection suivante :
+
+{{ title }}
+
+Le lien pour voter est le suivant :
+
+%recipient.urlVote%
+
+A la fin de l'élection, vous pourrez accéder aux résultats en cliquant sur ce lien :
+
+%recipient.urlResult%
+
+Bon vote ! 🤗
+
+Mieux Voter
diff --git a/functions/send-invite-email/invite.html b/functions/send-invite-email/invite.html
index 14b78e8..a399098 100644
--- a/functions/send-invite-email/invite.html
+++ b/functions/send-invite-email/invite.html
@@ -130,7 +130,9 @@
@@ -144,7 +146,7 @@
{{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}}
- {{invitation_url}}
+ %recipient.urlVote%
@@ -152,9 +154,9 @@
- {{#i18n email.linkResult }}The results will be available with the following link when the vote is finished:{{/i18n}}
+ {{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}}
- {{result_url}}
+ %recipient.urlResult%
diff --git a/functions/send-invite-email/send-invite-email.js b/functions/send-invite-email/send-invite-email.js
index 39fc630..6c4321b 100755
--- a/functions/send-invite-email/send-invite-email.js
+++ b/functions/send-invite-email/send-invite-email.js
@@ -34,57 +34,72 @@ const err = {
};
// setup i18n
-i18next.use(Backend).init({
- lng: "en",
- ns: ["emailInvite", "common"],
- defaultNS: "emailInvite",
- fallbackNS: "common",
- debug: false,
- fallbackLng: ["en", "fr"],
- backend: {
- backends: [FSBackend, HttpApi],
- backendOptions: [{ loadPath: "/public/locales/{{lng}}/{{ns}}.json" }, {}],
- },
-});
+// i18next.use(Backend).init({
+// lng: "fr",
+// ns: ["emailInvite", "common"],
+// defaultNS: "emailInvite",
+// fallbackNS: "common",
+// debug: false,
+// fallbackLng: ["fr"],
+// backend: {
+// backends: [FSBackend, HttpApi],
+// backendOptions: [{ loadPath: "/public/locales/{{lng}}/{{ns}}.json" }, {}],
+// },
+// });
// setup the template engine
// See https://github.com/UUDigitalHumanitieslab/handlebars-i18next
-function extend(target, ...sources) {
- sources.forEach((source) => {
- if (source)
- for (let key in source) {
- target[key] = source[key];
- }
- });
- return target;
-}
-Handlebars.registerHelper("i18n", function (key, { hash, data, fn }) {
- let parsed = {};
- const jsonKeys = [
- "lngs",
- "fallbackLng",
- "ns",
- "postProcess",
- "interpolation",
- ];
- jsonKeys.forEach((key) => {
- if (hash[key]) {
- parsed[key] = JSON.parse(hash[key]);
- delete hash[key];
- }
- });
- let options = extend({}, data.root.i18next, hash, parsed, {
- returnObjects: false,
- });
- let replace = (options.replace = extend({}, this, options.replace, hash));
- delete replace.i18next; // may creep in if this === data.root
- if (fn) options.defaultValue = fn(replace);
- return new Handlebars.SafeString(i18next.t(key, options));
-});
-const txtStr = fs.readFileSync(__dirname + "/invite.txt").toString();
-const txtTemplate = Handlebars.compile(txtStr);
-const htmlStr = fs.readFileSync(__dirname + "/invite.html").toString();
-const htmlTemplate = Handlebars.compile(htmlStr);
+// function extend(target, ...sources) {
+// sources.forEach((source) => {
+// if (source)
+// for (let key in source) {
+// target[key] = source[key];
+// }
+// });
+// return target;
+// }
+// Handlebars.registerHelper("i18n", function (key, { hash, data, fn }) {
+// let parsed = {};
+// const jsonKeys = [
+// "lngs",
+// "fallbackLng",
+// "ns",
+// "postProcess",
+// "interpolation",
+// ];
+// jsonKeys.forEach((key) => {
+// if (hash[key]) {
+// parsed[key] = JSON.parse(hash[key]);
+// delete hash[key];
+// }
+// });
+// let options = extend({}, data.root.i18next, hash, parsed, {
+// returnObjects: false,
+// });
+// let replace = (options.replace = extend({}, this, options.replace, hash));
+// delete replace.i18next; // may creep in if this === data.root
+// if (fn) options.defaultValue = fn(replace);
+// return new Handlebars.SafeString(i18next.t(key, options));
+// });
+// const txtStr = fs.readFileSync(__dirname + "/invite.txt").toString();
+const txtStr = {
+ en: fs.readFileSync(__dirname + "/invite-en.txt").toString(),
+ fr: fs.readFileSync(__dirname + "/invite-fr.txt").toString(),
+};
+const txtTemplate = {
+ en: Handlebars.compile(txtStr.en),
+ fr: Handlebars.compile(txtStr.fr),
+};
+const htmlStr = {
+ en: fs.readFileSync(__dirname + "/invite-en.html").toString(),
+ fr: fs.readFileSync(__dirname + "/invite-fr.html").toString(),
+};
+const htmlTemplate = {
+ en: Handlebars.compile(htmlStr.en),
+ fr: Handlebars.compile(htmlStr.fr),
+};
+
+const test = Handlebars.compile("test");
const sendMail = async (event) => {
if (event.httpMethod !== "POST") {
@@ -96,23 +111,24 @@ const sendMail = async (event) => {
}
const data = JSON.parse(event.body);
- if (!data.recipientVariables || !data.title) {
+ if (!data.recipientVariables || !data.title || !data.locale) {
return {
statusCode: 422,
body: "Recipient variables and title are required.",
};
}
- i18next.changeLanguage(data.locale || "en");
+ // i18next.changeLanguage(data.locale);
const templateData = {
title: data.title,
};
const mailgunData = {
- from: `${i18next.t("Mieux Voter")} `,
+ // from: `${i18next.t("Mieux Voter")} `,
+ from: '"Mieux Voter" ',
to: Object.keys(data.recipientVariables),
- text: txtTemplate(templateData),
- html: htmlTemplate(templateData),
+ text: txtTemplate.fr(templateData),
+ html: htmlTemplate.fr(templateData),
subject: data.title,
"h:Reply-To": "app@mieuxvoter.fr",
"recipient-variables": JSON.stringify(data.recipientVariables),
@@ -121,7 +137,6 @@ const sendMail = async (event) => {
const res = mg.messages
.create("mg.app.mieuxvoter.fr", mailgunData)
.then((msg) => {
- console.log(msg);
return success;
}) // logs response data
.catch((err) => {
diff --git a/package-lock.json b/package-lock.json
index 4266a8b..417cfea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2054,6 +2054,11 @@
"supports-color": "^5.3.0"
}
},
+ "charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+ },
"chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
@@ -2164,6 +2169,22 @@
}
}
},
+ "cookies": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
+ "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
+ "requires": {
+ "depd": "~2.0.0",
+ "keygrip": "~1.1.0"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ }
+ }
+ },
"core-js": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz",
@@ -2214,6 +2235,11 @@
"yaml": "^1.10.0"
}
},
+ "crc-32": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.2.2.tgz",
+ "integrity": "sha1-9EBWigxqRfDuu7V8M7FWAV9PBLY="
+ },
"create-ecdh": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
@@ -2263,6 +2289,11 @@
"node-fetch": "2.6.1"
}
},
+ "crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+ },
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@@ -2783,6 +2814,11 @@
}
}
},
+ "handlebars-i18next": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/handlebars-i18next/-/handlebars-i18next-1.0.1.tgz",
+ "integrity": "sha512-m5sxMthNYFXDYkj7r1MhSiW4tqfIfEKYlUn4TtdXSJT+r6YA9zQddd01BGOgDj3TmjJQc6bDiUQgQVEkluaSdg=="
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -2899,6 +2935,11 @@
}
}
},
+ "i18next-client": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/i18next-client/-/i18next-client-1.10.3.tgz",
+ "integrity": "sha1-dtA1NVftkNHnqHdU1QBNP3gB/ek="
+ },
"i18next-fs-backend": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz",
@@ -2920,6 +2961,34 @@
"@babel/runtime": "^7.4.5"
}
},
+ "i18next-text": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/i18next-text/-/i18next-text-0.5.6.tgz",
+ "integrity": "sha1-nxPB5DKmoOYaRNtYINeS/IizRmI=",
+ "requires": {
+ "crc-32": "^0.2.2",
+ "i18next": "^1.7.10",
+ "md5": "^2.0.0",
+ "sha1": "^1.1.0"
+ },
+ "dependencies": {
+ "i18next": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-1.10.6.tgz",
+ "integrity": "sha1-/d2LSRUCxIlnpiljvHIv+JfN3qA=",
+ "requires": {
+ "cookies": ">= 0.2.2",
+ "i18next-client": "1.10.3",
+ "json5": "^0.2.0"
+ }
+ },
+ "json5": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz",
+ "integrity": "sha1-ttcDXHDEVw+IPH7cdZ3jrgPbM0M="
+ }
+ }
+ },
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
@@ -2989,6 +3058,11 @@
"call-bind": "^1.0.0"
}
},
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
@@ -3158,6 +3232,14 @@
"minimist": "^1.2.0"
}
},
+ "keygrip": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+ "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
+ "requires": {
+ "tsscmp": "1.0.6"
+ }
+ },
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -3257,6 +3339,16 @@
"semver": "^6.0.0"
}
},
+ "md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "requires": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -4208,6 +4300,15 @@
"safe-buffer": "^5.0.1"
}
},
+ "sha1": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
+ "integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=",
+ "requires": {
+ "charenc": ">= 0.0.1",
+ "crypt": ">= 0.0.1"
+ }
+ },
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@@ -4445,6 +4546,11 @@
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
},
+ "tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
+ },
"tty-browserify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
diff --git a/package.json b/package.json
index 75b4e1b..00cfff8 100644
--- a/package.json
+++ b/package.json
@@ -22,11 +22,13 @@
"dotenv": "^8.6.0",
"form-data": "^4.0.0",
"handlebars": "^4.7.7",
+ "handlebars-i18next": "^1.0.1",
"i18next": "^20.2.2",
"i18next-chained-backend": "^2.1.0",
"i18next-fs-backend": "^1.1.1",
"i18next-http-backend": "^1.2.4",
"i18next-localstorage-backend": "^3.1.2",
+ "i18next-text": "^0.5.6",
"mailgun.js": "^3.3.2",
"next": "^10.2.0",
"next-i18next": "^8.2.0",
diff --git a/pages/new/confirm/[pid].jsx b/pages/new/confirm/[pid].jsx
index daf7582..d33439b 100644
--- a/pages/new/confirm/[pid].jsx
+++ b/pages/new/confirm/[pid].jsx
@@ -147,7 +147,7 @@ const ConfirmElection = ({
{participate}
-
+
{t("resource.resultsBtn")}
diff --git a/pages/new/index.js b/pages/new/index.js
index 05daa1c..9ccce37 100644
--- a/pages/new/index.js
+++ b/pages/new/index.js
@@ -165,7 +165,7 @@ const CreateElection = (props) => {
title,
candidates.map((c) => c.label).filter((c) => c !== ""),
{
- emails,
+ mails: emails,
numGrades,
start: start.getTime() / 1000,
finish: finish.getTime() / 1000,
diff --git a/services/api.js b/services/api.js
index 975a8a6..8f77576 100644
--- a/services/api.js
+++ b/services/api.js
@@ -18,6 +18,10 @@ const sendInviteMail = (res) => {
*/
const { title, mails, tokens, locale } = res;
+ if (!mails || !mails.length) {
+ throw new Error("No emails are provided.");
+ }
+
if (mails.length !== tokens.length) {
throw new Error("The number of emails differ from the number of tokens");
}
@@ -49,12 +53,7 @@ const sendInviteMail = (res) => {
}),
});
- return Promise.all([
- new Promise((resolve, reject) => {
- resolve(res);
- }),
- req,
- ]);
+ return req.then((any) => res);
};
const createElection = (
@@ -64,7 +63,7 @@ const createElection = (
/**
* Create an election from its title, its candidates and a bunch of options
*/
- emails,
+ mails,
numGrades,
start,
finish,
@@ -77,6 +76,7 @@ const createElection = (
const endpoint = new URL(api.routesServer.setElection, api.urlServer);
console.log(endpoint.href);
+ const onInvitationOnly = mails && mails.length > 0;
fetch(endpoint.href, {
method: "POST",
@@ -86,9 +86,9 @@ const createElection = (
body: JSON.stringify({
title,
candidates,
- on_invitation_only: emails.length > 0,
+ on_invitation_only: onInvitationOnly,
num_grades: numGrades,
- elector_emails: emails || [],
+ elector_emails: mails || [],
start_at: start,
finish_at: finish,
select_language: locale || "en",
@@ -103,8 +103,12 @@ const createElection = (
}
return response.json();
})
- .then((res) => sendInviteMail({ locale, mails: emails || [], ...res }))
- .then((res) => res[0]) // remove response from mail invitations
+ .then((res) => {
+ if (onInvitationOnly) {
+ return sendInviteMail({ locale, mails: mails, ...res });
+ }
+ return res;
+ })
.then(successCallback)
.catch(failureCallback || console.log);
};