commons.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import type { ColorScheme, IUser, IUserHasId } from '@growi/core';
  2. import {
  3. DevidedPagePath, Lang, AllLang,
  4. } from '@growi/core';
  5. import { GetServerSideProps, GetServerSidePropsContext } from 'next';
  6. import { SSRConfig, UserConfig } from 'next-i18next';
  7. import * as nextI18NextConfig from '^/config/next-i18next.config';
  8. import type { CrowiRequest } from '~/interfaces/crowi-request';
  9. export type CommonProps = {
  10. namespacesRequired: string[], // i18next
  11. currentPathname: string,
  12. appTitle: string,
  13. siteUrl: string,
  14. confidential: string,
  15. customTitleTemplate: string,
  16. csrfToken: string,
  17. isContainerFluid: boolean,
  18. growiVersion: string,
  19. isMaintenanceMode: boolean,
  20. redirectDestination: string | null,
  21. isDefaultLogo: boolean,
  22. currentUser?: IUser,
  23. forcedColorScheme?: ColorScheme,
  24. } & Partial<SSRConfig>;
  25. // eslint-disable-next-line max-len
  26. export const getServerSideCommonProps: GetServerSideProps<CommonProps> = async(context: GetServerSidePropsContext) => {
  27. const req = context.req as CrowiRequest<IUserHasId & any>;
  28. const { crowi, user } = req;
  29. const {
  30. appService, configManager, customizeService, attachmentService,
  31. } = crowi;
  32. const url = new URL(context.resolvedUrl, 'http://example.com');
  33. const currentPathname = decodeURIComponent(url.pathname);
  34. const isMaintenanceMode = appService.isMaintenanceMode();
  35. let currentUser;
  36. if (user != null) {
  37. currentUser = user.toObject();
  38. }
  39. // Redirect destination for page transition by next/link
  40. let redirectDestination: string | null = null;
  41. if (!crowi.aclService.isGuestAllowedToRead() && currentUser == null) {
  42. redirectDestination = '/login';
  43. }
  44. else if (!isMaintenanceMode && currentPathname === '/maintenance') {
  45. redirectDestination = '/';
  46. }
  47. else if (isMaintenanceMode && !currentPathname.match('/admin/*') && !(currentPathname === '/maintenance')) {
  48. redirectDestination = '/maintenance';
  49. }
  50. else {
  51. redirectDestination = null;
  52. }
  53. const isCustomizedLogoUploaded = await attachmentService.isBrandLogoExist();
  54. const isDefaultLogo = crowi.configManager.getConfig('crowi', 'customize:isDefaultLogo') || !isCustomizedLogoUploaded;
  55. const forcedColorScheme = crowi.customizeService.forcedColorScheme;
  56. const props: CommonProps = {
  57. namespacesRequired: ['translation'],
  58. currentPathname,
  59. appTitle: appService.getAppTitle(),
  60. siteUrl: configManager.getConfig('crowi', 'app:siteUrl'), // DON'T USE appService.getSiteUrl()
  61. confidential: appService.getAppConfidential() || '',
  62. customTitleTemplate: customizeService.customTitleTemplate,
  63. csrfToken: req.csrfToken(),
  64. isContainerFluid: configManager.getConfig('crowi', 'customize:isContainerFluid') ?? false,
  65. growiVersion: crowi.version,
  66. isMaintenanceMode,
  67. redirectDestination,
  68. currentUser,
  69. isDefaultLogo,
  70. forcedColorScheme,
  71. };
  72. return { props };
  73. };
  74. export const getNextI18NextConfig = async(
  75. // 'serverSideTranslations' method should be given from Next.js Page
  76. // because importing it in this file causes https://github.com/isaachinman/next-i18next/issues/1545
  77. serverSideTranslations: (
  78. initialLocale: string, namespacesRequired?: string[] | undefined, configOverride?: UserConfig | null, extraLocales?: string[] | false
  79. ) => Promise<SSRConfig>,
  80. context: GetServerSidePropsContext, namespacesRequired?: string[] | undefined, preloadAllLang = false,
  81. ): Promise<SSRConfig> => {
  82. const req: CrowiRequest = context.req as CrowiRequest;
  83. const { crowi, user } = req;
  84. const { configManager } = crowi;
  85. // determine language
  86. const locale = user?.lang
  87. ?? configManager.getConfig('crowi', 'app:globalLang') as Lang
  88. ?? Lang.en_US;
  89. const namespaces = ['commons'];
  90. if (namespacesRequired != null) {
  91. namespaces.push(...namespacesRequired);
  92. }
  93. // TODO: deprecate 'translation.json' in the future
  94. else {
  95. namespaces.push('translation');
  96. }
  97. return serverSideTranslations(locale, namespaces, nextI18NextConfig, preloadAllLang ? AllLang : false);
  98. };
  99. /**
  100. * Generate whole title string for the specified title
  101. * @param props
  102. * @param title
  103. */
  104. export const generateCustomTitle = (props: CommonProps, title: string): string => {
  105. return props.customTitleTemplate
  106. .replace('{{sitename}}', props.appTitle)
  107. .replace('{{pagepath}}', title)
  108. .replace('{{pagename}}', title);
  109. };
  110. /**
  111. * Generate whole title string for the specified page path
  112. * @param props
  113. * @param pagePath
  114. */
  115. export const generateCustomTitleForPage = (props: CommonProps, pagePath: string): string => {
  116. const dPagePath = new DevidedPagePath(pagePath, true, true);
  117. return props.customTitleTemplate
  118. .replace('{{sitename}}', props.appTitle)
  119. .replace('{{pagepath}}', pagePath)
  120. .replace('{{pagename}}', dPagePath.latter);
  121. };