Add page localization (#7128)

* added localized pages; added refactored page templates; adapted test cases; introduced localization test cases

* added changelog entry

* fixed test description typo

* fixed bug in PromiseRouter where headers are not added for text reponse

* added page parameters in page headers for programmatic use

* refactored tests for PublicAPIRouter

* added mustache lib for template rendering

* fixed fs.promises module reference

* fixed template placeholder typo

* changed redirect response to provide headers instead of query parameters

* fix lint

* fixed syntax errors and typos in html templates

* removed obsolete URI encoding

* added locale inferring from request body and header

* added end-to-end localizaton test

* added server option validation; refactored pages server option

* fixed invalid redirect URL for no locale matching file

* added end-to-end localizaton tests

* adapted tests to new response content

* re-added PublicAPIRouter; added PagesRouter as experimental feature

* refactored PagesRouter test structure

* added configuration option for custom path to pages

* added configuration option for custom endpoint to pages

* fixed lint

* added tests

* added a distinct page for invalid password reset link

* renamed generic page invalidLink to expiredVerificationLink

* improved HTML files documentation

* improved HTML files documentation

* changed changelog entry for experimental feature

* improved file naming to make it more descriptive

* fixed file naming and env parameter naming

* added readme entry

* fixed readme TOC - hasn't been updated in a while

* added localization with JSON resource

* added JSON localization to feature pages (password reset, email verification)

* updated readme

* updated readme

* optimized JSON localization for feature pages; added e2e test case

* fixed readme typo

* minor refactoring of existing tests

* fixed bug where Object type was not recognized as config key type

* added feature config placeholders

* prettier

* added passing locale to page config placeholder callback

* refactored passing locale to placeholder to pass test

* added config placeholder feature to README

* fixed typo in README
This commit is contained in:
Manuel
2021-02-09 14:03:57 +01:00
committed by GitHub
parent e3ed6e4600
commit 7f47b0427e
41 changed files with 4335 additions and 2903 deletions

View File

@@ -10,8 +10,9 @@ import {
IdempotencyOptions,
FileUploadOptions,
AccountLockoutOptions,
PagesOptions,
} from './Options/Definitions';
import { isBoolean } from 'lodash';
import { isBoolean, isString } from 'lodash';
function removeTrailingSlash(str) {
if (!str) {
@@ -77,6 +78,7 @@ export class Config {
idempotencyOptions,
emailVerifyTokenReuseIfValid,
fileUpload,
pages,
}) {
if (masterKey === readOnlyMasterKey) {
throw new Error('masterKey and readOnlyMasterKey should be different');
@@ -111,6 +113,61 @@ export class Config {
this.validateMaxLimit(maxLimit);
this.validateAllowHeaders(allowHeaders);
this.validateIdempotencyOptions(idempotencyOptions);
this.validatePagesOptions(pages);
}
static validatePagesOptions(pages) {
if (Object.prototype.toString.call(pages) !== '[object Object]') {
throw 'Parse Server option pages must be an object.';
}
if (pages.enableRouter === undefined) {
pages.enableRouter = PagesOptions.enableRouter.default;
} else if (!isBoolean(pages.enableRouter)) {
throw 'Parse Server option pages.enableRouter must be a boolean.';
}
if (pages.enableLocalization === undefined) {
pages.enableLocalization = PagesOptions.enableLocalization.default;
} else if (!isBoolean(pages.enableLocalization)) {
throw 'Parse Server option pages.enableLocalization must be a boolean.';
}
if (pages.localizationJsonPath === undefined) {
pages.localizationJsonPath = PagesOptions.localizationJsonPath.default;
} else if (!isString(pages.localizationJsonPath)) {
throw 'Parse Server option pages.localizationJsonPath must be a string.';
}
if (pages.localizationFallbackLocale === undefined) {
pages.localizationFallbackLocale = PagesOptions.localizationFallbackLocale.default;
} else if (!isString(pages.localizationFallbackLocale)) {
throw 'Parse Server option pages.localizationFallbackLocale must be a string.';
}
if (pages.placeholders === undefined) {
pages.placeholders = PagesOptions.placeholders.default;
} else if (
Object.prototype.toString.call(pages.placeholders) !== '[object Object]' &&
typeof pages.placeholders !== 'function'
) {
throw 'Parse Server option pages.placeholders must be an object or a function.';
}
if (pages.forceRedirect === undefined) {
pages.forceRedirect = PagesOptions.forceRedirect.default;
} else if (!isBoolean(pages.forceRedirect)) {
throw 'Parse Server option pages.forceRedirect must be a boolean.';
}
if (pages.pagesPath === undefined) {
pages.pagesPath = PagesOptions.pagesPath.default;
} else if (!isString(pages.pagesPath)) {
throw 'Parse Server option pages.pagesPath must be a string.';
}
if (pages.pagesEndpoint === undefined) {
pages.pagesEndpoint = PagesOptions.pagesEndpoint.default;
} else if (!isString(pages.pagesEndpoint)) {
throw 'Parse Server option pages.pagesEndpoint must be a string.';
}
if (pages.customUrls === undefined) {
pages.customUrls = PagesOptions.customUrls.default;
} else if (Object.prototype.toString.call(pages.customUrls) !== '[object Object]') {
throw 'Parse Server option pages.customUrls must be an object.';
}
}
static validateIdempotencyOptions(idempotencyOptions) {