瀏覽代碼

Merge branch 'support/apply-nextjs-2' into imprv/98451-PageEditorByHackmd.jsx-FC-TS

yuken 3 年之前
父節點
當前提交
6c6ff40762
共有 100 個文件被更改,包括 686 次插入986 次删除
  1. 22 0
      .eslintrc.js
  2. 27 18
      .github/workflows/ci-app-prod.yml
  3. 17 8
      .github/workflows/ci-app.yml
  4. 10 6
      .github/workflows/reusable-app-prod.yml
  5. 3 0
      .gitignore
  6. 45 25
      .vscode/launch.json
  7. 16 11
      package.json
  8. 0 1
      packages/app/.env.development
  9. 5 0
      packages/app/.eslintignore
  10. 12 3
      packages/app/.eslintrc.js
  11. 5 2
      packages/app/.gitignore
  12. 1 0
      packages/app/_obsolete/config/webpack.common.js
  13. 1 0
      packages/app/_obsolete/config/webpack.dev.dll.js
  14. 1 0
      packages/app/_obsolete/config/webpack.dev.js
  15. 1 0
      packages/app/_obsolete/config/webpack.prod.js
  16. 9 9
      packages/app/_obsolete/src/client/admin.jsx
  17. 4 15
      packages/app/_obsolete/src/client/app.jsx
  18. 2 5
      packages/app/_obsolete/src/client/base.jsx
  19. 5 0
      packages/app/_obsolete/src/client/boot.js
  20. 0 0
      packages/app/_obsolete/src/client/installer.jsx
  21. 0 0
      packages/app/_obsolete/src/client/nologin.jsx
  22. 0 0
      packages/app/_obsolete/src/client/plugin.js
  23. 5 0
      packages/app/_obsolete/src/util/i18n.js
  24. 0 0
      packages/app/_obsolete/src/util/old-ios.js
  25. 0 56
      packages/app/bin/generate-plugin-definitions-source.ts
  26. 6 1
      packages/app/config/migrate-mongo-config.js
  27. 27 0
      packages/app/config/next-i18next.config.ts
  28. 1 2
      packages/app/docker/Dockerfile
  29. 8 1
      packages/app/jest.config.js
  30. 5 0
      packages/app/next-env.d.ts
  31. 110 0
      packages/app/next.config.js
  32. 60 72
      packages/app/package.json
  33. 0 0
      packages/app/public/static/locales/en_US/admin.json
  34. 10 1
      packages/app/public/static/locales/en_US/translation.json
  35. 0 2
      packages/app/public/static/locales/index.js
  36. 0 0
      packages/app/public/static/locales/ja_JP/admin.json
  37. 10 1
      packages/app/public/static/locales/ja_JP/translation.json
  38. 0 0
      packages/app/public/static/locales/zh_CN/admin.json
  39. 10 1
      packages/app/public/static/locales/zh_CN/translation.json
  40. 0 7
      packages/app/resource/cdn-manifests.js
  41. 0 3
      packages/app/resource/locales/en_US/sandbox.md
  42. 1 1
      packages/app/resource/locales/en_US/welcome.md
  43. 0 3
      packages/app/resource/locales/ja_JP/sandbox.md
  44. 1 1
      packages/app/resource/locales/ja_JP/welcome.md
  45. 0 3
      packages/app/resource/locales/zh_CN/sandbox.md
  46. 1 1
      packages/app/resource/locales/zh_CN/welcome.md
  47. 0 9
      packages/app/src/client/boot.js
  48. 0 2
      packages/app/src/client/legacy/crowi.js
  49. 0 0
      packages/app/src/client/legacy/thirdparty-js/waves.js
  50. 1 1
      packages/app/src/client/models/BootstrapGrid.js
  51. 2 2
      packages/app/src/client/models/Linker.js
  52. 4 4
      packages/app/src/client/models/MarkdownTable.js
  53. 5 4
      packages/app/src/client/services/AdminAppContainer.js
  54. 5 6
      packages/app/src/client/services/AdminBasicSecurityContainer.js
  55. 4 53
      packages/app/src/client/services/AdminCustomizeContainer.js
  56. 5 0
      packages/app/src/client/services/AdminExternalAccountsContainer.js
  57. 5 5
      packages/app/src/client/services/AdminGeneralSecurityContainer.js
  58. 5 2
      packages/app/src/client/services/AdminGitHubSecurityContainer.js
  59. 5 2
      packages/app/src/client/services/AdminGoogleSecurityContainer.js
  60. 5 0
      packages/app/src/client/services/AdminHomeContainer.js
  61. 6 4
      packages/app/src/client/services/AdminImportContainer.js
  62. 6 4
      packages/app/src/client/services/AdminLdapSecurityContainer.js
  63. 5 0
      packages/app/src/client/services/AdminLocalSecurityContainer.js
  64. 6 4
      packages/app/src/client/services/AdminMarkDownContainer.js
  65. 5 0
      packages/app/src/client/services/AdminNotificationContainer.js
  66. 6 6
      packages/app/src/client/services/AdminOidcSecurityContainer.js
  67. 6 6
      packages/app/src/client/services/AdminSamlSecurityContainer.js
  68. 6 3
      packages/app/src/client/services/AdminSlackIntegrationLegacyContainer.js
  69. 1 24
      packages/app/src/client/services/AdminSocketIoContainer.js
  70. 6 6
      packages/app/src/client/services/AdminTwitterSecurityContainer.js
  71. 0 197
      packages/app/src/client/services/AdminUserGroupDetailContainer.js
  72. 5 4
      packages/app/src/client/services/AdminUsersContainer.js
  73. 12 13
      packages/app/src/client/services/AppContainer.js
  74. 34 51
      packages/app/src/client/services/ContextExtractor.tsx
  75. 0 24
      packages/app/src/client/services/EditorContainer.js
  76. 1 3
      packages/app/src/client/services/PageContainer.js
  77. 0 50
      packages/app/src/client/services/SocketIoContainer.js
  78. 1 2
      packages/app/src/client/services/page-operation.ts
  79. 2 22
      packages/app/src/client/util/apiv1-client.ts
  80. 4 29
      packages/app/src/client/util/apiv3-client.ts
  81. 0 73
      packages/app/src/client/util/color-scheme.js
  82. 1 5
      packages/app/src/client/util/locale-utils.ts
  83. 11 9
      packages/app/src/components/Admin/AdminHome/AdminHome.jsx
  84. 1 1
      packages/app/src/components/Admin/AdminHome/InstalledPluginTable.jsx
  85. 24 18
      packages/app/src/components/Admin/App/AppSetting.jsx
  86. 0 51
      packages/app/src/components/Admin/App/AppSettingsPage.jsx
  87. 23 2
      packages/app/src/components/Admin/App/AppSettingsPageContents.tsx
  88. 1 1
      packages/app/src/components/Admin/App/AwsSetting.jsx
  89. 1 1
      packages/app/src/components/Admin/App/ConfirmModal.tsx
  90. 2 2
      packages/app/src/components/Admin/App/FileUploadSetting.tsx
  91. 1 1
      packages/app/src/components/Admin/App/GcsSettings.jsx
  92. 1 1
      packages/app/src/components/Admin/App/MailSetting.tsx
  93. 1 1
      packages/app/src/components/Admin/App/MaintenanceMode.tsx
  94. 1 1
      packages/app/src/components/Admin/App/PluginSetting.tsx
  95. 1 2
      packages/app/src/components/Admin/App/SesSetting.tsx
  96. 1 1
      packages/app/src/components/Admin/App/SiteUrlSetting.tsx
  97. 2 3
      packages/app/src/components/Admin/App/SmtpSetting.tsx
  98. 1 1
      packages/app/src/components/Admin/App/V5PageMigration.tsx
  99. 2 0
      packages/app/src/components/Admin/AuditLog/DateRangePicker.tsx
  100. 12 11
      packages/app/src/components/Admin/Common/AdminNavigation.jsx

+ 22 - 0
.eslintrc.js

@@ -35,6 +35,18 @@ module.exports = {
             group: 'parent',
             group: 'parent',
             position: 'before',
             position: 'before',
           },
           },
+          {
+            pattern: '*.css',
+            group: 'type',
+            patternOptions: { matchBase: true },
+            position: 'after',
+          },
+          {
+            pattern: '*.scss',
+            group: 'type',
+            patternOptions: { matchBase: true },
+            position: 'after',
+          },
         ],
         ],
         alphabetize: {
         alphabetize: {
           order: 'asc',
           order: 'asc',
@@ -44,6 +56,7 @@ module.exports = {
       },
       },
     ],
     ],
     '@typescript-eslint/no-explicit-any': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/explicit-module-boundary-types': 'off',
     indent: [
     indent: [
       'error',
       'error',
       2,
       2,
@@ -68,4 +81,13 @@ module.exports = {
       },
       },
     ]],
     ]],
   },
   },
+  overrides: [
+    {
+      // enable the rule specifically for TypeScript files
+      files: ['*.ts', '*.tsx'],
+      rules: {
+        '@typescript-eslint/explicit-module-boundary-types': ['error'],
+      },
+    },
+  ],
 };
 };

+ 27 - 18
.github/workflows/ci-app-prod.yml

@@ -3,7 +3,8 @@ name: Node CI for app production
 on:
 on:
   push:
   push:
     branches:
     branches:
-      - master
+      # - master
+      - support/apply-nextjs-2
     paths:
     paths:
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/reusable-app-prod.yml
       - .github/workflows/reusable-app-prod.yml
@@ -12,13 +13,16 @@ on:
       - yarn.lock
       - yarn.lock
       - packages/app/**
       - packages/app/**
       - '!packages/app/docker/**'
       - '!packages/app/docker/**'
+      - packages/codemirror-textlint/**
       - packages/core/**
       - packages/core/**
+      - packages/remark-growi-plugin/**
       - packages/slack/**
       - packages/slack/**
       - packages/ui/**
       - packages/ui/**
       - packages/plugin-**
       - packages/plugin-**
   pull_request:
   pull_request:
     branches:
     branches:
-        - master
+      # - master
+      - support/apply-nextjs-2
     types: [opened, reopened, synchronize]
     types: [opened, reopened, synchronize]
     paths:
     paths:
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/ci-app-prod.yml
@@ -28,7 +32,9 @@ on:
       - yarn.lock
       - yarn.lock
       - packages/app/**
       - packages/app/**
       - '!packages/app/docker/**'
       - '!packages/app/docker/**'
+      - packages/codemirror-textlint/**
       - packages/core/**
       - packages/core/**
+      - packages/remark-growi-plugin/**
       - packages/slack/**
       - packages/slack/**
       - packages/ui/**
       - packages/ui/**
       - packages/plugin-**
       - packages/plugin-**
@@ -36,7 +42,8 @@ on:
 jobs:
 jobs:
 
 
   test-prod-node14:
   test-prod-node14:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    # uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/apply-nextjs-2
     with:
     with:
       node-version: 14.x
       node-version: 14.x
       skip-cypress: true
       skip-cypress: true
@@ -45,28 +52,30 @@ jobs:
 
 
 
 
   test-prod-node16:
   test-prod-node16:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    # uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@support/apply-nextjs-2
     with:
     with:
       node-version: 16.x
       node-version: 16.x
-      skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+      # skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+      skip-cypress: true
       cypress-report-artifact-name: Cypress report
       cypress-report-artifact-name: Cypress report
     secrets:
     secrets:
       SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
       SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
 
 
-  run-reg-suit-node16:
-    needs: [test-prod-node16]
+  # run-reg-suit-node16:
+  #   needs: [test-prod-node16]
 
 
-    uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@master
+  #   uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@master
 
 
-    if: always()
+  #   if: always()
 
 
-    with:
-      node-version: 16.x
-      skip-reg-suit: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
-      cypress-report-artifact-name: Cypress report
-    secrets:
-      REG_NOTIFY_GITHUB_PLUGIN_CLIENTID: ${{ secrets.REG_NOTIFY_GITHUB_PLUGIN_CLIENTID }}
-      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
-      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
-      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
+  #   with:
+  #     node-version: 16.x
+  #     skip-reg-suit: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) && contains( github.event.pull_request.labels.*.name, 'github_actions' ) }}
+  #     cypress-report-artifact-name: Cypress report
+  #   secrets:
+  #     REG_NOTIFY_GITHUB_PLUGIN_CLIENTID: ${{ secrets.REG_NOTIFY_GITHUB_PLUGIN_CLIENTID }}
+  #     AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+  #     AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+  #     SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

+ 17 - 8
.github/workflows/ci-app.yml

@@ -14,7 +14,9 @@ on:
       - yarn.lock
       - yarn.lock
       - packages/app/**
       - packages/app/**
       - '!packages/app/docker/**'
       - '!packages/app/docker/**'
+      - packages/codemirror-textlint/**
       - packages/core/**
       - packages/core/**
+      - packages/remark-growi-plugin/**
       - packages/slack/**
       - packages/slack/**
       - packages/ui/**
       - packages/ui/**
       - packages/plugin-*/**
       - packages/plugin-*/**
@@ -53,10 +55,10 @@ jobs:
 
 
       - name: lerna run lint for plugins
       - name: lerna run lint for plugins
         run: |
         run: |
-          yarn lerna run lint --scope @growi/plugin-*
+          yarn lerna run lint --scope @growi/remark-growi-plugin --scope @growi/plugin-*
       - name: lerna run lint for app
       - name: lerna run lint for app
         run: |
         run: |
-          yarn lerna run lint --scope @growi/app --scope @growi/codemirror-textlint --scope @growi/core --scope @growi/ui
+          yarn lerna run lint --scope @growi/app --scope @growi/codemirror-textlint --scope @growi/core --scope @growi/slack --scope @growi/ui
 
 
       - name: Slack Notification
       - name: Slack Notification
         uses: weseek/ghaction-slack-notification@master
         uses: weseek/ghaction-slack-notification@master
@@ -105,7 +107,11 @@ jobs:
         run: |
         run: |
           npx lerna bootstrap -- --frozen-lockfile
           npx lerna bootstrap -- --frozen-lockfile
 
 
-      - name: yarn test
+      - name: lerna run test for plugins
+        run: |
+          yarn lerna run test --scope @growi/remark-growi-plugin --scope @growi/plugin-*
+
+      - name: Test app
         working-directory: ./packages/app
         working-directory: ./packages/app
         run: |
         run: |
           yarn test:ci --selectProjects unit server ; yarn test:ci --selectProjects server-v5
           yarn test:ci --selectProjects unit server ; yarn test:ci --selectProjects server-v5
@@ -116,7 +122,9 @@ jobs:
         uses: actions/upload-artifact@v3
         uses: actions/upload-artifact@v3
         with:
         with:
           name: Coverage Report
           name: Coverage Report
-          path: packages/app/coverage
+          path: |
+            packages/app/coverage
+            packages/remark-growi-plugin/coverage
 
 
       - name: Slack Notification
       - name: Slack Notification
         uses: weseek/ghaction-slack-notification@master
         uses: weseek/ghaction-slack-notification@master
@@ -150,16 +158,17 @@ jobs:
           cache: 'yarn'
           cache: 'yarn'
           cache-dependency-path: '**/yarn.lock'
           cache-dependency-path: '**/yarn.lock'
 
 
-      - name: Cache/Restore node_modules
+      - name: Cache/Restore node_modules and next cache files
         id: cache-dependencies
         id: cache-dependencies
         uses: actions/cache@v3
         uses: actions/cache@v3
         with:
         with:
           path: |
           path: |
             **/node_modules
             **/node_modules
-          key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('packages/app/package.json') }}
+            ${{ github.workspace }}/packages/app/.next/cache
+          key: dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('packages/app/package.json') }}
           restore-keys: |
           restore-keys: |
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+            dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
+            dev-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: lerna bootstrap
       - name: lerna bootstrap
         run: |
         run: |

+ 10 - 6
.github/workflows/reusable-app-prod.yml

@@ -49,6 +49,7 @@ jobs:
     - name: Remove unnecessary packages
     - name: Remove unnecessary packages
       run: |
       run: |
         rm -rf packages/slackbot-proxy
         rm -rf packages/slackbot-proxy
+        rm -f "node_modules/@growi/slackbot-proxy"
 
 
     - name: Build
     - name: Build
       run: |
       run: |
@@ -59,29 +60,31 @@ jobs:
     - name: Archive production files
     - name: Archive production files
       id: archive-prod-files
       id: archive-prod-files
       run: |
       run: |
-        tar -cf production.tar \
+        tar -zcf production.tar.gz \
           package.json \
           package.json \
+          packages/app/.next \
           packages/app/config \
           packages/app/config \
           packages/app/public \
           packages/app/public \
           packages/app/resource \
           packages/app/resource \
           packages/app/tmp \
           packages/app/tmp \
-          packages/app/migrate-mongo-config.js \
           packages/app/.env.production* \
           packages/app/.env.production* \
           packages/*/package.json \
           packages/*/package.json \
           packages/*/dist
           packages/*/dist
-        echo ::set-output name=file::production.tar
+        echo ::set-output name=file::production.tar.gz
 
 
     - name: Upload production files as artifact
     - name: Upload production files as artifact
       uses: actions/upload-artifact@v3
       uses: actions/upload-artifact@v3
       with:
       with:
-        name: Production Files
+        name: Production Files (node${{ inputs.node-version }})
         path: ${{ steps.archive-prod-files.outputs.file }}
         path: ${{ steps.archive-prod-files.outputs.file }}
 
 
     - name: Upload report as artifact
     - name: Upload report as artifact
       uses: actions/upload-artifact@v3
       uses: actions/upload-artifact@v3
       with:
       with:
         name: Bundle Analyzing Report
         name: Bundle Analyzing Report
-        path: packages/app/report/bundle-analyzer.html
+        path: |
+          packages/app/.next/analyze/client.html
+          packages/app/.next/analyze/server.html
 
 
     - name: Slack Notification
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
       uses: weseek/ghaction-slack-notification@master
@@ -143,6 +146,7 @@ jobs:
     - name: Remove unnecessary packages
     - name: Remove unnecessary packages
       run: |
       run: |
         rm -rf packages/slackbot-proxy
         rm -rf packages/slackbot-proxy
+        rm -f "node_modules/@growi/slackbot-proxy"
 
 
     - name: lerna bootstrap --production
     - name: lerna bootstrap --production
       run: |
       run: |
@@ -151,7 +155,7 @@ jobs:
     - name: Download production files artifact
     - name: Download production files artifact
       uses: actions/download-artifact@v3
       uses: actions/download-artifact@v3
       with:
       with:
-        name: Production Files
+        name: Production Files (node${{ inputs.node-version }})
 
 
     - name: Extract procution files artifact
     - name: Extract procution files artifact
       run: |
       run: |

+ 3 - 0
.gitignore

@@ -26,6 +26,9 @@ yarn-error.log*
 .env.test.local
 .env.test.local
 .env.production.local
 .env.production.local
 
 
+# typescript
+*.tsbuildinfo
+
 # IDE, dev #
 # IDE, dev #
 .idea
 .idea
 *.orig
 *.orig

+ 45 - 25
.vscode/launch.json

