You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

templating.js 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. const handlebars = require("handlebars");
  2. const fs = require("fs");
  3. const path = require("path");
  4. const url = require("url");
  5. const accept_language_parser = require("accept-language-parser");
  6. const client_config = require("./client_configuration");
  7. /**
  8. * Associations from language to translation dictionnaries
  9. * @const
  10. * @type {object}
  11. */
  12. const TRANSLATIONS = JSON.parse(
  13. fs.readFileSync(path.join(__dirname, "translations.json"))
  14. );
  15. const languages = Object.keys(TRANSLATIONS);
  16. handlebars.registerHelper({
  17. json: JSON.stringify.bind(JSON),
  18. });
  19. function findBaseUrl(req) {
  20. var proto =
  21. req.headers["X-Forwarded-Proto"] ||
  22. (req.connection.encrypted ? "https" : "http");
  23. var host = req.headers["X-Forwarded-Host"] || req.headers.host;
  24. return proto + "://" + host;
  25. }
  26. class Template {
  27. constructor(path) {
  28. const contents = fs.readFileSync(path, { encoding: "utf8" });
  29. this.template = handlebars.compile(contents);
  30. }
  31. parameters(parsedUrl, request) {
  32. const accept_language_str = parsedUrl.query.lang || request.headers["accept-language"];
  33. const accept_languages = accept_language_parser.parse(accept_language_str);
  34. const opts = { loose: true };
  35. let language =
  36. accept_language_parser.pick(languages, accept_languages, opts) || "en";
  37. // The loose matcher returns the first language that partially matches, so we need to
  38. // check if the preferred language is supported to return it
  39. if (accept_languages.length > 0) {
  40. const preferred_language = accept_languages[0].code + "-" + accept_languages[0].region;
  41. if (languages.includes(preferred_language)) {
  42. language = preferred_language;
  43. }
  44. }
  45. const translations = TRANSLATIONS[language] || {};
  46. const configuration = client_config || {};
  47. const prefix = request.url.split("/boards/")[0].substr(1);
  48. const baseUrl = findBaseUrl(request) + (prefix ? prefix + "/" : "");
  49. return { baseUrl, languages, language, translations, configuration };
  50. }
  51. serve(request, response) {
  52. const parsedUrl = url.parse(request.url, true);
  53. const parameters = this.parameters(parsedUrl, request);
  54. var body = this.template(parameters);
  55. var headers = {
  56. "Content-Length": Buffer.byteLength(body),
  57. "Content-Type": "text/html",
  58. "Cache-Control": "public, max-age=3600",
  59. };
  60. if (!parsedUrl.query.lang) {
  61. headers["Vary"] = "Accept-Language";
  62. }
  63. response.writeHead(200, headers);
  64. response.end(body);
  65. }
  66. }
  67. class BoardTemplate extends Template {
  68. parameters(parsedUrl, request) {
  69. const params = super.parameters(parsedUrl, request);
  70. const parts = parsedUrl.pathname.split("boards/", 2);
  71. const boardUriComponent = parts[1];
  72. params["boardUriComponent"] = boardUriComponent;
  73. params["board"] = decodeURIComponent(boardUriComponent);
  74. params["hideMenu"] = parsedUrl.query.hideMenu == "true" || false;
  75. return params;
  76. }
  77. }
  78. module.exports = { Template, BoardTemplate };