@@ -1,37 +1,57 @@
 {
 {
-    // IntelliSense を使用して利用可能な属性を学べます。
-    // 既存の属性の説明をホバーして表示します。
-    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
     "version": "0.2.0",
     "version": "0.2.0",
     "configurations": [
     "configurations": [
       {
       {
-        "type": "node",
+        "type": "pwa-node",
         "request": "attach",
         "request": "attach",
         "name": "Debug: Attach Debugger to Server",
         "name": "Debug: Attach Debugger to Server",
-        "port": 9229
+        "port": 9229,
+        "cwd": "${workspaceFolder}/packages/app",
+        "sourceMapPathOverrides": {
+          "webpack://@growi/app/*": "${workspaceFolder}/packages/app/*"
+        }
+      },
+      {
+        "type": "pwa-node",
+        "request": "launch",
+        "name": "Debug: Current File",
+        "skipFiles": [
+          "<node_internals>/**"
+        ],
+        "console": "integratedTerminal",
+        "cwd": "${fileDirname}",
+        "runtimeExecutable": "yarn",
+        "runtimeArgs": [
+          "ts-node",
+          "${file}"
+        ]
       },
       },
       {
       {
-        "type": "node",
+        "type": "pwa-node",
         "request": "launch",
         "request": "launch",
         "name": "Debug: Server",
         "name": "Debug: Server",
         "cwd": "${workspaceFolder}/packages/app",
         "cwd": "${workspaceFolder}/packages/app",
-        "runtimeExecutable": "npm",
+        "runtimeExecutable": "yarn",
         "runtimeArgs": [
         "runtimeArgs": [
-          "run",
-          "dev:server"
+          "dev"
+        ],
+        "skipFiles": [
+          "<node_internals>/**"
         ],
         ],
-        "port": 9229,
         "restart": true,
         "restart": true,
         "console": "integratedTerminal",
         "console": "integratedTerminal",
-        "internalConsoleOptions": "neverOpen"
+        "internalConsoleOptions": "neverOpen",
+        "sourceMapPathOverrides": {
+          "webpack://@growi/app/*": "${workspaceFolder}/packages/app/*"
+        }
       },
       },
       {
       {
-        "type": "chrome",
+        "type": "pwa-chrome",
         "request": "launch",
         "request": "launch",
         "name": "Debug: Chrome",
         "name": "Debug: Chrome",
         "sourceMaps": true,
         "sourceMaps": true,
         "sourceMapPathOverrides": {
         "sourceMapPathOverrides": {
-          "webpack:///*": "${workspaceFolder}/packages/app/*"
+          "webpack://_N_E/*": "${workspaceFolder}/packages/app/*"
         },
         },
         "webRoot": "${workspaceFolder}/packages/app/public",
         "webRoot": "${workspaceFolder}/packages/app/public",
         "url": "http://localhost:3000"
         "url": "http://localhost:3000"
@@ -41,32 +61,32 @@
         "request": "launch",
         "request": "launch",
         "name": "Debug: Firefox",
         "name": "Debug: Firefox",
         "reAttach": true,
         "reAttach": true,
-        "url": "http://localhost:3000",
         "webRoot": "${workspaceFolder}/packages/app/public",
         "webRoot": "${workspaceFolder}/packages/app/public",
+        "url": "http://localhost:3000",
         "pathMappings": [
         "pathMappings": [
           {
           {
-            "url": "webpack:///core",
-            "path": "${workspaceFolder}/packages/core"
+            "url": "webpack://_n_e/src",
+            "path": "${workspaceFolder}/packages/app/src"
           },
           },
           {
           {
-            "url": "webpack:///plugin-attachment-refs",
-            "path": "${workspaceFolder}/packages/plugin-attachment-refs"
+            "url": "webpack://_n_e/core",
+            "path": "${workspaceFolder}/packages/core"
           },
           },
           {
           {
-            "url": "webpack:///plugin-pukiwiki-like-linker",
-            "path": "${workspaceFolder}/packages/plugin-pukiwiki-like-linker"
+            "url": "webpack://_n_e/plugin-attachment-refs",
+            "path": "${workspaceFolder}/packages/plugin-attachment-refs"
           },
           },
           {
           {
-            "url": "webpack:///plugin-lsx",
+            "url": "webpack://_n_e/plugin-lsx",
             "path": "${workspaceFolder}/packages/plugin-lsx"
             "path": "${workspaceFolder}/packages/plugin-lsx"
           },
           },
           {
           {
-            "url": "webpack:///ui",
-            "path": "${workspaceFolder}/packages/ui"
+            "url": "webpack://_n_e/slack",
+            "path": "${workspaceFolder}/packages/app/slack"
           },
           },
           {
           {
-            "url": "webpack:///src",
-            "path": "${workspaceFolder}/packages/app/src"
+            "url": "webpack://_n_e/ui",
+            "path": "${workspaceFolder}/packages/ui"
           },
           },
           {
           {
             "url": "http://localhost:3000",
             "url": "http://localhost:3000",

+ 16 - 11
package.json

@@ -48,24 +48,29 @@
     "cross-env": "^7.0.0",
     "cross-env": "^7.0.0",
     "dotenv-flow": "^3.2.0",
     "dotenv-flow": "^3.2.0",
     "npm-run-all": "^4.1.5",
     "npm-run-all": "^4.1.5",
+    "ts-deepmerge": "^3.0.0",
     "tslib": "^2.3.1"
     "tslib": "^2.3.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@swc/core": "^1.2.239",
+    "@swc/helpers": "^0.4.7",
     "@testing-library/cypress": "^8.0.2",
     "@testing-library/cypress": "^8.0.2",
+    "@types/css-modules": "^1.0.2",
     "@types/jest": "^26.0.22",
     "@types/jest": "^26.0.22",
-    "@types/node": "^14.14.35",
+    "@types/node": "^17.0.43",
     "@types/rewire": "^2.5.28",
     "@types/rewire": "^2.5.28",
-    "@typescript-eslint/eslint-plugin": "^4.28.5",
-    "@typescript-eslint/parser": "^4.28.5",
+    "@typescript-eslint/eslint-plugin": "^5.0.0",
+    "@typescript-eslint/parser": "^5.0.0",
     "cypress": "^9.2.0",
     "cypress": "^9.2.0",
-    "eslint": "^7.31.0",
+    "eslint": "^8.18.0",
+    "eslint-config-next": "^12.1.6",
     "eslint-config-weseek": "^2.1.0",
     "eslint-config-weseek": "^2.1.0",
-    "eslint-import-resolver-typescript": "^2.4.0",
-    "eslint-plugin-import": "^2.23.4",
-    "eslint-plugin-jest": "^24.3.2",
-    "eslint-plugin-react": "^7.24.0",
-    "eslint-plugin-react-hooks": "^4.2.0",
-    "jest": "^27.0.6",
+    "eslint-import-resolver-typescript": "^3.2.5",
+    "eslint-plugin-import": "^2.26.0",
+    "eslint-plugin-jest": "^26.5.3",
+    "eslint-plugin-react": "^7.30.1",
+    "eslint-plugin-react-hooks": "^4.6.0",
+    "jest": "^28.1.3",
     "jest-date-mock": "^1.0.8",
     "jest-date-mock": "^1.0.8",
     "jest-localstorage-mock": "^2.4.14",
     "jest-localstorage-mock": "^2.4.14",
     "lerna": "^4.0.0",
     "lerna": "^4.0.0",
@@ -80,7 +85,7 @@
     "shipjs": "^0.24.1",
     "shipjs": "^0.24.1",
     "stylelint": "^14.2.0",
     "stylelint": "^14.2.0",
     "stylelint-config-recess-order": "^3.0.0",
     "stylelint-config-recess-order": "^3.0.0",
-    "ts-jest": "^27.0.4",
+    "ts-jest": "^28.0.7",
     "ts-node": "^10.9.1",
     "ts-node": "^10.9.1",
     "tsconfig-paths": "^3.9.0",
     "tsconfig-paths": "^3.9.0",
     "typescript": "~4.7",
     "typescript": "~4.7",

+ 0 - 1
packages/app/.env.development

@@ -7,7 +7,6 @@ MIGRATIONS_DIR=src/migrations/
 APP_SITE_URL=http://localhost:3000
 APP_SITE_URL=http://localhost:3000
 FILE_UPLOAD=mongodb
 FILE_UPLOAD=mongodb
 # MONGO_GRIDFS_TOTAL_LIMIT=10485760
 # MONGO_GRIDFS_TOTAL_LIMIT=10485760
-MATHJAX=1
 # NO_CDN=true
 # NO_CDN=true
 MONGO_URI="mongodb://mongo:27017/growi"
 MONGO_URI="mongodb://mongo:27017/growi"
 # REDIS_URI="http://redis:6379"
 # REDIS_URI="http://redis:6379"

+ 5 - 0
packages/app/.eslintignore

@@ -1,6 +1,11 @@
+/_obsolete/**
 /dist/**
 /dist/**
+/transpiled/**
 /public/**
 /public/**
+/config/next-i18next.config.js
 /src/client/legacy/thirdparty-js/**
 /src/client/legacy/thirdparty-js/**
 /src/client/util/reveal/plugins/markdown.js
 /src/client/util/reveal/plugins/markdown.js
 /src/linter-checker/**
 /src/linter-checker/**
+/src/utils/next.config.utils.js
 /tmp/**
 /tmp/**
+/next-env.d.ts

+ 12 - 3
packages/app/.eslintrc.js

@@ -1,7 +1,6 @@
 module.exports = {
 module.exports = {
   extends: [
   extends: [
-    'weseek/react',
-    'weseek/typescript',
+    'next/core-web-vitals',
   ],
   ],
   plugins: [
   plugins: [
     'regex',
     'regex',
@@ -39,9 +38,19 @@ module.exports = {
     '@typescript-eslint/no-var-requires': 'off',
     '@typescript-eslint/no-var-requires': 'off',
 
 
     // set 'warn' temporarily -- 2021.08.02 Yuki Takei
     // set 'warn' temporarily -- 2021.08.02 Yuki Takei
-    '@typescript-eslint/explicit-module-boundary-types': ['warn'],
     '@typescript-eslint/no-use-before-define': ['warn'],
     '@typescript-eslint/no-use-before-define': ['warn'],
     '@typescript-eslint/no-this-alias': ['warn'],
     '@typescript-eslint/no-this-alias': ['warn'],
     'jest/no-done-callback': ['warn'],
     'jest/no-done-callback': ['warn'],
   },
   },
+  overrides: [
+    {
+      // enable the rule specifically for TypeScript files
+      files: ['*.ts', '*.tsx'],
+      rules: {
+        // '@typescript-eslint/explicit-module-boundary-types': ['error'],
+        // set 'warn' temporarily -- 2022.07.25 Yuki Takei
+        '@typescript-eslint/explicit-module-boundary-types': ['warn'],
+      },
+    },
+  ],
 };
 };

+ 5 - 2
packages/app/.gitignore

@@ -8,14 +8,17 @@ test/cypress/videos
 .reg
 .reg
 
 
 # dist
 # dist
+/build/
 /dist/
 /dist/
 /transpiled/
 /transpiled/
-/report/
 /public/static/js
 /public/static/js
 /public/static/styles
 /public/static/styles
 /public/uploads
 /public/uploads
 /tmp/
 /tmp/
-*.d.ts
+
+# transpiled configuration files for production build
+/config/next-i18next.config.js
+/src/utils/next.config.utils.js
 
 
 # dist (for GROWI v4.x and below)
 # dist (for GROWI v4.x and below)
 /public/*.chunk.js
 /public/*.chunk.js

+ 1 - 0
packages/app/config/webpack.common.js → packages/app/_obsolete/config/webpack.common.js

@@ -1,3 +1,4 @@
+/* eslint-disable */
 /**
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
  */

+ 1 - 0
packages/app/config/webpack.dev.dll.js → packages/app/_obsolete/config/webpack.dev.dll.js

@@ -1,3 +1,4 @@
+/* eslint-disable */
 /**
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
  */

+ 1 - 0
packages/app/config/webpack.dev.js → packages/app/_obsolete/config/webpack.dev.js

@@ -1,3 +1,4 @@
+/* eslint-disable */
 /**
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
  */

+ 1 - 0
packages/app/config/webpack.prod.js → packages/app/_obsolete/config/webpack.prod.js

@@ -1,3 +1,4 @@
+/* eslint-disable */
 /**
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
  */

+ 9 - 9
packages/app/src/client/admin.jsx → packages/app/_obsolete/src/client/admin.jsx

@@ -23,23 +23,23 @@ import AdminSamlSecurityContainer from '~/client/services/AdminSamlSecurityConta
 import AdminSlackIntegrationLegacyContainer from '~/client/services/AdminSlackIntegrationLegacyContainer';
 import AdminSlackIntegrationLegacyContainer from '~/client/services/AdminSlackIntegrationLegacyContainer';
 import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
 import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
 import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
 import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
-import AdminUserGroupDetailContainer from '~/client/services/AdminUserGroupDetailContainer';
+// import AdminUserGroupDetailContainer from '~/client/services/AdminUserGroupDetailContainer';
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import ContextExtractor from '~/client/services/ContextExtractor';
 import ContextExtractor from '~/client/services/ContextExtractor';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { swrGlobalConfiguration } from '~/utils/swr-utils';
 import { swrGlobalConfiguration } from '~/utils/swr-utils';
 
 
 import AdminHome from '../components/Admin/AdminHome/AdminHome';
 import AdminHome from '../components/Admin/AdminHome/AdminHome';
-import AppSettingsPage from '../components/Admin/App/AppSettingsPage';
+// import AppSettingsPage from '../components/Admin/App/AppSettingsPage';
 import { AuditLogManagement } from '../components/Admin/AuditLogManagement';
 import { AuditLogManagement } from '../components/Admin/AuditLogManagement';
 import AdminNavigation from '../components/Admin/Common/AdminNavigation';
 import AdminNavigation from '../components/Admin/Common/AdminNavigation';
 import Customize from '../components/Admin/Customize/Customize';
 import Customize from '../components/Admin/Customize/Customize';
 import ExportArchiveDataPage from '../components/Admin/ExportArchiveDataPage';
 import ExportArchiveDataPage from '../components/Admin/ExportArchiveDataPage';
 import FullTextSearchManagement from '../components/Admin/FullTextSearchManagement';
 import FullTextSearchManagement from '../components/Admin/FullTextSearchManagement';
-import ImportDataPage from '../components/Admin/ImportDataPage';
+// import ImportDataPage from '../components/Admin/ImportDataPage';
 import LegacySlackIntegration from '../components/Admin/LegacySlackIntegration/LegacySlackIntegration';
 import LegacySlackIntegration from '../components/Admin/LegacySlackIntegration/LegacySlackIntegration';
 import ManageExternalAccount from '../components/Admin/ManageExternalAccount';
 import ManageExternalAccount from '../components/Admin/ManageExternalAccount';
-import MarkdownSetting from '../components/Admin/MarkdownSetting/MarkDownSetting';
+// import MarkdownSetting from '../components/Admin/MarkdownSetting/MarkDownSetting';
 import ManageGlobalNotification from '../components/Admin/Notification/ManageGlobalNotification';
 import ManageGlobalNotification from '../components/Admin/Notification/ManageGlobalNotification';
 import NotificationSetting from '../components/Admin/Notification/NotificationSetting';
 import NotificationSetting from '../components/Admin/Notification/NotificationSetting';
 import SecurityManagement from '../components/Admin/Security/SecurityManagement';
 import SecurityManagement from '../components/Admin/Security/SecurityManagement';
@@ -67,7 +67,7 @@ const adminExternalAccountsContainer = new AdminExternalAccountsContainer(appCon
 const adminNotificationContainer = new AdminNotificationContainer(appContainer);
 const adminNotificationContainer = new AdminNotificationContainer(appContainer);
 const adminSlackIntegrationLegacyContainer = new AdminSlackIntegrationLegacyContainer(appContainer);
 const adminSlackIntegrationLegacyContainer = new AdminSlackIntegrationLegacyContainer(appContainer);
 const adminMarkDownContainer = new AdminMarkDownContainer(appContainer);
 const adminMarkDownContainer = new AdminMarkDownContainer(appContainer);
-const adminUserGroupDetailContainer = new AdminUserGroupDetailContainer(appContainer);
+// const adminUserGroupDetailContainer = new AdminUserGroupDetailContainer(appContainer);
 const socketIoContainer = appContainer.getContainer('SocketIoContainer');
 const socketIoContainer = appContainer.getContainer('SocketIoContainer');
 const injectableContainers = [
 const injectableContainers = [
   appContainer,
   appContainer,
@@ -81,7 +81,7 @@ const injectableContainers = [
   adminNotificationContainer,
   adminNotificationContainer,
   adminSlackIntegrationLegacyContainer,
   adminSlackIntegrationLegacyContainer,
   adminMarkDownContainer,
   adminMarkDownContainer,
-  adminUserGroupDetailContainer,
+  // adminUserGroupDetailContainer,
   socketIoContainer,
   socketIoContainer,
 ];
 ];
 
 
@@ -94,10 +94,10 @@ logger.info('unstated containers have been initialized');
  */
  */
 Object.assign(componentMappings, {
 Object.assign(componentMappings, {
   'admin-home': <AdminHome />,
   'admin-home': <AdminHome />,
-  'admin-app': <AppSettingsPage />,
-  'admin-markdown-setting': <MarkdownSetting />,
+  // 'admin-app': <AppSettingsPage />,
+  // 'admin-markdown-setting': <MarkdownSetting />,
   'admin-customize': <Customize />,
   'admin-customize': <Customize />,
-  'admin-importer': <ImportDataPage />,
+  // 'admin-importer': <ImportDataPage />,
   'admin-export-page': <ExportArchiveDataPage />,
   'admin-export-page': <ExportArchiveDataPage />,
   'admin-notification-setting': <NotificationSetting />,
   'admin-notification-setting': <NotificationSetting />,
   'admin-slack-integration': <SlackIntegration />,
   'admin-slack-integration': <SlackIntegration />,

+ 4 - 15
packages/app/src/client/app.jsx → packages/app/_obsolete/src/client/app.jsx

@@ -10,7 +10,7 @@ import { Provider } from 'unstated';
 import ContextExtractor from '~/client/services/ContextExtractor';
 import ContextExtractor from '~/client/services/ContextExtractor';
 import EditorContainer from '~/client/services/EditorContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import PageContainer from '~/client/services/PageContainer';
 import PageContainer from '~/client/services/PageContainer';
-import IdenticalPathPage from '~/components/IdenticalPathPage';
+import { IdenticalPathPage } from '~/components/IdenticalPathPage';
 import PrivateLegacyPages from '~/components/PrivateLegacyPages';
 import PrivateLegacyPages from '~/components/PrivateLegacyPages';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { swrGlobalConfiguration } from '~/utils/swr-utils';
 import { swrGlobalConfiguration } from '~/utils/swr-utils';
@@ -26,22 +26,19 @@ import MyDraftList from '../components/MyDraftList/MyDraftList';
 import GrowiContextualSubNavigation from '../components/Navbar/GrowiContextualSubNavigation';
 import GrowiContextualSubNavigation from '../components/Navbar/GrowiContextualSubNavigation';
 import GrowiSubNavigationSwitcher from '../components/Navbar/GrowiSubNavigationSwitcher';
 import GrowiSubNavigationSwitcher from '../components/Navbar/GrowiSubNavigationSwitcher';
 import NotFoundPage from '../components/NotFoundPage';
 import NotFoundPage from '../components/NotFoundPage';
-import Page from '../components/Page';
+import { Page } from '../components/Page';
 import DisplaySwitcher from '../components/Page/DisplaySwitcher';
 import DisplaySwitcher from '../components/Page/DisplaySwitcher';
-import FixPageGrantAlert from '../components/Page/FixPageGrantAlert';
 import RedirectedAlert from '../components/Page/RedirectedAlert';
 import RedirectedAlert from '../components/Page/RedirectedAlert';
 import ShareLinkAlert from '../components/Page/ShareLinkAlert';
 import ShareLinkAlert from '../components/Page/ShareLinkAlert';
-import TrashPageAlert from '../components/Page/TrashPageAlert';
-import PageComment from '../components/PageComment';
+import { PageComment } from '../components/PageComment';
 import CommentEditorLazyRenderer from '../components/PageComment/CommentEditorLazyRenderer';
 import CommentEditorLazyRenderer from '../components/PageComment/CommentEditorLazyRenderer';
 import PageContentFooter from '../components/PageContentFooter';
 import PageContentFooter from '../components/PageContentFooter';
 import BookmarkList from '../components/PageList/BookmarkList';
 import BookmarkList from '../components/PageList/BookmarkList';
 import PageStatusAlert from '../components/PageStatusAlert';
 import PageStatusAlert from '../components/PageStatusAlert';
-import PageTimeline from '../components/PageTimeline';
+import { PageTimeline } from '../components/PageTimeline';
 import RecentCreated from '../components/RecentCreated/RecentCreated';
 import RecentCreated from '../components/RecentCreated/RecentCreated';
 import { SearchPage } from '../components/SearchPage';
 import { SearchPage } from '../components/SearchPage';
 import Sidebar from '../components/Sidebar';
 import Sidebar from '../components/Sidebar';
-import TagPage from '../components/TagPage';
 import TrashPageList from '../components/TrashPageList';
 import TrashPageList from '../components/TrashPageList';
 
 
 import { appContainer, componentMappings } from './base';
 import { appContainer, componentMappings } from './base';
@@ -77,14 +74,11 @@ Object.assign(componentMappings, {
   'identical-path-page': <IdenticalPathPage />,
   'identical-path-page': <IdenticalPathPage />,
 
 
   // 'revision-history': <PageHistory pageId={pageId} />,
   // 'revision-history': <PageHistory pageId={pageId} />,
-  'tags-page': <TagPage />,
 
 
   'grw-page-status-alert-container': <PageStatusAlert />,
   'grw-page-status-alert-container': <PageStatusAlert />,
 
 
   'maintenance-mode-content': <MaintenanceModeContent />,
   'maintenance-mode-content': <MaintenanceModeContent />,
 
 
-  'trash-page-alert': <TrashPageAlert />,
-
   'trash-page-list-container': <TrashPageList />,
   'trash-page-list-container': <TrashPageList />,
 
 
   'not-found-page': <NotFoundPage />,
   'not-found-page': <NotFoundPage />,
@@ -116,11 +110,6 @@ if (pageContainer.state.pageId != null) {
 
 
     'recent-created-icon': <RecentlyCreatedIcon />,
     'recent-created-icon': <RecentlyCreatedIcon />,
   });
   });
-  if (!pageContainer.state.isEmpty) {
-    Object.assign(componentMappings, {
-      'fix-page-grant-alert': <FixPageGrantAlert />,
-    });
-  }
 }
 }
 if (pageContainer.state.creator != null) {
 if (pageContainer.state.creator != null) {
   Object.assign(componentMappings, {
   Object.assign(componentMappings, {

+ 2 - 5
packages/app/src/client/base.jsx → packages/app/_obsolete/src/client/base.jsx

@@ -3,7 +3,6 @@ import React from 'react';
 import EventEmitter from 'events';
 import EventEmitter from 'events';
 
 
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';
-import SocketIoContainer from '~/client/services/SocketIoContainer';
 import { DescendantsPageListModal } from '~/components/DescendantsPageListModal';
 import { DescendantsPageListModal } from '~/components/DescendantsPageListModal';
 import PutbackPageModal from '~/components/PutbackPageModal';
 import PutbackPageModal from '~/components/PutbackPageModal';
 import ShortcutsModal from '~/components/ShortcutsModal';
 import ShortcutsModal from '~/components/ShortcutsModal';
@@ -13,8 +12,8 @@ import loggerFactory from '~/utils/logger';
 
 
 import EmptyTrashModal from '../components/EmptyTrashModal';
 import EmptyTrashModal from '../components/EmptyTrashModal';
 import HotkeysManager from '../components/Hotkeys/HotkeysManager';
 import HotkeysManager from '../components/Hotkeys/HotkeysManager';
-import GrowiNavbar from '../components/Navbar/GrowiNavbar';
-import GrowiNavbarBottom from '../components/Navbar/GrowiNavbarBottom';
+import { GrowiNavbar } from '../components/Navbar/GrowiNavbar';
+import { GrowiNavbarBottom } from '../components/Navbar/GrowiNavbarBottom';
 import PageAccessoriesModal from '../components/PageAccessoriesModal';
 import PageAccessoriesModal from '../components/PageAccessoriesModal';
 import PageCreateModal from '../components/PageCreateModal';
 import PageCreateModal from '../components/PageCreateModal';
 import PageDeleteModal from '../components/PageDeleteModal';
 import PageDeleteModal from '../components/PageDeleteModal';
@@ -35,8 +34,6 @@ window.interceptorManager = new InterceptorManager();
 
 
 // create unstated container instance
 // create unstated container instance
 const appContainer = new AppContainer();
 const appContainer = new AppContainer();
-// eslint-disable-next-line no-unused-vars
-const socketIoContainer = new SocketIoContainer(appContainer);
 
 
 appContainer.initApp();
 appContainer.initApp();
 
 

+ 5 - 0
packages/app/_obsolete/src/client/boot.js

@@ -0,0 +1,5 @@
+import {
+  applyOldIos,
+} from './util/old-ios';
+
+applyOldIos();

+ 0 - 0
packages/app/src/client/installer.jsx → packages/app/_obsolete/src/client/installer.jsx


+ 0 - 0
packages/app/src/client/nologin.jsx → packages/app/_obsolete/src/client/nologin.jsx


+ 0 - 0
packages/app/src/client/plugin.js → packages/app/_obsolete/src/client/plugin.js


+ 5 - 0
packages/app/src/client/util/i18n.js → packages/app/_obsolete/src/util/i18n.js

@@ -1,3 +1,5 @@
+
+/* eslint-disable */
 import i18n from 'i18next';
 import i18n from 'i18next';
 import LanguageDetector from 'i18next-browser-languagedetector';
 import LanguageDetector from 'i18next-browser-languagedetector';
 import { initReactI18next } from 'react-i18next';
 import { initReactI18next } from 'react-i18next';
@@ -14,6 +16,9 @@ Object.values(locales).forEach((locale) => {
   });
   });
 });
 });
 
 
+/*
+* Note: This file will be deleted. use "^/config/next-i18next.config" instead
+*/
 // extract metadata list from 'public/static/locales/${locale}/meta.json'
 // extract metadata list from 'public/static/locales/${locale}/meta.json'
 export const localeMetadatas = Object.values(locales).map(locale => locale.meta);
 export const localeMetadatas = Object.values(locales).map(locale => locale.meta);
 
 

+ 0 - 0
packages/app/src/client/util/old-ios.js → packages/app/_obsolete/src/util/old-ios.js


+ 0 - 56
packages/app/bin/generate-plugin-definitions-source.ts

@@ -1,56 +0,0 @@
-/**
- * the tool for genetion of plugin definitions source code
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- */
-import fs from 'graceful-fs';
-import normalize from 'normalize-path';
-import swig from 'swig-templates';
-
-import { PluginDefinitionV4 } from '@growi/core';
-
-import PluginUtils from '../src/server/plugins/plugin-utils';
-import loggerFactory from '../src/utils/logger';
-import { resolveFromRoot } from '../src/utils/project-dir-utils';
-
-const logger = loggerFactory('growi:bin:generate-plugin-definitions-source');
-
-
-const pluginUtils = new PluginUtils();
-
-const TEMPLATE = resolveFromRoot('bin/templates/plugin-definitions.js.swig');
-const OUT = resolveFromRoot('tmp/plugins/plugin-definitions.js');
-
-// list plugin names
-const pluginNames: string[] = pluginUtils.listPluginNames();
-logger.info('Detected plugins: ', pluginNames);
-
-async function main(): Promise<void> {
-
-  // get definitions
-  const definitions: PluginDefinitionV4[] = [];
-  for (const pluginName of pluginNames) {
-    // eslint-disable-next-line no-await-in-loop
-    const definition = await pluginUtils.generatePluginDefinition(pluginName, true);
-    if (definition != null) {
-      definitions.push(definition);
-    }
-  }
-
-  definitions.map((definition) => {
-    // convert backslash to slash
-    definition.entries = definition.entries.map((entryPath) => {
-      return normalize(entryPath);
-    });
-    return definition;
-  });
-
-  const compiledTemplate = swig.compileFile(TEMPLATE);
-  const code = compiledTemplate({ definitions });
-
-  // write
-  fs.writeFileSync(OUT, code);
-
-}
-
-main();

+ 6 - 1
packages/app/migrate-mongo-config.js → packages/app/config/migrate-mongo-config.js

@@ -4,16 +4,21 @@
  *
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
  */
+const isProduction = process.env.NODE_ENV === 'production';
 
 
 const { URL } = require('url');
 const { URL } = require('url');
 
 
+const { initMongooseGlobalSettings, getMongoUri, mongoOptions } = isProduction
+  // eslint-disable-next-line import/extensions, import/no-unresolved
+  ? require('../dist/server/util/mongoose-utils')
+  : require('../src/server/util/mongoose-utils');
+
 // get migrationsDir from env var
 // get migrationsDir from env var
 const migrationsDir = process.env.MIGRATIONS_DIR;
 const migrationsDir = process.env.MIGRATIONS_DIR;
 if (migrationsDir == null) {
 if (migrationsDir == null) {
   throw new Error('An env var MIGRATIONS_DIR must be set.');
   throw new Error('An env var MIGRATIONS_DIR must be set.');
 }
 }
 
 
-const { initMongooseGlobalSettings, getMongoUri, mongoOptions } = require('@growi/core');
 
 
 initMongooseGlobalSettings();
 initMongooseGlobalSettings();
 
 

+ 27 - 0
packages/app/config/next-i18next.config.ts

@@ -0,0 +1,27 @@
+import path from 'path';
+
+import { isServer, AllLang, Lang } from '@growi/core';
+import I18nextChainedBackend from 'i18next-chained-backend';
+import I18NextHttpBackend from 'i18next-http-backend';
+import I18NextLocalStorageBackend from 'i18next-localstorage-backend';
+
+const isDev = process.env.NODE_ENV === 'development';
+
+export const i18n = {
+  defaultLocale: Lang.en_US,
+  locales: AllLang,
+};
+export const defaultNS = 'translation';
+export const localePath = path.resolve('./public/static/locales');
+
+export const serializeConfig = false;
+export const use = isServer() ? [] : [I18nextChainedBackend];
+export const backend = {
+  backends: isServer() ? [] : [I18NextLocalStorageBackend, I18NextHttpBackend],
+  backendOptions: [
+    // options for i18next-localstorage-backend
+    { expirationTime: isDev ? 0 : 24 * 60 * 60 * 1000 }, // 1 day in production
+    // options for i18next-http-backend
+    { loadPath: '/static/locales/{{lng}}/{{ns}}.json' },
+  ],
+};

+ 1 - 2
packages/app/docker/Dockerfile

@@ -101,7 +101,6 @@ COPY packages/core packages/core
 COPY packages/codemirror-textlint packages/codemirror-textlint
 COPY packages/codemirror-textlint packages/codemirror-textlint
 COPY packages/plugin-attachment-refs packages/plugin-attachment-refs
 COPY packages/plugin-attachment-refs packages/plugin-attachment-refs
 COPY packages/plugin-lsx packages/plugin-lsx
 COPY packages/plugin-lsx packages/plugin-lsx
-COPY packages/plugin-pukiwiki-like-linker packages/plugin-pukiwiki-like-linker
 COPY packages/slack packages/slack
 COPY packages/slack packages/slack
 COPY packages/ui packages/ui
 COPY packages/ui packages/ui
 
 
@@ -111,11 +110,11 @@ RUN yarn lerna run build
 # make artifacts
 # make artifacts
 RUN tar -cf packages.tar \
 RUN tar -cf packages.tar \
   package.json \
   package.json \
+  packages/app/.next \
   packages/app/config \
   packages/app/config \
   packages/app/public \
   packages/app/public \
   packages/app/resource \
   packages/app/resource \
   packages/app/tmp \
   packages/app/tmp \
-  packages/app/migrate-mongo-config.js \
   packages/app/.env.production* \
   packages/app/.env.production* \
   packages/*/package.json \
   packages/*/package.json \
   packages/*/dist
   packages/*/dist

+ 8 - 1
packages/app/jest.config.js

@@ -5,7 +5,8 @@
 const MODULE_NAME_MAPPING = {
 const MODULE_NAME_MAPPING = {
   '^\\^/(.+)$': '<rootDir>/$1',
   '^\\^/(.+)$': '<rootDir>/$1',
   '^~/(.+)$': '<rootDir>/src/$1',
   '^~/(.+)$': '<rootDir>/src/$1',
-  '^@growi/(.+)$': '<rootDir>/../$1/src',
+  '^@growi/([^/]+)$': '<rootDir>/../$1/src',
+  '^@growi/([^/]+)/(.+)$': '<rootDir>/../$1/src/$2',
 };
 };
 
 
 module.exports = {
 module.exports = {
@@ -20,6 +21,11 @@ module.exports = {
 
 
       preset: 'ts-jest/presets/js-with-ts',
       preset: 'ts-jest/presets/js-with-ts',
 
 
+      // transform ESM to CJS
+      transformIgnorePatterns: [
+        '/node_modules/(?!remark-gfm)/',
+      ],
+
       rootDir: '.',
       rootDir: '.',
       roots: ['<rootDir>'],
       roots: ['<rootDir>'],
       testMatch: ['<rootDir>/test/unit/**/*.test.ts', '<rootDir>/test/unit/**/*.test.js'],
       testMatch: ['<rootDir>/test/unit/**/*.test.ts', '<rootDir>/test/unit/**/*.test.js'],
@@ -29,6 +35,7 @@ module.exports = {
       // Automatically clear mock calls and instances between every test
       // Automatically clear mock calls and instances between every test
       clearMocks: true,
       clearMocks: true,
       moduleNameMapper: MODULE_NAME_MAPPING,
       moduleNameMapper: MODULE_NAME_MAPPING,
+
     },
     },
     {
     {
       displayName: 'server',
       displayName: 'server',

+ 5 - 0
packages/app/next-env.d.ts

@@ -0,0 +1,5 @@
+/// <reference types="next" />
+/// <reference types="next/image-types/global" />
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.

+ 110 - 0
packages/app/next.config.js

@@ -0,0 +1,110 @@
+/**
+ * == Notes for production build==
+ * The modules required from this file must be transpiled before running `next build`.
+ *
+ * See: https://github.com/vercel/next.js/discussions/35969#discussioncomment-2522954
+ */
+
+const { withSuperjson } = require('next-superjson');
+const { PHASE_PRODUCTION_BUILD, PHASE_PRODUCTION_SERVER } = require('next/constants');
+
+
+const setupTranspileModules = () => {
+  const eazyLogger = require('eazy-logger');
+  const { listScopedPackages, listPrefixedPackages } = require('./src/utils/next.config.utils');
+
+  // setup logger
+  const logger = eazyLogger.Logger({
+    prefix: '[{green:next.config.js}] ',
+    useLevelPrefixes: false,
+  });
+
+  // define transpiled packages for '@growi/*'
+  const packages = [
+    ...listScopedPackages(['@growi'], { ignorePackageNames: ['@growi/app'] }),
+    // listing ESM packages until experimental.esmExternals works correctly to avoid ERR_REQUIRE_ESM
+    'react-markdown',
+    'unified',
+    'character-entities-html4',
+    'comma-separated-tokens',
+    'decode-named-character-reference',
+    'hastscript',
+    'html-void-elements',
+    'is-absolute-url',
+    'longest-streak',
+    'property-information',
+    'space-separated-tokens',
+    'stringify-entities',
+    'trim-lines',
+    'trough',
+    'web-namespaces',
+    'vfile',
+    'zwitch',
+    'emoticon',
+    'direction', // for hast-util-select
+    'bcp-47-match', // for hast-util-select
+    ...listPrefixedPackages(['remark-', 'rehype-', 'hast-', 'mdast-', 'micromark-', 'unist-']),
+  ];
+
+  logger.info('{bold:Listing scoped packages for transpiling:}');
+  logger.unprefixed('info', `{grey:${JSON.stringify(packages, null, 2)}}`);
+
+  return require('next-transpile-modules')(packages);
+};
+
+
+module.exports = async(phase, { defaultConfig }) => {
+
+  const { i18n, localePath } = require('./config/next-i18next.config');
+
+  /** @type {import('next').NextConfig} */
+  const nextConfig = {
+    // == DOES NOT WORK
+    // see: https://github.com/vercel/next.js/discussions/27876
+    // experimental: { esmExternals: true }, // Prefer loading of ES Modules over CommonJS
+
+    reactStrictMode: true,
+    swcMinify: true,
+    typescript: {
+      tsconfigPath: 'tsconfig.build.client.json',
+    },
+    pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
+
+    i18n,
+
+    /** @param config {import('next').NextConfig} */
+    webpack(config, options) {
+      // Avoid "Module not found: Can't resolve 'fs'"
+      // See: https://stackoverflow.com/a/68511591
+      if (!options.isServer) {
+        config.resolve.fallback.fs = false;
+      }
+
+      // See: https://webpack.js.org/configuration/externals/
+      // This provides a way of excluding dependencies from the output bundles
+      config.externals.push('dtrace-provider');
+      config.externals.push('mongoose');
+
+      // setup i18next-hmr
+      if (!options.isServer && options.dev) {
+        const { I18NextHMRPlugin } = require('i18next-hmr/plugin');
+        config.plugins.push(new I18NextHMRPlugin({ localesDir: localePath }));
+      }
+
+      return config;
+    },
+
+  };
+
+  // production server
+  if (phase === PHASE_PRODUCTION_SERVER) {
+    return withSuperjson()(nextConfig);
+  }
+
+  const withTM = setupTranspileModules();
+  const withBundleAnalyzer = require('@next/bundle-analyzer')({
+    enabled: phase === PHASE_PRODUCTION_BUILD || process.env.ANALYZE === 'true',
+  });
+
+  return withBundleAnalyzer(withTM(withSuperjson()(nextConfig)));
+};

+ 60 - 72
packages/app/package.json

@@ -4,33 +4,31 @@
   "license": "MIT",
   "license": "MIT",
   "scripts": {
   "scripts": {
     "//// for production": "",
     "//// for production": "",
-    "start": "yarn build && yarn server",
     "build": "run-p build:*",
     "build": "run-p build:*",
-    "build:client": "yarn cross-env NODE_ENV=production webpack --config config/webpack.prod.js",
-    "build:server": "yarn cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server.json",
+    "start": "yarn next start",
+    "build:client": "yarn next build",
+    "prebuild:client": "tsc -p tsconfig.build.next.config.json",
+    "build:server": "yarn cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server-tsc-alias.json",
+    "postbuild:server": "npx -y shx echo \"Listing files under transpiled\" && npx -y shx ls transpiled && npx -y shx mv transpiled/src dist && npx -y shx cp -r transpiled/config/* config && npx -y shx cp -r src/server/views dist/server/ && npx -y shx rm -rf transpiled",
     "clean": "npx -y shx rm -rf dist transpiled",
     "clean": "npx -y shx rm -rf dist transpiled",
     "prebuild": "yarn cross-env NODE_ENV=production run-p clean resources:*",
     "prebuild": "yarn cross-env NODE_ENV=production run-p clean resources:*",
-    "postbuild": "npx -y shx mv transpiled/src dist && npx -y shx cp -r transpiled/config/* config && npx -y shx cp -r src/server/views dist/server/ && npx -y shx rm -rf transpiled",
     "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
     "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
     "server:ci": "yarn server --ci",
     "server:ci": "yarn server --ci",
     "preserver": "yarn cross-env NODE_ENV=production yarn migrate",
     "preserver": "yarn cross-env NODE_ENV=production yarn migrate",
-    "migrate": "node -r dotenv-flow/config node_modules/.bin/migrate-mongo up",
+    "migrate": "node -r dotenv-flow/config node_modules/.bin/migrate-mongo up -f config/migrate-mongo-config.js",
     "//// for development": "",
     "//// for development": "",
-    "dev": "run-p dev:client dev:server",
-    "dev:client": "yarn cross-env NODE_ENV=development webpack --config config/webpack.dev.js --progress --watch",
-    "dev:client:nowatch": "yarn cross-env NODE_ENV=development webpack --config config/webpack.dev.js",
-    "dev:server": "yarn cross-env NODE_ENV=development ts-node-dev --inspect -r tsconfig-paths/register -r dotenv-flow/config --transpile-only src/server/app.ts",
-    "predev:client": "yarn cross-env NODE_ENV=development run-p resources:*",
-    "predev:server": "yarn cross-env NODE_ENV=development yarn dev:migrate:up",
+    "dev": "yarn cross-env NODE_ENV=development ts-node-dev -r tsconfig-paths/register -r dotenv-flow/config --inspect --transpile-only src/server/app.ts",
+    "predev": "yarn cross-env NODE_ENV=development run-p resources:* dev:migrate:up",
+    "dev:analyze": "yarn cross-env ANALYZE=true yarn dev",
     "dev:migrate-mongo": "yarn cross-env NODE_ENV=development yarn ts-node node_modules/.bin/migrate-mongo",
     "dev:migrate-mongo": "yarn cross-env NODE_ENV=development yarn ts-node node_modules/.bin/migrate-mongo",
     "dev:migrate": "yarn dev:migrate:up",
     "dev:migrate": "yarn dev:migrate:up",
-    "dev:migrate:create": "yarn dev:migrate-mongo create",
-    "dev:migrate:status": "yarn dev:migrate-mongo status",
-    "dev:migrate:up": "yarn dev:migrate-mongo up",
-    "dev:migrate:down": "yarn dev:migrate-mongo down",
+    "dev:migrate:create": "yarn dev:migrate-mongo create -f config/migrate-mongo-config.js",
+    "dev:migrate:status": "yarn dev:migrate-mongo status -f config/migrate-mongo-config.js",
+    "dev:migrate:up": "yarn dev:migrate-mongo up -f config/migrate-mongo-config.js",
+    "dev:migrate:down": "yarn dev:migrate-mongo down -f config/migrate-mongo-config.js",
     "cy:run": "cypress run --browser chrome",
     "cy:run": "cypress run --browser chrome",
     "//// for CI": "",
     "//// for CI": "",
-    "dev:ci": "yarn dev:client:nowatch && yarn dev:server --ci",
+    "dev:ci": "yarn dev --ci",
     "predev:ci": "run-p resources:*",
     "predev:ci": "run-p resources:*",
     "lint:typecheck": "npx -y tsc",
     "lint:typecheck": "npx -y tsc",
     "lint:eslint": "eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:eslint": "eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
@@ -39,7 +37,7 @@
     "lint": "run-p lint:*",
     "lint": "run-p lint:*",
     "test": "cross-env NODE_ENV=test jest --passWithNoTests -- ",
     "test": "cross-env NODE_ENV=test jest --passWithNoTests -- ",
     "test:ci": "cross-env NODE_ENV=test jest",
     "test:ci": "cross-env NODE_ENV=test jest",
-    "prelint:eslint": "yarn resources:plugin",
+    "// prelint:eslint": "yarn resources:plugin",
     "prelint:swagger2openapi": "yarn openapi:v3",
     "prelint:swagger2openapi": "yarn openapi:v3",
     "reg:run": "reg-suit run",
     "reg:run": "reg-suit run",
     "//// misc": "",
     "//// misc": "",
@@ -47,9 +45,10 @@
     "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
     "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
     "openapi:v3": "yarn cross-env API_VERSION=3 yarn swagger-jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
     "openapi:v3": "yarn cross-env API_VERSION=3 yarn swagger-jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
     "openapi:v1": "yarn cross-env API_VERSION=1 yarn swagger-jsdoc -- \"src/server/*/*.js\" \"src/server/models/**/*.js\"",
     "openapi:v1": "yarn cross-env API_VERSION=1 yarn swagger-jsdoc -- \"src/server/*/*.js\" \"src/server/models/**/*.js\"",
-    "resources:plugin": "yarn ts-node bin/generate-plugin-definitions-source.ts",
-    "resources:dl-resources": "yarn ts-node bin/download-cdn-resources.ts",
-    "ts-node": "ts-node -r tsconfig-paths/register -r dotenv-flow/config --transpile-only"
+    "resources:dummy": "true",
+    "// resources:plugin": "yarn ts-node bin/generate-plugin-definitions-source.ts",
+    "// resources:dl-resources": "yarn ts-node bin/download-cdn-resources.ts",
+    "ts-node": "node -r ts-node/register -r tsconfig-paths/register -r dotenv-flow/config"
   },
   },
   "// comments for dependencies": {
   "// comments for dependencies": {
     "openid-client": "Node.js 12 or higher is required for openid-client@3 and above.",
     "openid-client": "Node.js 12 or higher is required for openid-client@3 and above.",
@@ -68,7 +67,6 @@
     "@growi/core": "^5.1.3-RC.0",
     "@growi/core": "^5.1.3-RC.0",
     "@growi/plugin-attachment-refs": "^5.1.3-RC.0",
     "@growi/plugin-attachment-refs": "^5.1.3-RC.0",
     "@growi/plugin-lsx": "^5.1.3-RC.0",
     "@growi/plugin-lsx": "^5.1.3-RC.0",
-    "@growi/plugin-pukiwiki-like-linker": "^5.1.3-RC.0",
     "@growi/slack": "^5.1.3-RC.0",
     "@growi/slack": "^5.1.3-RC.0",
     "@promster/express": "^7.0.2",
     "@promster/express": "^7.0.2",
     "@promster/server": "^7.0.4",
     "@promster/server": "^7.0.4",
@@ -84,6 +82,7 @@
     "axios-retry": "^3.2.4",
     "axios-retry": "^3.2.4",
     "body-parser": "^1.18.2",
     "body-parser": "^1.18.2",
     "browser-bunyan": "^1.8.0",
     "browser-bunyan": "^1.8.0",
+    "bson-objectid": "^2.0.3",
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",
     "check-node-version": "^4.1.0",
     "check-node-version": "^4.1.0",
     "compression": "^1.7.4",
     "compression": "^1.7.4",
@@ -91,7 +90,7 @@
     "connect-mongo": "^4.6.0",
     "connect-mongo": "^4.6.0",
     "connect-redis": "^4.0.4",
     "connect-redis": "^4.0.4",
     "cookie-parser": "^1.4.5",
     "cookie-parser": "^1.4.5",
-    "csrf": "^3.1.0",
+    "csurf": "^1.11.0",
     "date-fns": "^2.23.0",
     "date-fns": "^2.23.0",
     "detect-indent": "^7.0.0",
     "detect-indent": "^7.0.0",
     "diff": "^5.0.0",
     "diff": "^5.0.0",
@@ -109,12 +108,13 @@
     "express-webpack-assets": "^0.1.0",
     "express-webpack-assets": "^0.1.0",
     "extensible-custom-error": "^0.0.7",
     "extensible-custom-error": "^0.0.7",
     "graceful-fs": "^4.1.11",
     "graceful-fs": "^4.1.11",
+    "hast-util-select": "^5.0.2",
     "helmet": "^4.6.0",
     "helmet": "^4.6.0",
     "http-errors": "^2.0.0",
     "http-errors": "^2.0.0",
-    "i18next": "^20.3.2",
-    "i18next-express-middleware": "^2.0.0",
-    "i18next-node-fs-backend": "^2.1.3",
-    "i18next-sprintf-postprocessor": "^0.2.2",
+    "i18next-chained-backend": "^3.0.2",
+    "i18next-http-backend": "^1.4.1",
+    "i18next-localstorage-backend": "^3.1.3",
+    "is-absolute-url": "^4.0.1",
     "is-iso-date": "^0.0.1",
     "is-iso-date": "^0.0.1",
     "lucene-query-parser": "^1.2.0",
     "lucene-query-parser": "^1.2.0",
     "md5": "^2.2.1",
     "md5": "^2.2.1",
@@ -127,6 +127,10 @@
     "mongoose-unique-validator": "^2.0.3",
     "mongoose-unique-validator": "^2.0.3",
     "multer": "~1.4.0",
     "multer": "~1.4.0",
     "multer-autoreap": "^1.0.3",
     "multer-autoreap": "^1.0.3",
+    "next": "^12.1.6",
+    "next-i18next": "^11.0.0",
+    "next-superjson": "^0.0.4",
+    "next-themes": "^0.2.0",
     "nocache": "^3.0.1",
     "nocache": "^3.0.1",
     "nodemailer": "^6.6.2",
     "nodemailer": "^6.6.2",
     "nodemailer-ses-transport": "~1.5.0",
     "nodemailer-ses-transport": "~1.5.0",
@@ -140,20 +144,36 @@
     "passport-local": "^1.0.0",
     "passport-local": "^1.0.0",
     "passport-saml": "^3.2.0",
     "passport-saml": "^3.2.0",
     "passport-twitter": "^1.0.4",
     "passport-twitter": "^1.0.4",
+    "prism-themes": "^1.9.0",
     "prom-client": "^13.0.0",
     "prom-client": "^13.0.0",
     "rate-limiter-flexible": "^2.3.7",
     "rate-limiter-flexible": "^2.3.7",
+    "react": "^18.2.0",
     "react-card-flip": "^1.0.10",
     "react-card-flip": "^1.0.10",
     "react-datepicker": "^4.7.0",
     "react-datepicker": "^4.7.0",
     "react-dnd": "^14.0.5",
     "react-dnd": "^14.0.5",
     "react-dnd-html5-backend": "^14.1.0",
     "react-dnd-html5-backend": "^14.1.0",
+    "react-dom": "^18.2.0",
     "react-image-crop": "^8.3.0",
     "react-image-crop": "^8.3.0",
+    "react-markdown": "^8.0.3",
     "react-multiline-clamp": "^2.0.0",
     "react-multiline-clamp": "^2.0.0",
+    "react-syntax-highlighter": "^15.5.0",
     "reconnecting-websocket": "^4.4.0",
     "reconnecting-websocket": "^4.4.0",
     "redis": "^3.0.2",
     "redis": "^3.0.2",
+    "rehype-katex": "^6.0.2",
+    "rehype-raw": "^6.1.1",
+    "rehype-sanitize": "^5.0.1",
+    "rehype-slug": "^5.0.1",
+    "rehype-toc": "^3.0.2",
+    "remark-breaks": "^3.0.2",
+    "remark-emoji": "^3.0.2",
+    "remark-gfm": "^3.0.1",
+    "remark-math": "^5.1.1",
+    "remark-wiki-link": "^1.0.4",
     "rimraf": "^3.0.0",
     "rimraf": "^3.0.0",
     "socket.io": "^4.2.0",
     "socket.io": "^4.2.0",
     "stream-to-promise": "^3.0.0",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
     "string-width": "=4.2.2",
+    "superjson": "^1.9.1",
     "swagger-jsdoc": "^6.1.0",
     "swagger-jsdoc": "^6.1.0",
     "swig-templates": "^2.0.2",
     "swig-templates": "^2.0.2",
     "uglifycss": "^0.0.29",
     "uglifycss": "^0.0.29",
@@ -166,21 +186,21 @@
   },
   },
   "// comments for defDependencies": {
   "// comments for defDependencies": {
     "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
     "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
-    "handsontable": "v7.0.0 or above is no loger MIT lisence.",
-    "ts-loader": "v9 is not compatible with webpack@5",
-    "ts-node": "v10 occurs 'SyntaxError: Cannot use import statement outside a module' when using migrate-mongo"
+    "handsontable": "v7.0.0 or above is no loger MIT lisence."
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.1.4",
     "@alienfast/i18next-loader": "^1.1.4",
     "@growi/ui": "^5.1.3-RC.0",
     "@growi/ui": "^5.1.3-RC.0",
     "@handsontable/react": "=2.1.0",
     "@handsontable/react": "=2.1.0",
+    "@icon/themify-icons": "1.0.1-alpha.3",
+    "@next/bundle-analyzer": "^12.2.3",
     "@types/compression": "^1.7.0",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",
     "@types/express": "^4.17.11",
     "@types/jquery": "^3.5.8",
     "@types/jquery": "^3.5.8",
     "@types/multer": "^1.4.5",
     "@types/multer": "^1.4.5",
-    "@types/react-dom": "^17.0.9",
     "autoprefixer": "^9.0.0",
     "autoprefixer": "^9.0.0",
-    "bootstrap": "^4.5.0",
+    "babel-loader": "^8.2.5",
+    "bootstrap": "^4.6.1",
     "browser-sync": "^2.27.7",
     "browser-sync": "^2.27.7",
     "bunyan-debug": "^2.0.0",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",
     "cli": "~1.0.1",
@@ -188,82 +208,50 @@
     "colors": "=1.4.0",
     "colors": "=1.4.0",
     "connect-browser-sync": "^2.1.0",
     "connect-browser-sync": "^2.1.0",
     "core-js": "=2.6.9",
     "core-js": "=2.6.9",
-    "css-loader": "^3.0.0",
     "csv-to-markdown-table": "^1.0.1",
     "csv-to-markdown-table": "^1.0.1",
     "diff2html": "^3.1.2",
     "diff2html": "^3.1.2",
     "eazy-logger": "^3.1.0",
     "eazy-logger": "^3.1.0",
     "emoji-mart": "npm:panta82-emoji-mart@^3.0.1",
     "emoji-mart": "npm:panta82-emoji-mart@^3.0.1",
     "eslint-plugin-cypress": "^2.12.1",
     "eslint-plugin-cypress": "^2.12.1",
     "eslint-plugin-regex": "^1.8.0",
     "eslint-plugin-regex": "^1.8.0",
-    "file-loader": "^5.0.2",
+    "font-awesome": "^4.7.0",
     "handsontable": "=6.2.2",
     "handsontable": "=6.2.2",
-    "hard-source-webpack-plugin": "^0.13.1",
-    "i18next-browser-languagedetector": "^4.0.1",
-    "imports-loader": "^0.8.0",
+    "i18next-hmr": "^1.7.7",
     "jquery-slimscroll": "^1.3.8",
     "jquery-slimscroll": "^1.3.8",
-    "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
     "jquery.cookie": "~1.4.1",
     "jshint": "^2.13.0",
     "jshint": "^2.13.0",
     "load-css-file": "^1.0.0",
     "load-css-file": "^1.0.0",
-    "lodash-webpack-plugin": "^0.11.5",
-    "markdown-it": "^10.0.0",
-    "markdown-it-blockdiag": "^1.1.1",
-    "markdown-it-drawio-viewer": "^1.3.1",
-    "markdown-it-emoji": "^1.4.0",
-    "markdown-it-emoji-mart": "^0.1.1",
-    "markdown-it-footnote": "^3.0.1",
-    "markdown-it-mathjax": "^2.0.0",
-    "markdown-it-named-headers": "^0.0.4",
-    "markdown-it-plantuml": "^1.3.0",
-    "markdown-it-task-checkbox": "^1.0.6",
-    "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
     "markdown-table": "^1.1.1",
     "markdown-table": "^1.1.1",
-    "mini-css-extract-plugin": "^0.9.0",
+    "material-icons": "^1.11.3",
     "morgan": "^1.10.0",
     "morgan": "^1.10.0",
-    "node-dev": "^4.0.0",
+    "next-transpile-modules": "^9.0.0",
     "normalize-path": "^3.0.0",
     "normalize-path": "^3.0.0",
-    "null-loader": "^3.0.0",
-    "on-headers": "^1.0.1",
-    "optimize-css-assets-webpack-plugin": "^5.0.3",
     "penpal": "^4.0.0",
     "penpal": "^4.0.0",
     "plantuml-encoder": "^1.2.5",
     "plantuml-encoder": "^1.2.5",
-    "postcss-loader": "^3.0.0",
     "prettier": "^1.19.1",
     "prettier": "^1.19.1",
-    "react": "^16.8.3",
     "react-bootstrap-typeahead": "^5.2.2",
     "react-bootstrap-typeahead": "^5.2.2",
     "react-codemirror2": "^6.0.0",
     "react-codemirror2": "^6.0.0",
     "react-copy-to-clipboard": "^5.0.1",
     "react-copy-to-clipboard": "^5.0.1",
-    "react-dom": "^16.8.3",
     "react-dropzone": "^11.2.4",
     "react-dropzone": "^11.2.4",
     "react-frame-component": "^4.0.0",
     "react-frame-component": "^4.0.0",
     "react-hotkeys": "^2.0.0",
     "react-hotkeys": "^2.0.0",
-    "react-i18next": "^11.1.0",
+    "react-use-ripple": "^1.5.2",
     "react-waypoint": "^10.1.0",
     "react-waypoint": "^10.1.0",
     "reactstrap": "^8.9.0",
     "reactstrap": "^8.9.0",
     "replacestream": "^4.0.3",
     "replacestream": "^4.0.3",
     "reveal.js": "^4.3.1",
     "reveal.js": "^4.3.1",
-    "sass": "^1.43.4",
-    "sass-loader": "^10.1.1",
+    "sass": "^1.53.0",
+    "simple-line-icons": "^2.5.5",
     "simple-load-script": "^1.0.2",
     "simple-load-script": "^1.0.2",
     "simplebar-react": "^2.3.6",
     "simplebar-react": "^2.3.6",
     "socket.io-client": "^4.2.0",
     "socket.io-client": "^4.2.0",
     "sticky-events": "^3.4.11",
     "sticky-events": "^3.4.11",
-    "style-loader": "^1.0.0",
-    "styled-components": "^5.0.1",
     "swagger2openapi": "^5.3.1",
     "swagger2openapi": "^5.3.1",
-    "swr": "^1.1.2",
-    "terser-webpack-plugin": "^4.1.0",
+    "swr": "^1.3.0",
     "throttle-debounce": "^3.0.1",
     "throttle-debounce": "^3.0.1",
     "toastr": "^2.1.2",
     "toastr": "^2.1.2",
-    "ts-loader": "^8.3.0",
-    "ts-node": "^9.1.1",
     "ts-node-dev": "^2.0.0",
     "ts-node-dev": "^2.0.0",
     "tsc-alias": "^1.2.9",
     "tsc-alias": "^1.2.9",
-    "tsconfig-paths-webpack-plugin": "^3.5.1",
-    "unstated": "^2.1.1",
-    "webpack": "^4.46.0",
-    "webpack-assets-manifest": "^3.1.1",
-    "webpack-bundle-analyzer": "^3.9.0",
-    "webpack-cli": "^4.9.1"
+    "unstated": "^2.1.1"
   }
   }
 }
 }

+ 0 - 0
packages/app/public/static/locales/en_US/admin/admin.json → packages/app/public/static/locales/en_US/admin.json


+ 10 - 1
packages/app/public/static/locales/en_US/translation.json

@@ -1,4 +1,7 @@
 {
 {
+  "meta": {
+    "display_name": "English"
+  },
   "Help": "Help",
   "Help": "Help",
   "view": "View",
   "view": "View",
   "Edit": "Edit",
   "Edit": "Edit",
@@ -16,6 +19,7 @@
   "Move/Rename": "Move/Rename",
   "Move/Rename": "Move/Rename",
   "Redirected": "Redirected",
   "Redirected": "Redirected",
   "Unlinked": "Unlinked",
   "Unlinked": "Unlinked",
+  "unlink_redirection": "Unlink redirection",
   "Done": "Done",
   "Done": "Done",
   "Cancel": "Cancel",
   "Cancel": "Cancel",
   "Create": "Create",
   "Create": "Create",
@@ -27,6 +31,7 @@
   "New": "New",
   "New": "New",
   "Close": "Close",
   "Close": "Close",
   "Shortcuts": "Shortcuts",
   "Shortcuts": "Shortcuts",
+  "CustomSidebar": "Custom Sidebar",
   "eg": "e.g.",
   "eg": "e.g.",
   "add": "Add",
   "add": "Add",
   "Undo": "Undo",
   "Undo": "Undo",
@@ -196,6 +201,9 @@
     "page_not_exist": "This page does not exist.",
     "page_not_exist": "This page does not exist.",
     "page_not_exist_alert": "This page does not exist. Please create a new page."
     "page_not_exist_alert": "This page does not exist. Please create a new page."
   },
   },
+  "not_creatable_page": {
+    "could_not_creata_path": "Couldn't create path."
+  },
   "custom_navigation": {
   "custom_navigation": {
     "no_page_list": "There are no pages under this page.",
     "no_page_list": "There are no pages under this page.",
     "link_sharing_is_disabled": "Link sharing is disabled."
     "link_sharing_is_disabled": "Link sharing is disabled."
@@ -390,7 +398,8 @@
     "overwrite_scopes": "{{operation}} and Overwrite scopes of all descendants",
     "overwrite_scopes": "{{operation}} and Overwrite scopes of all descendants",
     "notice": {
     "notice": {
       "conflict": "Couldn't save the changes you made because someone else was editing this page. Please re-edit the affected section after reloading the page."
       "conflict": "Couldn't save the changes you made because someone else was editing this page. Please re-edit the affected section after reloading the page."
-    }
+    },
+    "changes_not_saved": "Changes you made may not be saved."
   },
   },
   "page_comment": {
   "page_comment": {
     "display_the_page_when_posting_this_comment": "Display the page when posting this comment",
     "display_the_page_when_posting_this_comment": "Display the page when posting this comment",

+ 0 - 2
packages/app/public/static/locales/index.js

@@ -1,2 +0,0 @@
-// !!DO NOT EDIT/REMOVE THIS FILE!!
-// entry point for @alienfast/i18next-loader

+ 0 - 0
packages/app/public/static/locales/ja_JP/admin/admin.json → packages/app/public/static/locales/ja_JP/admin.json


+ 10 - 1
packages/app/public/static/locales/ja_JP/translation.json

@@ -1,4 +1,7 @@
 {
 {
+  "meta": {
+    "display_name": "日本語"
+  },
   "Help": "ヘルプ",
   "Help": "ヘルプ",
   "view": "View",
   "view": "View",
   "Edit": "編集",
   "Edit": "編集",
@@ -16,6 +19,7 @@
   "Move/Rename": "移動/名前変更",
   "Move/Rename": "移動/名前変更",
   "Redirected": "リダイレクトされました",
   "Redirected": "リダイレクトされました",
   "Unlinked": "リダイレクト削除",
   "Unlinked": "リダイレクト削除",
+  "unlink_redirection": "リダイレクト削除",
   "Done": "完了",
   "Done": "完了",
   "Cancel": "キャンセル",
   "Cancel": "キャンセル",
   "Create": "作成",
   "Create": "作成",
@@ -27,6 +31,7 @@
   "New": "作成",
   "New": "作成",
   "Close": "閉じる",
   "Close": "閉じる",
   "Shortcuts": "ショートカット",
   "Shortcuts": "ショートカット",
+  "CustomSidebar": "カスタムサイドバー",
   "eg": "例:",
   "eg": "例:",
   "add": "追加",
   "add": "追加",
   "Undo": "元に戻す",
   "Undo": "元に戻す",
@@ -198,6 +203,9 @@
     "page_not_exist": "このページは存在しません。",
     "page_not_exist": "このページは存在しません。",
     "page_not_exist_alert": "このページは存在しません。新たに作成する必要があります。"
     "page_not_exist_alert": "このページは存在しません。新たに作成する必要があります。"
   },
   },
+  "not_creatable_page": {
+    "could_not_creata_path": "パスを作成できませんでした。"
+  },
   "custom_navigation": {
   "custom_navigation": {
     "no_page_list": "このページの配下にはページが存在しません。",
     "no_page_list": "このページの配下にはページが存在しません。",
     "link_sharing_is_disabled": "リンクのシェアは無効化されています"
     "link_sharing_is_disabled": "リンクのシェアは無効化されています"
@@ -390,7 +398,8 @@
     "overwrite_scopes": "{{operation}}と同時に全ての配下ページのスコープを上書き",
     "overwrite_scopes": "{{operation}}と同時に全ての配下ページのスコープを上書き",
     "notice": {
     "notice": {
       "conflict": "すでに他の人がこのページを編集していたため保存できませんでした。ページを再読み込み後、自分の編集箇所のみ再度編集してください。"
       "conflict": "すでに他の人がこのページを編集していたため保存できませんでした。ページを再読み込み後、自分の編集箇所のみ再度編集してください。"
-    }
+    },
+    "changes_not_saved": "変更が保存されていない可能性があります。"
   },
   },
   "page_comment": {
   "page_comment": {
     "display_the_page_when_posting_this_comment": "投稿時のページを表示する",
     "display_the_page_when_posting_this_comment": "投稿時のページを表示する",

+ 0 - 0
packages/app/public/static/locales/zh_CN/admin/admin.json → packages/app/public/static/locales/zh_CN/admin.json


+ 10 - 1
packages/app/public/static/locales/zh_CN/translation.json

@@ -1,4 +1,7 @@
 {
 {
+  "meta": {
+    "display_name": "简体中文"
+  },
   "Help": "帮助",
   "Help": "帮助",
   "view": "View",
   "view": "View",
 	"Edit": "编辑",
 	"Edit": "编辑",
@@ -17,6 +20,7 @@
 	"Move/Rename": "移动/重命名",
 	"Move/Rename": "移动/重命名",
 	"Redirected": "重定向",
 	"Redirected": "重定向",
 	"Unlinked": "Unlinked",
 	"Unlinked": "Unlinked",
+  "unlink_redirection": "取消链接重定向",
   "Done": "Done",
   "Done": "Done",
   "Cancel": "取消",
   "Cancel": "取消",
 	"Create": "创建",
 	"Create": "创建",
@@ -28,6 +32,7 @@
   "New": "新建",
   "New": "新建",
   "Close": "Close",
   "Close": "Close",
 	"Shortcuts": "快捷方式",
 	"Shortcuts": "快捷方式",
+  "CustomSidebar": "Custom Sidebar",
 	"eg": "e.g.",
 	"eg": "e.g.",
 	"add": "添加",
 	"add": "添加",
 	"Undo": "撤销",
 	"Undo": "撤销",
@@ -196,6 +201,9 @@
     "page_not_exist": "该页面不存在",
     "page_not_exist": "该页面不存在",
     "page_not_exist_alert": "该页面不存在,请创建一个新页面"
     "page_not_exist_alert": "该页面不存在,请创建一个新页面"
   },
   },
+  "not_creatable_page": {
+    "could_not_creata_path": "无法创建路径"
+  },
   "custom_navigation": {
   "custom_navigation": {
     "no_page_list": "There are no pages under this page.",
     "no_page_list": "There are no pages under this page.",
     "link_sharing_is_disabled": "链接共享已被禁用"
     "link_sharing_is_disabled": "链接共享已被禁用"
@@ -369,7 +377,8 @@
 		"overwrite_scopes": "{{operation}和覆盖所有子体的作用域",
 		"overwrite_scopes": "{{operation}和覆盖所有子体的作用域",
 		"notice": {
 		"notice": {
 			"conflict": "无法保存您所做的更改,因为其他人正在编辑此页。请在重新加载页面后重新编辑受影响的部分。"
 			"conflict": "无法保存您所做的更改,因为其他人正在编辑此页。请在重新加载页面后重新编辑受影响的部分。"
-		}
+		},
+    "changes_not_saved": "您所做的更改可能不会保存。"
   },
   },
   "page_comment": {
   "page_comment": {
     "display_the_page_when_posting_this_comment": "Display the page when posting this comment",
     "display_the_page_when_posting_this_comment": "Display the page when posting this comment",

+ 0 - 7
packages/app/resource/cdn-manifests.js

@@ -147,13 +147,6 @@ module.exports = {
         integrity: '',
         integrity: '',
       },
       },
     },
     },
-    {
-      name: 'jquery-ui',
-      url: 'https://cdn.jsdelivr.net/jquery.ui/1.11.4/jquery-ui.min.css',
-      args: {
-        integrity: '',
-      },
-    },
     {
     {
       name: 'highlight-theme-github',
       name: 'highlight-theme-github',
       url: 'https://cdn.jsdelivr.net/npm/highlight.js@9.13.0/styles/github.css',
       url: 'https://cdn.jsdelivr.net/npm/highlight.js@9.13.0/styles/github.css',

+ 0 - 3
packages/app/resource/locales/en_US/sandbox.md

@@ -245,9 +245,6 @@ You can create links using `[Display text](URL)`.
 
 
 ## Pukiwiki like linker
 ## Pukiwiki like linker
 
 
-(available by [weseek/growi-plugin-pukiwiki-like-linker
-](https://github.com/weseek/growi-plugin-pukiwiki-like-linker) )
-
 This is the most flexible linker.
 This is the most flexible linker.
 Both the page description and link address can be displayed on the page.
 Both the page description and link address can be displayed on the page.
 
 

+ 1 - 1
packages/app/resource/locales/en_US/welcome.md

@@ -1,7 +1,7 @@
 # :tada: Welcome to GROWI
 # :tada: Welcome to GROWI
 
 
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
-[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
+[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/weseek/growi/blob/master/LICENSE)
 
 
 GROWI is a Wiki for Individuals and Corporations | A knowledge base tool.
 GROWI is a Wiki for Individuals and Corporations | A knowledge base tool.
 Knowledge in companies, university laboratories, and clubs can be easily shared and anyone can edit the page.
 Knowledge in companies, university laboratories, and clubs can be easily shared and anyone can edit the page.

+ 0 - 3
packages/app/resource/locales/ja_JP/sandbox.md

@@ -244,9 +244,6 @@ ___
 
 
 ## Pukiwiki like linker
 ## Pukiwiki like linker
 
 
-(available by [weseek/growi-plugin-pukiwiki-like-linker
-](https://github.com/weseek/growi-plugin-pukiwiki-like-linker) )
-
 最も柔軟な Linker です。
 最も柔軟な Linker です。
 記述中のページを基点とした相対リンクと、表示テキストに対するリンクを同時に実現できます。
 記述中のページを基点とした相対リンクと、表示テキストに対するリンクを同時に実現できます。
 
 

+ 1 - 1
packages/app/resource/locales/ja_JP/welcome.md

@@ -1,6 +1,6 @@
 # :tada: GROWI へようこそ
 # :tada: GROWI へようこそ
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
-[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
+[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/weseek/growi/blob/master/LICENSE)
 
 
 GROWI は個人・法人向けの Wiki | ナレッジベースツールです。  
 GROWI は個人・法人向けの Wiki | ナレッジベースツールです。  
 会社や大学の研究室、サークルでのナレッジ情報を簡単に共有でき、作られたページは誰でも編集が可能です。
 会社や大学の研究室、サークルでのナレッジ情報を簡単に共有でき、作られたページは誰でも編集が可能です。

+ 0 - 3
packages/app/resource/locales/zh_CN/sandbox.md

@@ -245,9 +245,6 @@ You can create links using `[Display text](URL)`.
 
 
 ## Pukiwiki like linker
 ## Pukiwiki like linker
 
 
-(available by [weseek/growi-plugin-pukiwiki-like-linker
-](https://github.com/weseek/growi-plugin-pukiwiki-like-linker) )
-
 This is the most flexible linker.
 This is the most flexible linker.
 Both the page description and link address can be displayed on the page.
 Both the page description and link address can be displayed on the page.
 
 

+ 1 - 1
packages/app/resource/locales/zh_CN/welcome.md

@@ -1,7 +1,7 @@
 # :tada: 欢迎来到GROWI
 # :tada: 欢迎来到GROWI
 
 
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
 [![GitHub Releases](https://img.shields.io/github/release/weseek/growi.svg)](https://github.com/weseek/growi/releases/latest)
-[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
+[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/weseek/growi/blob/master/LICENSE)
 
 
 GROWI是一个针对个人和公司的Wiki - 一个知识库工具。
 GROWI是一个针对个人和公司的Wiki - 一个知识库工具。
 公司、大学实验室和俱乐部的知识可以轻松共享,任何人都可以编辑页面。
 公司、大学实验室和俱乐部的知识可以轻松共享,任何人都可以编辑页面。

+ 0 - 9
packages/app/src/client/boot.js

@@ -1,9 +0,0 @@
-import {
-  applyColorScheme,
-} from './util/color-scheme';
-import {
-  applyOldIos,
-} from './util/old-ios';
-
-applyColorScheme();
-applyOldIos();

+ 0 - 2
packages/app/src/client/legacy/crowi.js

@@ -1,8 +1,6 @@
 /* eslint-disable react/jsx-filename-extension */
 /* eslint-disable react/jsx-filename-extension */
 require('jquery.cookie');
 require('jquery.cookie');
 
 
-require('./thirdparty-js/waves');
-
 const Crowi = {};
 const Crowi = {};
 
 
 if (!window) {
 if (!window) {

File diff suppressed because it is too large
+ 0 - 0
packages/app/src/client/legacy/thirdparty-js/waves.js


+ 1 - 1
packages/app/src/client/models/BootstrapGrid.js

@@ -7,7 +7,7 @@ export default class BootstrapGrid {
 
 
   static ResponsiveSize = {
   static ResponsiveSize = {
     XS_SIZE: 'xs', SM_SIZE: 'sm', MD_SIZE: 'md',
     XS_SIZE: 'xs', SM_SIZE: 'sm', MD_SIZE: 'md',
-  }
+  };
 
 
   static validateColsRatios(colsRatios) {
   static validateColsRatios(colsRatios) {
 
 

+ 2 - 2
packages/app/src/client/models/Linker.js

@@ -26,14 +26,14 @@ export default class Linker {
     markdownLink: 'mdLink',
     markdownLink: 'mdLink',
     growiLink: 'growiLink',
     growiLink: 'growiLink',
     pukiwikiLink: 'pukiwikiLink',
     pukiwikiLink: 'pukiwikiLink',
-  }
+  };
 
 
   static patterns = {
   static patterns = {
     pukiwikiLinkWithLabel: /^\[\[(?<label>.+)>(?<link>.+)\]\]$/, // https://regex101.com/r/2fNmUN/2
     pukiwikiLinkWithLabel: /^\[\[(?<label>.+)>(?<link>.+)\]\]$/, // https://regex101.com/r/2fNmUN/2
     pukiwikiLinkWithoutLabel: /^\[\[(?<label>.+)\]\]$/, // https://regex101.com/r/S7w5Xu/1
     pukiwikiLinkWithoutLabel: /^\[\[(?<label>.+)\]\]$/, // https://regex101.com/r/S7w5Xu/1
     growiLink: /^\[(?<label>\/.+)\]$/, // https://regex101.com/r/DJfkYf/3
     growiLink: /^\[(?<label>\/.+)\]$/, // https://regex101.com/r/DJfkYf/3
     markdownLink: /^\[(?<label>.*)\]\((?<link>.*)\)$/, // https://regex101.com/r/DZCKP3/2
     markdownLink: /^\[(?<label>.*)\]\((?<link>.*)\)$/, // https://regex101.com/r/DZCKP3/2
-  }
+  };
 
 
   initWhenMarkdownLink() {
   initWhenMarkdownLink() {
     // fill label with link if empty
     // fill label with link if empty

+ 4 - 4
packages/app/src/client/models/MarkdownTable.js

@@ -1,6 +1,6 @@
+import csvToMarkdown from 'csv-to-markdown-table';
 import markdownTable from 'markdown-table';
 import markdownTable from 'markdown-table';
 import stringWidth from 'string-width';
 import stringWidth from 'string-width';
-import csvToMarkdown from 'csv-to-markdown-table';
 
 
 // https://github.com/markdown-it/markdown-it/blob/d29f421927e93e88daf75f22089a3e732e195bd2/lib/rules_block/table.js#L83
 // https://github.com/markdown-it/markdown-it/blob/d29f421927e93e88daf75f22089a3e732e195bd2/lib/rules_block/table.js#L83
 // https://regex101.com/r/7BN2fR/7
 // https://regex101.com/r/7BN2fR/7
@@ -8,9 +8,6 @@ const tableAlignmentLineRE = /^[-:|][-:|\s]*$/;
 const tableAlignmentLineNegRE = /^[^-:]*$/; // it is need to check to ignore empty row which is matched above RE
 const tableAlignmentLineNegRE = /^[^-:]*$/; // it is need to check to ignore empty row which is matched above RE
 const linePartOfTableRE = /^\|[^\r\n]*|[^\r\n]*\|$|([^|\r\n]+\|[^|\r\n]*)+/; // own idea
 const linePartOfTableRE = /^\|[^\r\n]*|[^\r\n]*\|$|([^|\r\n]+\|[^|\r\n]*)+/; // own idea
 
 
-// set up DOMParser
-const domParser = new (window.DOMParser)();
-
 const defaultOptions = { stringLength: stringWidth };
 const defaultOptions = { stringLength: stringWidth };
 
 
 /**
 /**
@@ -67,6 +64,9 @@ export default class MarkdownTable {
    * The error message is a innerHTML, so must not assign it into element.innerHTML because it can lead to Mutation-based XSS
    * The error message is a innerHTML, so must not assign it into element.innerHTML because it can lead to Mutation-based XSS
    */
    */
   static fromHTMLTableTag(str) {
   static fromHTMLTableTag(str) {
+    // set up DOMParser
+    const domParser = new (window.DOMParser)();
+
     // use DOMParser to prevent DOM based XSS (https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
     // use DOMParser to prevent DOM based XSS (https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
     const dom = domParser.parseFromString(str, 'application/xml');
     const dom = domParser.parseFromString(str, 'application/xml');
 
 

+ 5 - 4
packages/app/src/client/services/AdminAppContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Post, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Post, apiv3Put } from '../util/apiv3-client';
@@ -11,13 +12,13 @@ export default class AdminAppContainer extends Container {
   constructor() {
   constructor() {
     super();
     super();
 
 
-    this.dummyTitle = 0;
-    this.dummyTitleForError = 1;
+    if (isServer()) {
+      return;
+    }
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      // set dummy value tile for using suspense
-      title: this.dummyTitle,
+      title: '',
       confidential: '',
       confidential: '',
       globalLang: '',
       globalLang: '',
       isEmailPublishedForNewUser: true,
       isEmailPublishedForNewUser: true,

+ 5 - 6
packages/app/src/client/services/AdminBasicSecurityContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -16,15 +17,13 @@ export default class AdminBasicSecurityContainer extends Container {
   constructor() {
   constructor() {
     super();
     super();
 
 
-    this.dummyIsSameUsernameTreatedAsIdenticalUser = 0;
-    this.dummyIsSameUsernameTreatedAsIdenticalUserForError = 1;
+    if (isServer()) {
+      return;
+    }
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
-      // set dummy value tile for using suspense
-      isSameUsernameTreatedAsIdenticalUser: this.dummyIsSameUsernameTreatedAsIdenticalUser,
+      isSameUsernameTreatedAsIdenticalUser: false,
     };
     };
-
   }
   }
 
 
   /**
   /**

+ 4 - 53
packages/app/src/client/services/AdminCustomizeContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -17,13 +18,12 @@ export default class AdminCustomizeContainer extends Container {
   constructor() {
   constructor() {
     super();
     super();
 
 
-    this.dummyCurrentTheme = 0;
-    this.dummyCurrentThemeForError = 1;
+    if (isServer()) {
+      return;
+    }
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      // set dummy value tile for using suspense
-      currentTheme: this.dummyCurrentTheme,
       isEnabledTimeline: false,
       isEnabledTimeline: false,
       isSavedStatesOfTabChanges: false,
       isSavedStatesOfTabChanges: false,
       isEnabledAttachTitleHeader: false,
       isEnabledAttachTitleHeader: false,
@@ -80,7 +80,6 @@ export default class AdminCustomizeContainer extends Container {
       const { customizeParams } = response.data;
       const { customizeParams } = response.data;
 
 
       this.setState({
       this.setState({
-        currentTheme: customizeParams.themeType,
         isEnabledTimeline: customizeParams.isEnabledTimeline,
         isEnabledTimeline: customizeParams.isEnabledTimeline,
         isSavedStatesOfTabChanges: customizeParams.isSavedStatesOfTabChanges,
         isSavedStatesOfTabChanges: customizeParams.isSavedStatesOfTabChanges,
         isEnabledAttachTitleHeader: customizeParams.isEnabledAttachTitleHeader,
         isEnabledAttachTitleHeader: customizeParams.isEnabledAttachTitleHeader,
@@ -109,17 +108,6 @@ export default class AdminCustomizeContainer extends Container {
     }
     }
   }
   }
 
 
-  /**
-   * Switch themeType
-   */
-  switchThemeType(themeName) {
-    this.setState({ currentTheme: themeName });
-
-    // preview if production
-    if (process.env.NODE_ENV !== 'development') {
-      this.previewTheme(themeName);
-    }
-  }
 
 
   /**
   /**
    * Switch enabledTimeLine
    * Switch enabledTimeLine
@@ -239,24 +227,6 @@ export default class AdminCustomizeContainer extends Container {
     this.setState({ currentCustomizeScript: inpuValue });
     this.setState({ currentCustomizeScript: inpuValue });
   }
   }
 
 
-  /**
-   * Preview theme
-   * @param {string} themeName
-   */
-  async previewTheme(themeName) {
-    try {
-      // get theme asset path
-      const response = await apiv3Get('/customize-setting/theme/asset-path', { themeName });
-      const { assetPath } = response.data;
-
-      const themeLink = document.getElementById('grw-theme-link');
-      themeLink.setAttribute('href', assetPath);
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
   /**
   /**
    * Preview hljs style
    * Preview hljs style
    * @param {string} styleId
    * @param {string} styleId
@@ -268,25 +238,6 @@ export default class AdminCustomizeContainer extends Container {
     styleLInk.href = styleLInk.href.replace(/[^/]+\.css$/, `${styleId}.css`);
     styleLInk.href = styleLInk.href.replace(/[^/]+\.css$/, `${styleId}.css`);
   }
   }
 
 
-  /**
-   * Update theme
-   * @memberOf AdminCustomizeContainer
-   */
-  async updateCustomizeTheme() {
-    try {
-      const response = await apiv3Put('/customize-setting/theme', {
-        themeType: this.state.currentTheme,
-      });
-      const { customizedParams } = response.data;
-      this.setState({
-        themeType: customizedParams.themeType,
-      });
-    }
-    catch (err) {
-      logger.error(err);
-      throw new Error('Failed to update data');
-    }
-  }
 
 
   /**
   /**
    * Update function
    * Update function

+ 5 - 0
packages/app/src/client/services/AdminExternalAccountsContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -17,6 +18,10 @@ export default class AdminExternalAccountsContainer extends Container {
   constructor() {
   constructor() {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.state = {
     this.state = {
       externalAccounts: [],
       externalAccounts: [],
       totalAccounts: 0,
       totalAccounts: 0,

+ 5 - 5
packages/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import {
 import {
@@ -18,15 +19,15 @@ export default class AdminGeneralSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
-    this.dummyCurrentRestrictGuestMode = 0;
-    this.dummyCurrentRestrictGuestModeForError = 1;
+    if (isServer()) {
+      return;
+    }
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
       sessionMaxAge: null,
       sessionMaxAge: null,
       wikiMode: '',
       wikiMode: '',
-      // set dummy value tile for using suspense
-      currentRestrictGuestMode: this.dummyCurrentRestrictGuestMode,
+      currentRestrictGuestMode: '',
       currentPageDeletionAuthority: PageSingleDeleteConfigValue.AdminOnly,
       currentPageDeletionAuthority: PageSingleDeleteConfigValue.AdminOnly,
       currentPageRecursiveDeletionAuthority: PageRecursiveDeleteConfigValue.Inherit,
       currentPageRecursiveDeletionAuthority: PageRecursiveDeleteConfigValue.Inherit,
       currentPageCompleteDeletionAuthority: PageSingleDeleteCompConfigValue.AdminOnly,
       currentPageCompleteDeletionAuthority: PageSingleDeleteCompConfigValue.AdminOnly,
@@ -37,7 +38,6 @@ export default class AdminGeneralSecurityContainer extends Container {
       expandOtherOptionsForCompleteDeletion: false,
       expandOtherOptionsForCompleteDeletion: false,
       isShowRestrictedByOwner: false,
       isShowRestrictedByOwner: false,
       isShowRestrictedByGroup: false,
       isShowRestrictedByGroup: false,
-      appSiteUrl: appContainer.config.crowi.url || '',
       isLocalEnabled: false,
       isLocalEnabled: false,
       isLdapEnabled: false,
       isLdapEnabled: false,
       isSamlEnabled: false,
       isSamlEnabled: false,

+ 5 - 2
packages/app/src/client/services/AdminGitHubSecurityContainer.js

@@ -1,4 +1,4 @@
-import { pathUtils } from '@growi/core';
+import { isServer, pathUtils } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
@@ -18,12 +18,15 @@ export default class AdminGitHubSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.dummyGithubClientId = 0;
     this.dummyGithubClientId = 0;
     this.dummyGithubClientIdForError = 1;
     this.dummyGithubClientIdForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/github/callback'),
       // set dummy value tile for using suspense
       // set dummy value tile for using suspense
       githubClientId: this.dummyGithubClientId,
       githubClientId: this.dummyGithubClientId,
       githubClientSecret: '',
       githubClientSecret: '',

+ 5 - 2
packages/app/src/client/services/AdminGoogleSecurityContainer.js

@@ -1,4 +1,4 @@
-import { pathUtils } from '@growi/core';
+import { isServer, pathUtils } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
@@ -18,12 +18,15 @@ export default class AdminGoogleSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.dummyGoogleClientId = 0;
     this.dummyGoogleClientId = 0;
     this.dummyGoogleClientIdForError = 1;
     this.dummyGoogleClientIdForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/google/callback'),
       // set dummy value tile for using suspense
       // set dummy value tile for using suspense
       googleClientId: this.dummyGoogleClientId,
       googleClientId: this.dummyGoogleClientId,
       googleClientSecret: '',
       googleClientSecret: '',

+ 5 - 0
packages/app/src/client/services/AdminHomeContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -17,6 +18,10 @@ export default class AdminHomeContainer extends Container {
   constructor() {
   constructor() {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.copyStateValues = {
     this.copyStateValues = {
       DEFAULT: 'default',
       DEFAULT: 'default',
       DONE: 'done',
       DONE: 'done',

+ 6 - 4
packages/app/src/client/services/AdminImportContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -17,14 +18,15 @@ export default class AdminImportContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyEsaTeamName = 0;
-    this.dummyEsaTeamNameForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      // set dummy value tile for using suspense
-      esaTeamName: this.dummyEsaTeamName,
+      esaTeamName: '',
       esaAccessToken: '',
       esaAccessToken: '',
       qiitaTeamName: '',
       qiitaTeamName: '',
       qiitaAccessToken: '',
       qiitaAccessToken: '',

+ 6 - 4
packages/app/src/client/services/AdminLdapSecurityContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -16,14 +17,15 @@ export default class AdminLdapSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyServerUrl = 0;
-    this.dummyServerUrlForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      // set dummy value tile for using suspense
-      serverUrl: this.dummyServerUrl,
+      serverUrl: '',
       isUserBind: false,
       isUserBind: false,
       ldapBindDN: '',
       ldapBindDN: '',
       ldapBindDNPassword: '',
       ldapBindDNPassword: '',

+ 5 - 0
packages/app/src/client/services/AdminLocalSecurityContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -15,6 +16,10 @@ export default class AdminLocalSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
     this.dummyRegistrationMode = 0;
     this.dummyRegistrationMode = 0;
     this.dummyRegistrationModeForError = 1;
     this.dummyRegistrationModeForError = 1;

+ 6 - 4
packages/app/src/client/services/AdminMarkDownContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
@@ -11,14 +12,15 @@ export default class AdminMarkDownContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyIsEnabledLinebreaks = 0;
-    this.dummyIsEnabledLinebreaksForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      // set dummy value tile for using suspense
-      isEnabledLinebreaks: this.dummyIsEnabledLinebreaks,
+      isEnabledLinebreaks: false,
       isEnabledLinebreaksInComments: false,
       isEnabledLinebreaksInComments: false,
       adminPreferredIndentSize: 4,
       adminPreferredIndentSize: 4,
       isIndentSizeForced: false,
       isIndentSizeForced: false,

+ 5 - 0
packages/app/src/client/services/AdminNotificationContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import {
 import {
@@ -13,6 +14,10 @@ export default class AdminNotificationContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {

+ 6 - 6
packages/app/src/client/services/AdminOidcSecurityContainer.js

@@ -1,4 +1,4 @@
-import { pathUtils } from '@growi/core';
+import { isServer, pathUtils } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
@@ -18,15 +18,15 @@ export default class AdminOidcSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyOidcProviderName = 0;
-    this.dummyOidcProviderNameForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
-      callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/oidc/callback'),
-      // set dummy value tile for using suspense
-      oidcProviderName: this.dummyOidcProviderName,
+      oidcProviderName: '',
       oidcIssuerHost: '',
       oidcIssuerHost: '',
       oidcAuthorizationEndpoint: '',
       oidcAuthorizationEndpoint: '',
       oidcTokenEndpoint: '',
       oidcTokenEndpoint: '',

+ 6 - 6
packages/app/src/client/services/AdminSamlSecurityContainer.js

@@ -1,4 +1,4 @@
-import { pathUtils } from '@growi/core';
+import { isServer, pathUtils } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
@@ -18,18 +18,18 @@ export default class AdminSamlSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummySamlEntryPoint = 0;
-    this.dummySamlEntryPointForError = 1;
 
 
     this.state = {
     this.state = {
       retrieveError: null,
       retrieveError: null,
       // TODO GW-1324 ABLCRure DB value takes precedence
       // TODO GW-1324 ABLCRure DB value takes precedence
       useOnlyEnvVars: false,
       useOnlyEnvVars: false,
-      callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/saml/callback'),
       missingMandatoryConfigKeys: [],
       missingMandatoryConfigKeys: [],
-      // set dummy value tile for using suspense
-      samlEntryPoint: this.dummySamlEntryPoint,
+      samlEntryPoint: '',
       samlIssuer: '',
       samlIssuer: '',
       samlCert: '',
       samlCert: '',
       samlAttrMapId: '',
       samlAttrMapId: '',

+ 6 - 3
packages/app/src/client/services/AdminSlackIntegrationLegacyContainer.js

@@ -1,3 +1,4 @@
+import { isServer } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
@@ -11,15 +12,17 @@ export default class AdminSlackIntegrationLegacyContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyWebhookUrl = 0;
-    this.dummyWebhookUrlForError = 1;
 
 
     this.state = {
     this.state = {
       isSlackbotConfigured: false,
       isSlackbotConfigured: false,
       retrieveError: null,
       retrieveError: null,
       selectSlackOption: 'Incoming Webhooks',
       selectSlackOption: 'Incoming Webhooks',
-      webhookUrl: this.dummyWebhookUrl,
+      webhookUrl: '',
       isIncomingWebhookPrioritized: false,
       isIncomingWebhookPrioritized: false,
       slackToken: '',
       slackToken: '',
     };
     };

+ 1 - 24
packages/app/src/client/services/AdminSocketIoContainer.js

@@ -1,25 +1,2 @@
-import SocketIoContainer from './SocketIoContainer';
-import { toastError } from '../util/apiNotification';
 
 
-/**
- * A subclass of SocketIoContainer for /admin namespace
- */
-export default class AdminSocketIoContainer extends SocketIoContainer {
-
-  constructor(appContainer) {
-    super(appContainer, '/admin');
-
-    // show toastr
-    this.socket.on('error', (error) => {
-      toastError(new Error(error));
-    });
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'AdminSocketIoContainer';
-  }
-
-}
+export default class AdminSocketIoContainer {}

+ 6 - 6
packages/app/src/client/services/AdminTwitterSecurityContainer.js

@@ -1,4 +1,4 @@
-import { pathUtils } from '@growi/core';
+import { isServer, pathUtils } from '@growi/core';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
@@ -18,14 +18,14 @@ export default class AdminTwitterSecurityContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
-    this.dummyTwitterConsumerKey = 0;
-    this.dummyTwitterConsumerKeyForError = 1;
 
 
     this.state = {
     this.state = {
-      callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/twitter/callback'),
-      // set dummy value tile for using suspense
-      twitterConsumerKey: this.dummyTwitterConsumerKey,
+      twitterConsumerKey: '',
       twitterConsumerSecret: '',
       twitterConsumerSecret: '',
       isSameUsernameTreatedAsIdenticalUser: false,
       isSameUsernameTreatedAsIdenticalUser: false,
     };
     };

+ 0 - 197
packages/app/src/client/services/AdminUserGroupDetailContainer.js

@@ -1,197 +0,0 @@
-/*
- * TODO 85062: AdminUserGroupDetailContainer is under transplantation to UserGroupDetailPage.tsx
- */
-
-import { Container } from 'unstated';
-
-import loggerFactory from '~/utils/logger';
-
-import { toastError } from '../util/apiNotification';
-
-import {
-  apiv3Get, apiv3Delete, apiv3Put, apiv3Post,
-} from '~/client/util/apiv3-client';
-
-// eslint-disable-next-line no-unused-vars
-const logger = loggerFactory('growi:services:AdminUserGroupDetailContainer');
-
-/**
- * Service container for admin user group detail page (UserGroupDetailPage.jsx)
- * @extends {Container} unstated Container
- */
-export default class AdminUserGroupDetailContainer extends Container {
-
-  constructor(appContainer) {
-    super();
-
-    this.appContainer = appContainer;
-
-    const rootElem = document.getElementById('admin-user-group-detail');
-
-    if (rootElem == null) {
-      return;
-    }
-
-    this.state = {
-      // TODO: [SPA] get userGroup from props
-      userGroup: JSON.parse(rootElem.getAttribute('data-user-group')),
-      userGroupRelations: [], // For user list
-
-      // TODO 85062: /_api/v3/user-groups/children?include_grand_child=boolean
-      childUserGroups: [], // TODO 85062: fetch data on init (findChildGroupsByParentIds) For child group list
-      grandChildUserGroups: [], // TODO 85062: fetch data on init (findChildGroupsByParentIds) For child group list
-
-      childUserGroupRelations: [], // TODO 85062: fetch data on init (findRelationsByGroupIds) For child group list users
-      relatedPages: [], // For page list
-      isUserGroupUserModalOpen: false,
-      searchType: 'partial',
-      isAlsoMailSearched: false,
-      isAlsoNameSearched: false,
-    };
-
-    this.init();
-
-    this.switchIsAlsoMailSearched = this.switchIsAlsoMailSearched.bind(this);
-    this.switchIsAlsoNameSearched = this.switchIsAlsoNameSearched.bind(this);
-    this.openUserGroupUserModal = this.openUserGroupUserModal.bind(this);
-    this.closeUserGroupUserModal = this.closeUserGroupUserModal.bind(this);
-    this.addUserByUsername = this.addUserByUsername.bind(this);
-    this.removeUserByUsername = this.removeUserByUsername.bind(this);
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'AdminUserGroupDetailContainer';
-  }
-
-  /**
-   * retrieve user group data
-   */
-  async init() {
-    try {
-      const [
-        userGroupRelations,
-        relatedPages,
-      ] = await Promise.all([
-        apiv3Get(`/user-groups/${this.state.userGroup._id}/user-group-relations`).then((res) => { return res.data.userGroupRelations }),
-        apiv3Get(`/user-groups/${this.state.userGroup._id}/pages`).then((res) => { return res.data.pages }),
-      ]);
-
-      await this.setState({
-        userGroupRelations,
-        relatedPages,
-      });
-    }
-    catch (err) {
-      logger.error(err);
-      toastError(new Error('Failed to fetch data'));
-    }
-  }
-
-  /**
-   * switch isAlsoMailSearched
-   */
-  switchIsAlsoMailSearched() {
-    this.setState({ isAlsoMailSearched: !this.state.isAlsoMailSearched });
-  }
-
-  /**
-   * switch isAlsoNameSearched
-   */
-  switchIsAlsoNameSearched() {
-    this.setState({ isAlsoNameSearched: !this.state.isAlsoNameSearched });
-  }
-
-  /**
-   * switch searchType
-   */
-  switchSearchType(searchType) {
-    this.setState({ searchType });
-  }
-
-  /**
-   * update user group
-   *
-   * @memberOf AdminUserGroupDetailContainer
-   * @param {object} param update param for user group
-   * @return {object} response object
-   */
-  async updateUserGroup(param) {
-    const res = await apiv3Put(`/user-groups/${this.state.userGroup._id}`, param);
-    const { userGroup } = res.data;
-
-    await this.setState({ userGroup });
-
-    return res;
-  }
-
-  /**
-   * open a modal
-   *
-   * @memberOf AdminUserGroupDetailContainer
-   */
-  async openUserGroupUserModal() {
-    await this.setState({ isUserGroupUserModalOpen: true });
-  }
-
-  /**
-   * close a modal
-   *
-   * @memberOf AdminUserGroupDetailContainer
-   */
-  async closeUserGroupUserModal() {
-    await this.setState({ isUserGroupUserModalOpen: false });
-  }
-
-  /**
-   * search user for invitation
-   * @param {string} username username of the user to be searched
-   */
-  async fetchApplicableUsers(searchWord) {
-    const res = await apiv3Get(`/user-groups/${this.state.userGroup._id}/unrelated-users`, {
-      searchWord,
-      searchType: this.state.searchType,
-      isAlsoMailSearched: this.state.isAlsoMailSearched,
-      isAlsoNameSearched: this.state.isAlsoNameSearched,
-    });
-
-    const { users } = res.data;
-
-    return users;
-  }
-
-
-  /**
-   * update user group
-   *
-   * @memberOf AdminUserGroupDetailContainer
-   * @param {string} username username of the user to be added to the group
-   */
-  async addUserByUsername(username) {
-    const res = await apiv3Post(`/user-groups/${this.state.userGroup._id}/users/${username}`);
-
-    // do not add users for ducaplicate
-    if (res.data.userGroupRelation == null) { return }
-
-    this.init();
-  }
-
-  /**
-   * update user group
-   *
-   * @memberOf AdminUserGroupDetailContainer
-   * @param {string} username username of the user to be removed from the group
-   */
-  async removeUserByUsername(username) {
-    const res = await apiv3Delete(`/user-groups/${this.state.userGroup._id}/users/${username}`);
-
-    this.setState((prevState) => {
-      return {
-        userGroupRelations: prevState.userGroupRelations.filter((u) => { return u._id !== res.data.userGroupRelation._id }),
-      };
-    });
-  }
-
-}

+ 5 - 4
packages/app/src/client/services/AdminUsersContainer.js

@@ -1,14 +1,11 @@
+import { isServer } from '@growi/core';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
-import loggerFactory from '~/utils/logger';
-
 import {
 import {
   apiv3Delete, apiv3Get, apiv3Post, apiv3Put,
   apiv3Delete, apiv3Get, apiv3Post, apiv3Put,
 } from '../util/apiv3-client';
 } from '../util/apiv3-client';
 
 
-// eslint-disable-next-line no-unused-vars
-const logger = loggerFactory('growi:services:AdminUserGroupDetailContainer');
 
 
 /**
 /**
  * Service container for admin users page (Users.jsx)
  * Service container for admin users page (Users.jsx)
@@ -19,6 +16,10 @@ export default class AdminUsersContainer extends Container {
   constructor(appContainer) {
   constructor(appContainer) {
     super();
     super();
 
 
+    if (isServer()) {
+      return;
+    }
+
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {

+ 12 - 13
packages/app/src/client/services/AppContainer.js

@@ -1,9 +1,6 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
-
-import GrowiRenderer, { generatePreviewRenderer } from '~/services/renderer/growi-renderer';
-
-import { i18nFactory } from '../util/i18n';
+// import { i18nFactory } from '../util/i18n';
 
 
 /**
 /**
  * Service container related to options for Application
  * Service container related to options for Application
@@ -23,7 +20,7 @@ export default class AppContainer extends Container {
       const currentUser = JSON.parse(currentUserElem.textContent);
       const currentUser = JSON.parse(currentUserElem.textContent);
       userLocaleId = currentUser?.lang;
       userLocaleId = currentUser?.lang;
     }
     }
-    this.i18n = i18nFactory(userLocaleId);
+    // this.i18n = i18nFactory(userLocaleId);
 
 
     this.containerInstances = {};
     this.containerInstances = {};
     this.componentInstances = {};
     this.componentInstances = {};
@@ -59,17 +56,19 @@ export default class AppContainer extends Container {
   }
   }
 
 
   injectToWindow() {
   injectToWindow() {
-    window.appContainer = this;
+    // for fix lint error
+
+    // window.appContainer = this;
 
 
-    const growiRenderer = new GrowiRenderer(this.getConfig());
-    growiRenderer.init();
+    // const growiRenderer = new GrowiRenderer(this.getConfig());
+    // growiRenderer.init();
 
 
-    window.growiRenderer = growiRenderer;
+    // window.growiRenderer = growiRenderer;
 
 
-    // backward compatibility
-    window.crowi = this;
-    window.crowiRenderer = window.growiRenderer;
-    window.crowiPlugin = window.growiPlugin;
+    // // backward compatibility
+    // window.crowi = this;
+    // window.crowiRenderer = window.growiRenderer;
+    // window.crowiPlugin = window.growiPlugin;
   }
   }
 
 
   getConfig() {
   getConfig() {

+ 34 - 51
packages/app/src/client/services/ContextExtractor.tsx

@@ -1,28 +1,28 @@
+/* eslint-disable */
 import React, { FC, useEffect, useState } from 'react';
 import React, { FC, useEffect, useState } from 'react';
 
 
 import { pagePathUtils } from '@growi/core';
 import { pagePathUtils } from '@growi/core';
 
 
 import { CustomWindow } from '~/interfaces/global';
 import { CustomWindow } from '~/interfaces/global';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import { generatePreviewRenderer } from '~/services/renderer/growi-renderer';
-import { useRendererSettings } from '~/stores/renderer';
+// import { generatePreviewRenderer } from '~/services/renderer/growi-renderer';
 import {
 import {
   useIsDeviceSmallerThanMd, useIsDeviceSmallerThanLg,
   useIsDeviceSmallerThanMd, useIsDeviceSmallerThanLg,
   usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
   usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
-  useSelectedGrant, useSelectedGrantGroupId, useSelectedGrantGroupName,
+  useSelectedGrant,
 } from '~/stores/ui';
 } from '~/stores/ui';
 import { useSetupGlobalSocket, useSetupGlobalAdminSocket } from '~/stores/websocket';
 import { useSetupGlobalSocket, useSetupGlobalAdminSocket } from '~/stores/websocket';
 
 
 import {
 import {
   useSiteUrl,
   useSiteUrl,
-  useCurrentCreatedAt, useDeleteUsername, useDeletedAt, useHasChildren, useHasDraftOnHackmd,
+  useDeleteUsername, useDeletedAt, useHasChildren, useHasDraftOnHackmd,
   useIsNotCreatable, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   useIsNotCreatable, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
   useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
-  useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
-  useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath, useHasParent,
+  useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUser, useTargetAndAncestors,
+  useIsSearchPage, useIsForbidden, useIsIdenticalPath, useHasParent,
   useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader,
   useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader,
-  useDefaultIndentSize, useIsIndentSizeForced, useCsrfToken, useIsEmptyPage, useEmptyPageId, useGrowiVersion, useAuditLogEnabled,
-  useActivityExpirationSeconds, useAuditLogAvailableActions, useGrowiRendererConfig,
+  useDefaultIndentSize, useIsIndentSizeForced, useCsrfToken, useGrowiVersion, useAuditLogEnabled,
+  useActivityExpirationSeconds, useAuditLogAvailableActions, useRendererConfig,
 } from '../../stores/context';
 } from '../../stores/context';
 
 
 const { isTrashPage: _isTrashPage } = pagePathUtils;
 const { isTrashPage: _isTrashPage } = pagePathUtils;
@@ -63,17 +63,9 @@ const ContextExtractorOnce: FC = () => {
   const path = decodeURI(mainContent?.getAttribute('data-path') || '');
   const path = decodeURI(mainContent?.getAttribute('data-path') || '');
   // assign `null` to avoid returning empty string
   // assign `null` to avoid returning empty string
   const pageId = mainContent?.getAttribute('data-page-id') || null;
   const pageId = mainContent?.getAttribute('data-page-id') || null;
-  const emptyPageId = notFoundContext?.getAttribute('data-page-id') || null;
 
 
   const revisionCreatedAt = +(mainContent?.getAttribute('data-page-revision-created') || '');
   const revisionCreatedAt = +(mainContent?.getAttribute('data-page-revision-created') || '');
 
 
-  // createdAt
-  const createdAtAttribute = mainContent?.getAttribute('data-page-created-at');
-  const createdAt: Date | null = (createdAtAttribute != null) ? new Date(createdAtAttribute) : null;
-  // updatedAt
-  const updatedAtAttribute = mainContent?.getAttribute('data-page-updated-at');
-  const updatedAt: Date | null = (updatedAtAttribute != null) ? new Date(updatedAtAttribute) : null;
-
   const deletedAt = mainContent?.getAttribute('data-page-deleted-at') || null;
   const deletedAt = mainContent?.getAttribute('data-page-deleted-at') || null;
   const isIdenticalPath = JSON.parse(mainContent?.getAttribute('data-identical-path') || jsonNull) ?? false;
   const isIdenticalPath = JSON.parse(mainContent?.getAttribute('data-identical-path') || jsonNull) ?? false;
   const isUserPage = JSON.parse(mainContent?.getAttribute('data-page-user') || jsonNull) != null;
   const isUserPage = JSON.parse(mainContent?.getAttribute('data-page-user') || jsonNull) != null;
@@ -91,8 +83,6 @@ const ContextExtractorOnce: FC = () => {
   const deleteUsername = mainContent?.getAttribute('data-page-delete-username') || null;
   const deleteUsername = mainContent?.getAttribute('data-page-delete-username') || null;
   const pageIdOnHackmd = mainContent?.getAttribute('data-page-id-on-hackmd') || null;
   const pageIdOnHackmd = mainContent?.getAttribute('data-page-id-on-hackmd') || null;
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
-  const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
-  const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
   const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
   const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
   const notFoundTargetPathOrId = JSON.parse(notFoundContentForPt?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
   const notFoundTargetPathOrId = JSON.parse(notFoundContentForPt?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
   const isSearchPage = document.getElementById('search-page') != null;
   const isSearchPage = document.getElementById('search-page') != null;
@@ -129,26 +119,25 @@ const ContextExtractorOnce: FC = () => {
   useActivityExpirationSeconds(configByContextHydrate.activityExpirationSeconds);
   useActivityExpirationSeconds(configByContextHydrate.activityExpirationSeconds);
   useAuditLogAvailableActions(configByContextHydrate.auditLogAvailableActions);
   useAuditLogAvailableActions(configByContextHydrate.auditLogAvailableActions);
   useGrowiVersion(configByContextHydrate.crowi.version);
   useGrowiVersion(configByContextHydrate.crowi.version);
-  useRendererSettings({
+  useRendererConfig({
     isEnabledLinebreaks: configByContextHydrate.isEnabledLinebreaks,
     isEnabledLinebreaks: configByContextHydrate.isEnabledLinebreaks,
     isEnabledLinebreaksInComments: configByContextHydrate.isEnabledLinebreaksInComments,
     isEnabledLinebreaksInComments: configByContextHydrate.isEnabledLinebreaksInComments,
     adminPreferredIndentSize: configByContextHydrate.adminPreferredIndentSize,
     adminPreferredIndentSize: configByContextHydrate.adminPreferredIndentSize,
     isIndentSizeForced: configByContextHydrate.isIndentSizeForced,
     isIndentSizeForced: configByContextHydrate.isIndentSizeForced,
-  });
-  useGrowiRendererConfig({
+
     isEnabledXssPrevention: configByContextHydrate.isEnabledXssPrevention,
     isEnabledXssPrevention: configByContextHydrate.isEnabledXssPrevention,
     attrWhiteList: configByContextHydrate.attrWhiteList,
     attrWhiteList: configByContextHydrate.attrWhiteList,
     tagWhiteList: configByContextHydrate.tagWhiteList,
     tagWhiteList: configByContextHydrate.tagWhiteList,
     highlightJsStyleBorder: configByContextHydrate.highlightJsStyleBorder,
     highlightJsStyleBorder: configByContextHydrate.highlightJsStyleBorder,
-    env: {
-      MATHJAX: configByContextHydrate.env.MATHJAX,
-      PLANTUML_URI: configByContextHydrate.env.PLANTUML_URI,
-      BLOCKDIAG_URI: configByContextHydrate.env.BLOCKDIAG_URI,
-    },
+
+    plantumlUri: configByContextHydrate.env.PLANTUML_URI,
+    blockdiagUri: configByContextHydrate.env.BLOCKDIAG_URI,
   });
   });
+  // useNoCdn(configByContextHydrate.env.NO_CDN);
+  // useUploadableImage(configByContextHydrate.upload.image);
+  // useUploadableFile(configByContextHydrate.upload.file);
 
 
   // Page
   // Page
-  useCurrentCreatedAt(createdAt);
   useDeleteUsername(deleteUsername);
   useDeleteUsername(deleteUsername);
   useDeletedAt(deletedAt);
   useDeletedAt(deletedAt);
   useHasChildren(hasChildren);
   useHasChildren(hasChildren);
@@ -160,7 +149,6 @@ const ContextExtractorOnce: FC = () => {
   useIsUserPage(isUserPage);
   useIsUserPage(isUserPage);
   useLastUpdateUsername(lastUpdateUsername);
   useLastUpdateUsername(lastUpdateUsername);
   useCurrentPageId(pageId);
   useCurrentPageId(pageId);
-  useEmptyPageId(emptyPageId);
   usePageIdOnHackmd(pageIdOnHackmd);
   usePageIdOnHackmd(pageIdOnHackmd);
   usePageUser(pageUser);
   usePageUser(pageUser);
   useCurrentPagePath(path);
   useCurrentPagePath(path);
@@ -170,13 +158,8 @@ const ContextExtractorOnce: FC = () => {
   useShareLinkId(shareLinkId);
   useShareLinkId(shareLinkId);
   useShareLinksNumber(shareLinksNumber);
   useShareLinksNumber(shareLinksNumber);
   useTemplateTagData(templateTagData);
   useTemplateTagData(templateTagData);
-  useCurrentUpdatedAt(updatedAt);
-  useCreator(creator);
-  useRevisionAuthor(revisionAuthor);
   useTargetAndAncestors(targetAndAncestors);
   useTargetAndAncestors(targetAndAncestors);
-  useNotFoundTargetPathOrId(notFoundTargetPathOrId);
   useIsSearchPage(isSearchPage);
   useIsSearchPage(isSearchPage);
-  useIsEmptyPage(isEmptyPage);
   useHasParent(hasParent);
   useHasParent(hasParent);
 
 
   // Navigation
   // Navigation
@@ -190,9 +173,9 @@ const ContextExtractorOnce: FC = () => {
   useIsDeviceSmallerThanMd();
   useIsDeviceSmallerThanMd();
 
 
   // Editor
   // Editor
-  useSelectedGrant(grant);
-  useSelectedGrantGroupId(grantGroupId);
-  useSelectedGrantGroupName(grantGroupName);
+  // useSelectedGrant(grant);
+  // useSelectedGrantGroupId(grantGroupId);
+  // useSelectedGrantGroupName(grantGroupName);
 
 
   // SearchResult
   // SearchResult
   useIsDeviceSmallerThanLg();
   useIsDeviceSmallerThanLg();
@@ -204,21 +187,21 @@ const ContextExtractorOnce: FC = () => {
 
 
   // TODO: Remove this code when reveal.js is omitted. see: https://github.com/weseek/growi/pull/6223
   // TODO: Remove this code when reveal.js is omitted. see: https://github.com/weseek/growi/pull/6223
   // Do not access this property from other than reveal.js plugins.
   // Do not access this property from other than reveal.js plugins.
-  (window as CustomWindow).previewRenderer = generatePreviewRenderer(
-    {
-      isEnabledXssPrevention: configByContextHydrate.isEnabledXssPrevention,
-      attrWhiteList: configByContextHydrate.attrWhiteList,
-      tagWhiteList: configByContextHydrate.tagWhiteList,
-      highlightJsStyleBorder: configByContextHydrate.highlightJsStyleBorder,
-      env: {
-        MATHJAX: configByContextHydrate.env.MATHJAX,
-        PLANTUML_URI: configByContextHydrate.env.PLANTUML_URI,
-        BLOCKDIAG_URI: configByContextHydrate.env.BLOCKDIAG_URI,
-      },
-    },
-    null,
-    path,
-  );
+  // (window as CustomWindow).previewRenderer = generatePreviewRenderer(
+  //   {
+  //     isEnabledXssPrevention: configByContextHydrate.isEnabledXssPrevention,
+  //     attrWhiteList: configByContextHydrate.attrWhiteList,
+  //     tagWhiteList: configByContextHydrate.tagWhiteList,
+  //     highlightJsStyleBorder: configByContextHydrate.highlightJsStyleBorder,
+  //     env: {
+  //       MATHJAX: configByContextHydrate.env.MATHJAX,
+  //       PLANTUML_URI: configByContextHydrate.env.PLANTUML_URI,
+  //       BLOCKDIAG_URI: configByContextHydrate.env.BLOCKDIAG_URI,
+  //     },
+  //   },
+  //   null,
+  //   path,
+  // );
 
 
   return null;
   return null;
 };
 };

+ 0 - 24
packages/app/src/client/services/EditorContainer.js

@@ -21,8 +21,6 @@ export default class EditorContainer extends Container {
       tags: null,
       tags: null,
     };
     };
 
 
-    this.isSetBeforeunloadEventHandler = false;
-
     this.initDrafts();
     this.initDrafts();
 
 
   }
   }
@@ -59,28 +57,6 @@ export default class EditorContainer extends Container {
     }
     }
   }
   }
 
 
-
-  // See https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload#example
-  showUnsavedWarning(e) {
-    // Cancel the event
-    e.preventDefault();
-    // display browser default message
-    e.returnValue = '';
-    return '';
-  }
-
-  disableUnsavedWarning() {
-    window.removeEventListener('beforeunload', this.showUnsavedWarning);
-    this.isSetBeforeunloadEventHandler = false;
-  }
-
-  enableUnsavedWarning() {
-    if (!this.isSetBeforeunloadEventHandler) {
-      window.addEventListener('beforeunload', this.showUnsavedWarning);
-      this.isSetBeforeunloadEventHandler = true;
-    }
-  }
-
   clearDraft(path) {
   clearDraft(path) {
     delete this.drafts[path];
     delete this.drafts[path];
     window.localStorage.setItem('drafts', JSON.stringify(this.drafts));
     window.localStorage.setItem('drafts', JSON.stringify(this.drafts));

+ 1 - 3
packages/app/src/client/services/PageContainer.js

@@ -54,9 +54,6 @@ export default class PageContainer extends Container {
       path,
       path,
       isEmpty: mainContent.getAttribute('data-page-is-empty'),
       isEmpty: mainContent.getAttribute('data-page-is-empty'),
 
 
-      createdAt: mainContent.getAttribute('data-page-created-at'),
-      // please use useCurrentUpdatedAt instead
-      updatedAt: mainContent.getAttribute('data-page-updated-at'),
       deletedAt: mainContent.getAttribute('data-page-deleted-at') || null,
       deletedAt: mainContent.getAttribute('data-page-deleted-at') || null,
 
 
       isUserPage: JSON.parse(mainContent.getAttribute('data-page-user')) != null,
       isUserPage: JSON.parse(mainContent.getAttribute('data-page-user')) != null,
@@ -138,6 +135,7 @@ export default class PageContainer extends Container {
 
 
   /**
   /**
    * initialize state for markdown data
    * initialize state for markdown data
+   * [Already SWRized]
    */
    */
   initStateMarkdown() {
   initStateMarkdown() {
     let pageContent = '';
     let pageContent = '';

+ 0 - 50
packages/app/src/client/services/SocketIoContainer.js

@@ -1,50 +0,0 @@
-import { Container } from 'unstated';
-
-import io from 'socket.io-client';
-
-import loggerFactory from '~/utils/logger';
-
-const logger = loggerFactory('growi:cli:SocketIoContainer');
-
-/**
- * Service container related to options for WebSocket
- * @extends {Container} unstated Container
- */
-export default class SocketIoContainer extends Container {
-
-  constructor(appContainer, namespace) {
-    super();
-
-    this.appContainer = appContainer;
-    this.appContainer.registerContainer(this);
-
-    const ns = namespace || '/';
-
-    this.socket = io(ns, {
-      transports: ['websocket'],
-    });
-
-    this.socket.on('connect_error', (error) => {
-      logger.error(error);
-    });
-    this.socket.on('error', (error) => {
-      logger.error(error);
-    });
-
-    this.state = {
-    };
-
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'SocketIoContainer';
-  }
-
-  getSocket() {
-    return this.socket;
-  }
-
-}

+ 1 - 2
packages/app/src/client/services/page-operation.ts

@@ -1,7 +1,6 @@
+import { SubscriptionStatusType } from '@growi/core';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
-import { SubscriptionStatusType } from '~/interfaces/subscription';
-
 import { toastError } from '../util/apiNotification';
 import { toastError } from '../util/apiNotification';
 import { apiv3Post, apiv3Put } from '../util/apiv3-client';
 import { apiv3Post, apiv3Put } from '../util/apiv3-client';
 
 

+ 2 - 22
packages/app/src/client/util/apiv1-client.ts

@@ -4,15 +4,6 @@ import axios from '~/utils/axios';
 
 
 const apiv1Root = '/_api';
 const apiv1Root = '/_api';
 
 
-// get csrf token from body element
-const body = document.querySelector('body');
-const csrfToken = body?.dataset.csrftoken;
-
-
-type ParamWithCsrfKey = {
-  _csrf: string,
-}
-
 class Apiv1ErrorHandler extends Error {
 class Apiv1ErrorHandler extends Error {
 
 
   code;
   code;
@@ -50,25 +41,14 @@ export async function apiGet<T>(path: string, params: unknown = {}): Promise<T>
   return apiRequest<T>('get', path, { params });
   return apiRequest<T>('get', path, { params });
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiPost<T>(path: string, params: any & ParamWithCsrfKey = {}): Promise<T> {
-  if (params._csrf == null) {
-    params._csrf = csrfToken;
-  }
+export async function apiPost<T>(path: string, params: unknown = {}): Promise<T> {
   return apiRequest<T>('post', path, params);
   return apiRequest<T>('post', path, params);
 }
 }
 
 
 export async function apiPostForm<T>(path: string, formData: FormData): Promise<T> {
 export async function apiPostForm<T>(path: string, formData: FormData): Promise<T> {
-  if (formData.get('_csrf') == null && csrfToken != null) {
-    formData.append('_csrf', csrfToken);
-  }
   return apiPost<T>(path, formData);
   return apiPost<T>(path, formData);
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiDelete<T>(path: string, params: any & ParamWithCsrfKey = {}): Promise<T> {
-  if (params._csrf == null) {
-    params._csrf = csrfToken;
-  }
+export async function apiDelete<T>(path: string, params: unknown = {}): Promise<T> {
   return apiRequest<T>('delete', path, { data: params });
   return apiRequest<T>('delete', path, { data: params });
 }
 }

+ 4 - 29
packages/app/src/client/util/apiv3-client.ts

@@ -12,16 +12,8 @@ const apiv3Root = '/_api/v3';
 
 
 const logger = loggerFactory('growi:apiv3');
 const logger = loggerFactory('growi:apiv3');
 
 
-// get csrf token from body element
-const body = document.querySelector('body');
-const csrfToken = body?.dataset.csrftoken;
 
 
-
-type ParamWithCsrfKey = {
-  _csrf: string,
-}
-
-const apiv3ErrorHandler = (_err) => {
+const apiv3ErrorHandler = (_err: any): any[] => {
   // extract api errors from general 400 err
   // extract api errors from general 400 err
   const err = _err.response ? _err.response.data.errors : _err;
   const err = _err.response ? _err.response.data.errors : _err;
   const errs = toArrayIfNot(err);
   const errs = toArrayIfNot(err);
@@ -45,39 +37,22 @@ export async function apiv3Request<T = any>(method: string, path: string, params
   }
   }
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
 export async function apiv3Get<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
 export async function apiv3Get<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
   return apiv3Request('get', path, { params });
   return apiv3Request('get', path, { params });
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Post<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
-  if (params._csrf == null) {
-    params._csrf = csrfToken;
-  }
+export async function apiv3Post<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
   return apiv3Request('post', path, params);
   return apiv3Request('post', path, params);
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
 export async function apiv3PostForm<T = any>(path: string, formData: FormData): Promise<AxiosResponse<T>> {
 export async function apiv3PostForm<T = any>(path: string, formData: FormData): Promise<AxiosResponse<T>> {
-  if (formData.get('_csrf') == null && csrfToken != null) {
-    formData.append('_csrf', csrfToken);
-  }
   return apiv3Post<T>(path, formData);
   return apiv3Post<T>(path, formData);
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Put<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
-  if (params._csrf == null) {
-    params._csrf = csrfToken;
-  }
+export async function apiv3Put<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
   return apiv3Request('put', path, params);
   return apiv3Request('put', path, params);
 }
 }
 
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Delete<T = any>(path: string, params: any & ParamWithCsrfKey = {}): Promise<AxiosResponse<T>> {
-  if (params._csrf == null) {
-    params._csrf = csrfToken;
-  }
+export async function apiv3Delete<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
   return apiv3Request('delete', path, { params });
   return apiv3Request('delete', path, { params });
 }
 }

+ 0 - 73
packages/app/src/client/util/color-scheme.js

@@ -1,73 +0,0 @@
-const mediaQueryListForDarkMode = window.matchMedia('(prefers-color-scheme: dark)');
-
-function isUserPreferenceExists() {
-  return localStorage.preferDarkModeByUser != null;
-}
-
-function isPreferedDarkModeByUser() {
-  return localStorage.preferDarkModeByUser === 'true';
-}
-
-function isDarkMode() {
-  if (isUserPreferenceExists()) {
-    return isPreferedDarkModeByUser();
-  }
-  return mediaQueryListForDarkMode.matches;
-}
-
-/**
- * Apply color scheme as 'dark' attribute of <html></html>
- */
-function applyColorScheme() {
-  let isDarkMode = mediaQueryListForDarkMode.matches;
-  if (isUserPreferenceExists()) {
-    isDarkMode = isPreferedDarkModeByUser();
-  }
-
-  // switch to dark mode
-  if (isDarkMode) {
-    document.documentElement.removeAttribute('light');
-    document.documentElement.setAttribute('dark', 'true');
-  }
-  // switch to light mode
-  else {
-    document.documentElement.setAttribute('light', 'true');
-    document.documentElement.removeAttribute('dark');
-  }
-}
-
-/**
- * Remove color scheme preference
- */
-function removeUserPreference() {
-  if (isUserPreferenceExists()) {
-    delete localStorage.removeItem('preferDarkModeByUser');
-  }
-}
-
-/**
- * Set color scheme preference
- * @param {boolean} isDarkMode
- */
-function updateUserPreference(isDarkMode) {
-  // store settings to localStorage
-  localStorage.preferDarkModeByUser = isDarkMode;
-}
-
-/**
- * Set color scheme preference with OS settings
- */
-function updateUserPreferenceWithOsSettings() {
-  localStorage.preferDarkModeByUser = mediaQueryListForDarkMode.matches;
-}
-
-export {
-  mediaQueryListForDarkMode,
-  isUserPreferenceExists,
-  isPreferedDarkModeByUser,
-  isDarkMode,
-  applyColorScheme,
-  removeUserPreference,
-  updateUserPreference,
-  updateUserPreferenceWithOsSettings,
-};

+ 1 - 5
packages/app/src/client/util/locale-utils.js → packages/app/src/client/util/locale-utils.ts

@@ -4,10 +4,6 @@ const DIAGRAMS_NET_LANG_MAP = {
   zh_CN: 'zh',
   zh_CN: 'zh',
 };
 };
 
 
-const getDiagramsNetLangCode = (lang) => {
+export const getDiagramsNetLangCode = (lang) => {
   return DIAGRAMS_NET_LANG_MAP[lang];
   return DIAGRAMS_NET_LANG_MAP[lang];
 };
 };
-
-module.exports = {
-  getDiagramsNetLangCode,
-};

+ 11 - 9
packages/app/src/components/Admin/AdminHome/AdminHome.jsx

@@ -1,19 +1,22 @@
 import React, { useEffect, useCallback } from 'react';
 import React, { useEffect, useCallback } from 'react';
+
+import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { Tooltip } from 'reactstrap';
 import { Tooltip } from 'reactstrap';
-import loggerFactory from '~/utils/logger';
 
 
+import AdminHomeContainer from '~/client/services/AdminHomeContainer';
 import { toastError } from '~/client/util/apiNotification';
 import { toastError } from '~/client/util/apiNotification';
+import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
+import loggerFactory from '~/utils/logger';
+
 
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
-import AppContainer from '~/client/services/AppContainer';
-import AdminHomeContainer from '~/client/services/AdminHomeContainer';
-import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
-import SystemInfomationTable from './SystemInfomationTable';
-import InstalledPluginTable from './InstalledPluginTable';
+
+
 import EnvVarsTable from './EnvVarsTable';
 import EnvVarsTable from './EnvVarsTable';
+import InstalledPluginTable from './InstalledPluginTable';
+import SystemInfomationTable from './SystemInfomationTable';
 
 
 const logger = loggerFactory('growi:admin');
 const logger = loggerFactory('growi:admin');
 
 
@@ -129,10 +132,9 @@ const AdminHome = (props) => {
 };
 };
 
 
 
 
-const AdminHomeWrapper = withUnstatedContainers(AdminHome, [AppContainer, AdminHomeContainer]);
+const AdminHomeWrapper = withUnstatedContainers(AdminHome, [AdminHomeContainer]);
 
 
 AdminHome.propTypes = {
 AdminHome.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminHomeContainer: PropTypes.instanceOf(AdminHomeContainer).isRequired,
   adminHomeContainer: PropTypes.instanceOf(AdminHomeContainer).isRequired,
 };
 };
 
 

+ 1 - 1
packages/app/src/components/Admin/AdminHome/InstalledPluginTable.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 
 
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminHomeContainer from '~/client/services/AdminHomeContainer';
 import AdminHomeContainer from '~/client/services/AdminHomeContainer';
 
 

+ 24 - 18
packages/app/src/components/Admin/App/AppSetting.jsx

@@ -1,11 +1,12 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
+import { useTranslation, i18n } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
+
+import { i18n as i18nConfig } from '^/config/next-i18next.config';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import { localeMetadatas } from '~/client/util/i18n';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 
 
@@ -77,22 +78,27 @@ const AppSetting = (props) => {
         </label>
         </label>
         <div className="col-md-6 py-2">
         <div className="col-md-6 py-2">
           {
           {
-            localeMetadatas.map(meta => (
-              <div key={meta.id} className="custom-control custom-radio custom-control-inline">
-                <input
-                  type="radio"
-                  id={`radioLang${meta.id}`}
-                  className="custom-control-input"
-                  name="globalLang"
-                  value={meta.id}
-                  checked={adminAppContainer.state.globalLang === meta.id}
-                  onChange={(e) => {
-                    adminAppContainer.changeGlobalLang(e.target.value);
-                  }}
-                />
-                <label className="custom-control-label" htmlFor={`radioLang${meta.id}`}>{meta.displayName}</label>
-              </div>
-            ))
+            i18nConfig.locales.map((locale) => {
+              const fixedT = i18n.getFixedT(locale);
+              i18n.loadLanguages(i18nConfig.locales);
+
+              return (
+                <div key={locale} className="custom-control custom-radio custom-control-inline">
+                  <input
+                    type="radio"
+                    id={`radioLang${locale}`}
+                    className="custom-control-input"
+                    name="globalLang"
+                    value={locale}
+                    checked={adminAppContainer.state.globalLang === locale}
+                    onChange={(e) => {
+                      adminAppContainer.changeGlobalLang(e.target.value);
+                    }}
+                  />
+                  <label className="custom-control-label" htmlFor={`radioLang${locale}`}>{fixedT('meta.display_name')}</label>
+                </div>
+              );
+            })
           }
           }
         </div>
         </div>
       </div>
       </div>

+ 0 - 51
packages/app/src/components/Admin/App/AppSettingsPage.jsx

@@ -1,51 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-import { toastError } from '~/client/util/apiNotification';
-import { toArrayIfNot } from '~/utils/array-utils';
-import { withLoadingSppiner } from '../../SuspenseUtils';
-
-import AdminAppContainer from '~/client/services/AdminAppContainer';
-
-import AppSettingsPageContents from './AppSettingsPageContents';
-
-const logger = loggerFactory('growi:appSettings');
-
-let retrieveErrors = null;
-function AppSettingsPage(props) {
-  if (props.adminAppContainer.state.title === props.adminAppContainer.dummyTitle) {
-    throw (async() => {
-      try {
-        await props.adminAppContainer.retrieveAppSettingsData();
-      }
-      catch (err) {
-        const errs = toArrayIfNot(err);
-        toastError(errs);
-        logger.error(errs);
-        props.adminAppContainer.setState({
-          title: props.adminAppContainer.dummyTitleForError,
-        });
-        retrieveErrors = errs;
-      }
-    })();
-  }
-
-  if (props.adminAppContainer.state.title === props.adminAppContainer.dummyTitleForError) {
-    throw new Error(`${retrieveErrors.length} errors occured`);
-  }
-
-  return <AppSettingsPageContents />;
-}
-
-AppSettingsPage.propTypes = {
-  adminAppContainer: PropTypes.instanceOf(AdminAppContainer).isRequired,
-};
-
-/**
- * Wrapper component for using unstated
- */
-const AppSettingsPageWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(AppSettingsPage), [AdminAppContainer]);
-
-export default AppSettingsPageWithUnstatedContainer;

+ 23 - 2
packages/app/src/components/Admin/App/AppSettingsPageContents.tsx

@@ -1,8 +1,12 @@
-import React from 'react';
+import React, { useEffect } from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
+import { toastError } from '~/client/util/apiNotification';
+import { toArrayIfNot } from '~/utils/array-utils';
+import loggerFactory from '~/utils/logger';
+
 
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 
@@ -14,6 +18,8 @@ import PluginSetting from './PluginSetting';
 import SiteUrlSetting from './SiteUrlSetting';
 import SiteUrlSetting from './SiteUrlSetting';
 import V5PageMigration from './V5PageMigration';
 import V5PageMigration from './V5PageMigration';
 
 
+const logger = loggerFactory('growi:appSettings');
+
 type Props = {
 type Props = {
   adminAppContainer: AdminAppContainer,
   adminAppContainer: AdminAppContainer,
 }
 }
@@ -23,6 +29,21 @@ const AppSettingsPageContents = (props: Props) => {
   const { adminAppContainer } = props;
   const { adminAppContainer } = props;
   const { isV5Compatible } = adminAppContainer.state;
   const { isV5Compatible } = adminAppContainer.state;
 
 
+  useEffect(() => {
+    const fetchAppSettingsData = async() => {
+      await adminAppContainer.retrieveAppSettingsData();
+    };
+
+    try {
+      fetchAppSettingsData();
+    }
+    catch (err) {
+      const errs = toArrayIfNot(err);
+      toastError(errs);
+      logger.error(errs);
+    }
+  }, [adminAppContainer]);
+
   return (
   return (
     <div data-testid="admin-app-settings">
     <div data-testid="admin-app-settings">
       {
       {

+ 1 - 1
packages/app/src/components/Admin/App/AwsSetting.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 
 
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 
 

+ 1 - 1
packages/app/src/components/Admin/App/ConfirmModal.tsx

@@ -2,7 +2,7 @@ import React, { FC } from 'react';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 import { TFunctionResult } from 'i18next';
 import { TFunctionResult } from 'i18next';
 
 
 type ConfirmModalProps = {
 type ConfirmModalProps = {

+ 2 - 2
packages/app/src/components/Admin/App/FileUploadSetting.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
@@ -40,7 +40,7 @@ const FileUploadSetting = (props: Props) => {
         <br />
         <br />
         <br />
         <br />
         <span className="text-danger">
         <span className="text-danger">
-          <i className="ti-unlink"></i>
+          <i className="ti ti-unlink"></i>
           {t('admin:app_setting.change_setting')}
           {t('admin:app_setting.change_setting')}
         </span>
         </span>
       </p>
       </p>

+ 1 - 1
packages/app/src/components/Admin/App/GcsSettings.jsx

@@ -2,7 +2,7 @@
 import React from 'react';
 import React from 'react';
 
 
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 
 

+ 1 - 1
packages/app/src/components/Admin/App/MailSetting.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';

+ 1 - 1
packages/app/src/components/Admin/App/MaintenanceMode.tsx

@@ -1,5 +1,5 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 

+ 1 - 1
packages/app/src/components/Admin/App/PluginSetting.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';

+ 1 - 2
packages/app/src/components/Admin/App/SesSetting.tsx

@@ -3,7 +3,6 @@ import React from 'react';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 
 
-import { withLoadingSppiner } from '../../SuspenseUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 
 type Props = {
 type Props = {
@@ -57,6 +56,6 @@ const SmtpSetting = (props: Props) => {
 /**
 /**
  * Wrapper component for using unstated
  * Wrapper component for using unstated
  */
  */
-const SmtpSettingWrapper = withUnstatedContainers(withLoadingSppiner(SmtpSetting), [AdminAppContainer]);
+const SmtpSettingWrapper = withUnstatedContainers(SmtpSetting, [AdminAppContainer]);
 
 
 export default SmtpSettingWrapper;
 export default SmtpSettingWrapper;

+ 1 - 1
packages/app/src/components/Admin/App/SiteUrlSetting.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';

+ 2 - 3
packages/app/src/components/Admin/App/SmtpSetting.tsx

@@ -1,11 +1,10 @@
 
 
 import React from 'react';
 import React from 'react';
 
 
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 
 
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 
 
-import { withLoadingSppiner } from '../../SuspenseUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 
 
 
@@ -82,5 +81,5 @@ const SmtpSetting = (props: Props) => {
 /**
 /**
  * Wrapper component for using unstated
  * Wrapper component for using unstated
  */
  */
-const SmtpSettingWrapper = withUnstatedContainers(withLoadingSppiner(SmtpSetting), [AdminAppContainer]);
+const SmtpSettingWrapper = withUnstatedContainers(SmtpSetting, [AdminAppContainer]);
 export default SmtpSettingWrapper;
 export default SmtpSettingWrapper;

+ 1 - 1
packages/app/src/components/Admin/App/V5PageMigration.tsx

@@ -1,7 +1,7 @@
 import React, {
 import React, {
   FC, useCallback, useEffect, useState,
   FC, useCallback, useEffect, useState,
 } from 'react';
 } from 'react';
-import { useTranslation } from 'react-i18next';
+import { useTranslation } from 'next-i18next';
 import { ConfirmModal } from './ConfirmModal';
 import { ConfirmModal } from './ConfirmModal';
 import AdminAppContainer from '../../../client/services/AdminAppContainer';
 import AdminAppContainer from '../../../client/services/AdminAppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';

+ 2 - 0
packages/app/src/components/Admin/AuditLog/DateRangePicker.tsx

@@ -37,6 +37,8 @@ const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>((props: Custo
   );
   );
 });
 });
 
 
+CustomInput.displayName = 'CustomInput';
+
 type DateRangePickerProps = {
 type DateRangePickerProps = {
   startDate: Date | null
   startDate: Date | null
   endDate: Date | null
   endDate: Date | null

+ 12 - 11
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -5,22 +5,22 @@
 import React from 'react';
 import React from 'react';
 
 
 import { pathUtils } from '@growi/core';
 import { pathUtils } from '@growi/core';
+import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { useTranslation } from 'react-i18next';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 
 
 
 
-import AppContainer from '~/client/services/AppContainer';
+// import AppContainer from '~/client/services/AppContainer';
 
 
-import { withUnstatedContainers } from '../../UnstatedUtils';
+// import { withUnstatedContainers } from '../../UnstatedUtils';
 
 
 const AdminNavigation = (props) => {
 const AdminNavigation = (props) => {
   const { t } = useTranslation();
   const { t } = useTranslation();
-  const { appContainer } = props;
+  // const { appContainer } = props;
   const pathname = window.location.pathname;
   const pathname = window.location.pathname;
 
 
-  const growiCloudUri = appContainer.config.env.GROWI_CLOUD_URI;
-  const growiAppIdForGrowiCloud = appContainer.config.env.GROWI_APP_ID_FOR_GROWI_CLOUD;
+  // const growiCloudUri = appContainer.config.env.GROWI_CLOUD_URI;
+  // const growiAppIdForGrowiCloud = appContainer.config.env.GROWI_APP_ID_FOR_GROWI_CLOUD;
 
 
   // eslint-disable-next-line react/prop-types
   // eslint-disable-next-line react/prop-types
   const MenuLabel = ({ menu }) => {
   const MenuLabel = ({ menu }) => {
@@ -89,7 +89,7 @@ const AdminNavigation = (props) => {
         <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
         <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
         <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
         <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
         <MenuLink menu="audit-log"    isListGroupItems isActive={isActiveMenu('/audit-log')} />
         <MenuLink menu="audit-log"    isListGroupItems isActive={isActiveMenu('/audit-log')} />
-        {growiCloudUri != null && growiAppIdForGrowiCloud != null
+        {/* {growiCloudUri != null && growiAppIdForGrowiCloud != null
           && (
           && (
             <a
             <a
               href={`${growiCloudUri}/my/apps/${growiAppIdForGrowiCloud}`}
               href={`${growiCloudUri}/my/apps/${growiAppIdForGrowiCloud}`}
@@ -98,7 +98,7 @@ const AdminNavigation = (props) => {
               <MenuLabel menu="cloud" />
               <MenuLabel menu="cloud" />
             </a>
             </a>
           )
           )
-        }
+        } */}
       </>
       </>
     );
     );
   };
   };
@@ -146,10 +146,11 @@ const AdminNavigation = (props) => {
   );
   );
 };
 };
 
 
-const AdminNavigationWrapper = withUnstatedContainers(AdminNavigation, [AppContainer]);
+// const AdminNavigationWrapper = withUnstatedContainers(AdminNavigation, [AppContainer]);
 
 
 AdminNavigation.propTypes = {
 AdminNavigation.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 };
 };
 
 
-export default AdminNavigationWrapper;
+// export default AdminNavigationWrapper;
+export default AdminNavigation;

Some files were not shown because too many files changed in this diff