Просмотр исходного кода

Merge branch 'support/apply-nextjs-2' into feat/modularize-page-list-scss

yohei0125 3 лет назад
Родитель
Сommit
dd99690a8b
28 измененных файлов с 612 добавлено и 597 удалено
  1. 3 1
      packages/app/public/static/locales/en_US/admin.json
  2. 0 2
      packages/app/public/static/locales/en_US/translation.json
  3. 303 0
      packages/app/public/static/locales/ja_JP/admin.json
  4. 0 298
      packages/app/public/static/locales/ja_JP/translation.json
  5. 5 4
      packages/app/src/components/Admin/Common/AdminNavigation.jsx
  6. 1 1
      packages/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx
  7. 1 1
      packages/app/src/components/Admin/ElasticsearchManagement/ReconnectControls.tsx
  8. 1 1
      packages/app/src/components/Admin/ElasticsearchManagement/StatusTable.jsx
  9. 1 1
      packages/app/src/components/Admin/ExportArchiveDataPage.jsx
  10. 7 7
      packages/app/src/components/Admin/Security/SecuritySetting.jsx
  11. 2 0
      packages/app/src/components/Layout/BasicLayout.tsx
  12. 24 24
      packages/app/src/components/PageAccessoriesModal.tsx
  13. 37 0
      packages/app/src/components/PageEditor/CodeMirrorEditor.module.scss
  14. 66 20
      packages/app/src/components/PageEditor/Editor.module.scss
  15. 4 2
      packages/app/src/components/PageEditor/Editor.tsx
  16. 4 2
      packages/app/src/components/PageEditor/GridEditModal.jsx
  17. 43 0
      packages/app/src/components/PageEditor/GridEditModal.module.scss
  18. 1 1
      packages/app/src/components/PageEditor/Preview.tsx
  19. 75 72
      packages/app/src/pages/[[...path]].page.tsx
  20. 4 4
      packages/app/src/pages/admin/[[...path]].page.tsx
  21. 2 2
      packages/app/src/server/views/admin/customize.html
  22. 2 2
      packages/app/src/server/views/admin/export.html
  23. 2 2
      packages/app/src/server/views/admin/importer.html
  24. 2 2
      packages/app/src/server/views/admin/search.html
  25. 0 28
      packages/app/src/styles/_editor-navbar.scss
  26. 0 72
      packages/app/src/styles/_editor-overlay.scss
  27. 20 0
      packages/app/src/styles/_mixins.scss
  28. 2 48
      packages/app/src/styles/_on-edit.scss

+ 3 - 1
packages/app/public/static/locales/en_US/admin.json

@@ -269,7 +269,9 @@
     "delete_notification_pattern_desc2": "Once deleted, it cannot be recovered",
     "toggle_notification": "Updated setting of {{path}}"
   },
-  "Customize": "Customize",
+  "customize": "Customize",
+  "ipmport_data": "Import Data",
+  "export_archive_data": "Export Archive Data",
   "full_text_search_management": "Full Text Search Management",
   "mailer_setup_required":"<a href='/admin/app'>Email settings</a> are required to send.",
   "admin_top": {

+ 0 - 2
packages/app/public/static/locales/en_US/translation.json

@@ -121,8 +121,6 @@
   "external_account_management": "External Account Management",
   "UserGroup": "UserGroup",
   "ChildUserGroup": "ChildUserGroup",
-  "Import Data": "Import Data",
-  "Export Archive Data": "Export Archive Data",
   "Basic Settings": "Basic Settings",
   "Basic authentication": "Basic authentication",
   "Register limitation": "Register limitation",

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

@@ -1,4 +1,300 @@
 {
+  "wiki_management_home_page": "Wiki管理トップ",
+  "app_settings": "アプリ設定",
+  "public": "公開",
+  "anyone_with_the_link": "リンクを知っている人のみ",
+  "specified_users": "特定ユーザーのみ",
+  "only_me": "自分のみ",
+  "only_inside_the_group": "特定グループのみ",
+  "security_settings": {
+    "security_settings": "セキュリティ設定",
+    "scope_of_page_disclosure": "ページの公開範囲",
+    "set_point": "設定値",
+    "Guest Users Access":"ゲストユーザーのアクセス",
+    "always_hidden": "非表示 (固定)",
+    "always_displayed": "表示 (固定)",
+    "displayed_or_hidden": "表示 / 非表示",
+    "Fixed by env var": "環境変数 <code>{{forcewikimode}}={{wikimode}}</code> により固定されています。",
+    "Register limitation": "登録の制限",
+    "Register limitation desc": "新しいユーザーを登録する方法を制限します.",
+    "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
+    "users_without_account": "アカウントを持たないユーザーはアクセス不可",
+    "example": "例",
+    "restrict_emails": "登録可能なメールアドレスを制限することができます。",
+    "for_example": "例えば、",
+    "in_this_case": "と記載することで、そのドメインのメールアドレスを持っている人のみ登録可能になります。",
+    "insert_single": "1行に1メールアドレス入力してください。",
+    "page_list_and_search_results": "ページリスト・検索結果",
+    "page_listing_1": "ページのリスト表示と検索<br>'自分のみ'に閲覧制限しているページ",
+    "page_listing_1_desc": "ページのリスト表示や検索結果において、'自分のみ'に閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+    "page_listing_2": "ページのリスト表示と検索<br>特定グループに閲覧制限しているページ",
+    "page_listing_2_desc": "ページのリスト表示や検索結果において、特定グループにのみ閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+    "page_access_rights": "ページの閲覧権限",
+    "page_delete_rights": "ページの削除権限",
+    "page_delete": "ゴミ箱に入れる",
+    "page_delete_completely": "完全に削除する",
+    "other_options": "その他のオプション",
+    "deletion_explain": "ページをゴミ箱に入れることができるユーザーを制限します。",
+    "complete_deletion_explain": "ページを完全削除することができるユーザーを制限します。",
+    "recursive_deletion_explain": "子孫を含めたページをゴミ箱に入れることができるユーザーを制限します。",
+    "recursive_complete_deletion_explain": "子孫を含めたページを完全削除することができるユーザーを制限します。",
+    "inherit": "単体のみと同じ",
+    "admin_only": "管理者のみ可能",
+    "admin_and_author": "管理者とページ作者が可能",
+    "anyone": "誰でも可能",
+    "session": "セッション",
+    "max_age": "有効期間 (ミリ秒)",
+    "max_age_desc": "ユーザーのセッション情報の有効期間をミリ秒で指定できます。<br>デフォルト値: 2592000000 (30日間)",
+    "max_age_caution": "この値を変更した後は、サーバーを再起動する必要があります。",
+    "forced_update_desc": "設定が強制変更されました。前回の設定: ",
+    "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。 <br><br> 管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能",
+    "Authentication mechanism settings": "認証機構設定",
+    "setup_is_not_yet_complete":"セットアップはまだ完了してません",
+    "alert_siteUrl_is_not_set": "'サイトURL' が設定されていません。{{link}} から設定してください。",
+    "xss_prevent_setting": "XSS(Cross Site Scripting)対策設定",
+    "xss_prevent_setting_link": "マークダウン設定ページに移動",
+    "callback_URL": "コールバックURL",
+    "desc_of_callback_URL": "{{AuthName}} プロバイダ側の設定で利用してください。",
+    "authorization_endpoint": "認可エンドポイント",
+    "token_endpoint": "トークンエンドポイント",
+    "revocation_endpoint": "失効エンドポイント",
+    "introspection_endpoint": "検証エンドポイント",
+    "userinfo_endpoint": "ユーザ情報エンドポイント",
+    "end_session_endpoint": "セッション終了エンドポイント",
+    "registration_endpoint": "登録エンドポイント",
+    "jwks_uri": "JSON Web Key Set URL",
+    "clientID": "クライアントID",
+    "client_secret": "クライアントシークレット",
+    "updated_general_security_setting": "セキュリティ設定を更新しました。",
+    "setup_not_completed_yet": "まだセットアップは完了していません。",
+    "guest_mode": {
+      "deny": "拒否 (アカウントを持つユーザーのみ利用可能)",
+      "readonly": "許可 (ゲストユーザーも閲覧のみ可能)"
+    },
+    "registration_mode": {
+      "open": "公開 (だれでも登録可能)",
+      "restricted": "制限 (登録完了には管理者の承認が必要)",
+      "closed": "非公開 (登録には管理者による招待が必要)"
+    },
+    "share_link_rights": "シェアリンクの権限",
+    "enable_link_sharing": "リンクのシェアを許可",
+    "all_share_links": "全てのシェアリンク",
+    "configuration": "設定",
+    "optional": "オプション",
+    "Treat username matching as identical": "新規ログイン時、<code>username</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
+    "Treat username matching as identical_warn": "警告: <code>username</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください",
+    "Treat email matching as identical": "新規ログイン時、<code>email</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
+    "Treat email matching as identical_warn": "警告: <code>email</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください",
+    "Use env var if empty": "空の場合、環境変数 <code>{{env}}</code> を利用します",
+    "Use default if both are empty": "どちらの値も空の場合、デフォルト値 <code>{{target}}</code> を利用します",
+    "missing mandatory configs": "以下の必須項目の値がデータベースと環境変数のどちらにも設定されていません",
+    "Local": {
+      "name": "ID/Password",
+      "note for the only env option": "現在LOCAL認証のON/OFFは環境変数の値によって制限されています<br>この設定を変更する場合は環境変数 <code>{{env}}</code> の値をfalseに変更もしくは削除してください",
+      "enable_local": "ID/Password を有効にする",
+      "password_reset_by_users": "ユーザーによるパスワード再設定",
+      "enable_password_reset_by_users": "ユーザーによるパスワード再設定を有効にする",
+      "password_reset_desc": "ログイン時のパスワードを忘れた際に、ユーザー自身がパスワードを再設定できます。",
+      "email_authentication": "ユーザー登録時のメール認証",
+      "enable_email_authentication": "メール認証を有効にする",
+      "enable_email_authentication_desc": "ユーザー登録時にメール認証を行います。",
+      "please_enable_mailer": "メール認証を有効にするには、メール設定を完了させてください。",
+      "need_complete_mail_setting_warning": "以下の機能を使えるようにするには、メール設定を完了させてください。"
+    },
+    "ldap": {
+      "enable_ldap": "LDAP を有効にする",
+      "server_url_detail": "LDAP URLを <code>ldap://host:port/DN</code> または <code>ldaps://host:port/DN</code> の形式で入力してください。",
+      "bind_mode": "Bind モード",
+      "bind_manager": "管理者 Bind",
+      "bind_user": "ユーザー Bind",
+      "bind_DN_manager_detail": "ディレクトリーサービスに認証する際のアカウント DN",
+      "bind_DN_user_detail1": "ディレクトリーサービスに Bind するアカウント DN を決定するためのクエリ",
+      "bind_DN_user_detail2": "ログイン時に入力されるユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
+      "bind_DN_password": "Bind DN パスワード",
+      "bind_DN_password_manager_detail": "Bind DN アカウントのパスワード",
+      "bind_DN_password_user_detail": "ログイン時のパスワードが使用されます。",
+      "search_filter": "検索フィルター",
+      "search_filter_detail1": "認証されるユーザーを一意に決定するための LDAP フィルタ",
+      "search_filter_detail2": "ログイン時のユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
+      "search_filter_detail3": "空欄の場合 <code>(uid=&#123;&#123;username&#125;&#125;)</code> が使用されます。",
+      "search_filter_example1": "'uid' または 'mail' に一致",
+      "search_filter_example2": "'sAMAccountName' に一致 (Active Directory)",
+      "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
+      "name_detail": "新規ユーザーの表示名に関連付ける属性",
+      "mail_detail": "新規ユーザーのメールアドレスに関連付ける属性",
+      "group_search_base_DN": "グループ検索ベース DN",
+      "group_search_base_DN_detail": "グループ検索を実行するベース DN。利用する場合は <code>グループ検索フィルター</code> も入力する必要があります。",
+      "group_search_filter": "グループ検索フィルター",
+      "group_search_filter_detail1": "グループフィルターに用いるクエリ",
+      "group_search_filter_detail2": "このクエリにヒットするグループがあったときのみ、LDAPでのログインが成功します。",
+      "group_search_filter_detail3": "ログイン対象ユーザーオブジェクトのプロパティーで置換する場合は <code>&#123;&#123;dn&#125;&#125;</code> を用いてください。",
+      "group_search_filter_detail4": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> は <code>cn=group1</code> と、ユーザーの <code>uid</code> を含む <code>memberUid</code> を持つグループにヒットします(<code>ユーザーの DN プロパティー</code> がデフォルトから変更されていない場合)",
+      "group_search_user_DN_property": "ユーザーの DN プロパティー",
+      "group_search_user_DN_property_detail": "<code>グループ検索フィルター</code> 内の <code>&#123;&#123;dn&#125;&#125;</code> で置換される、ユーザーオブジェクトのプロパティー",
+      "test_config": "ログインテスト",
+      "updated_ldap": "LDAP設定 を更新しました"
+    },
+    "SAML": {
+      "name": "SAML",
+      "enable_saml": "SAML を有効にする",
+      "id_detail": "SAML Identity プロバイダ内で一意に識別可能な値を格納している属性",
+      "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
+      "mapping_detail": "新規ユーザーの{{target}}に関連付ける属性",
+      "cert_detail": "IdP からのレスポンスの validation を行うためのPEMエンコードされた X.509 証明書",
+      "Use env var if empty": "データベース側の値が空の場合、環境変数 <code>{{env}}</code> の値を利用します",
+      "note for the only env option": "現在SAML認証のON/OFFの設定値及びハイライトされている設定値は環境変数の値のみを使用するようになっています<br>この設定を変更する場合は環境変数 <code>{{env}}</code> の値をfalseに変更もしくは削除してください",
+      "attr_based_login_control_detail": "SAMLの <code>&lt;saml:AttributeStatement&gt;</code> 要素に含まれる <code>&lt;saml:Attribute&gt;</code> 要素と、その子要素 <code>&lt;saml:AttributeValue&gt;</code> を利用してログインの可否を制御します。",
+      "attr_based_login_control_rule_help": "<h5>利用可能なクエリ:</h5><ul><li>Terms</li><li>Fields</li><li>AND/NOT/OR Operator</li><li>Grouping</li></ul><h5>利用不可なクエリ:</h5><ul><li>Wildcard, Fuzzy, Proximity, Range and Boosting</li><li>+/- Operator</li><li>Field Grouping</li></ul><h5>特殊文字のエスケープ</h5>次の特殊文字はエスケープする必要があります。<code>+ - && || ! ( ) { } [ ] ^ &quot; &tilde; * ? : &#92;</code> and <code>/</code>",
+      "attr_based_login_control_rule_example1": "<h5>条件式の例</h5>ルールに <code>(Department: A || Department: B) && Position: Leader</code> を指定した場合, <code>Department: A</code> または <code>Department: B</code> のどちらかに該当し、かつ <code>Position: Leader</code> を持つユーザーにログインを<strong>許可</strong>します。",
+      "attr_based_login_control_rule_exampl2": "<h5>エスケープの例</h5>ルールに URL を利用したい場合は、次のようにエスケープしてください:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
+      "updated_saml": "Succeeded to update SAML setting"
+    },
+    "Basic": {
+      "enable_basic": "Basic を有効にする",
+      "name": "Basic 認証",
+      "desc_1": "Authorization ヘッダに格納されている <code>username</code> でログインします。",
+      "desc_2": "ユーザーが存在しなかった場合は自動生成します。",
+      "updated_basic": "Basic認証 を更新しました"
+    },
+    "OAuth": {
+      "enable_oidc": "OIDC を有効にする",
+      "register": "%sに登録",
+      "change_redirect_url": "承認済みのリダイレクトURLに、 <code>%s</code> を入力",
+      "Google": {
+        "enable_google": "Google OAuth を有効にする",
+        "name": "Google OAuth",
+        "register_1": "{{link}}へアクセス",
+        "register_2": "プロジェクトがない場合はプロジェクトを作成",
+        "register_3": "認証情報を作成 &rightarrow; OAuthクライアントID &rightarrow; ウェブアプリケーションを選択",
+        "register_4": "承認済みのリダイレクトURIを<code>{{url}}</code>としてGrowiを登録",
+        "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力",
+        "updated_google": "Google OAuth を更新しました"
+      },
+      "Facebook": {
+        "name": "Facebook OAuth"
+      },
+      "Twitter": {
+        "enable_twitter": "Twitter OAuth を有効にする",
+        "name": "Twitter OAuth",
+        "register_1": "{{link}} へアクセス",
+        "register_2": "Twitterにサインイン",
+        "register_3": "Create New Appをクリック &rightarrow; Application Detailsの各項目を入力",
+        "register_4": "Create your Twitter Applicationで作成",
+        "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力",
+        "updated_twitter": "Twitter OAuth を更新しました"
+      },
+      "GitHub": {
+        "enable_github": "GitHub OAuth を有効にする",
+        "name": "GitHub OAuth",
+        "register_1": "{{link}} へアクセス",
+        "register_2": "\"Authorization callback URL\"を<code>{{url}}</code>としてGrowiを登録",
+        "register_3": "上記フォームにクライアントIDとクライアントシークレットを入力",
+        "updated_github": "GitHub OAuth を更新しました"
+      },
+      "OIDC": {
+        "name": "OpenID Connect",
+        "id_detail": "OIDC claims で一意に識別可能な値を格納している属性",
+        "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
+        "name_detail": "新規ユーザー名(<code>name</code>)に関連付ける属性",
+        "mapping_detail": "新規ユーザーの{{target}}に関連付ける属性",
+        "updated_oidc": "OpenID Connect を更新しました",
+        "Use discovered URL if empty": "データベース側の値が空の場合、\"Issuer Host\"から検出した値を利用します。"
+      },
+      "how_to": {
+        "google": "Google OAuth の設定方法",
+        "github": "GitHub OAuth の設定方法",
+        "twitter": "Twitter OAuth の設定方法"
+      }
+    },
+    "form_item_name": {
+      "entryPoint": "エントリーポイント",
+      "issuer": "発行者",
+      "cert": "証明書",
+      "attrMapId": "ID",
+      "attrMapUsername": "ユーザー名",
+      "attrMapMail": "メールアドレス",
+      "attrMapFirstName": "姓",
+      "attrMapLastName": "名",
+      "ABLCRule": "ルール"
+    }
+  },
+  "markdown_settings": "マークダウン設定",
+  "notification_settings": {
+    "notification_settings": "通知設定",
+    "slack_incoming_configuration": "Slack Incoming Webhooks 設定",
+    "prioritize_webhook": "Slack アプリより Incoming Webhook を優先する",
+    "prioritize_webhook_desc": "このオプションをオンにすると、 Slack App が有効になっていても GROWI は Incoming Webhook を使用します。",
+    "slack_app_configuration": "Slack App 設定",
+    "slack_app_configuration_desc": "Crowi 互換の機能です。<br /> <strong>設定が複雑すぎる</strong>のでオススメしません。",
+    "use_instead": "代わりに Slack Incoming Webhooks 設定を使用してください。",
+    "how_to": {
+      "header": "Incoming Webhooks の設定方法",
+      "workspace": "ワークスペースで Webhook を追加します。",
+      "workspace_desc1": "<a href='https://slack.com/services/new/incoming-webhook'>Incoming Webhooks Configuration page</a> にアクセスします。",
+      "workspace_desc2": "投稿するチャンネルを選びます。",
+      "workspace_desc3": "追加します。",
+      "at_growi": "GROWI 管理画面で Webhook URL を設定します。",
+      "at_growi_desc": "このページで &rdquo;Webhook URL&rdquo; を入力して送信します。"
+    },
+    "user_trigger_notification_header": "デフォルトパターンの通知設定",
+    "pattern": "パターン",
+    "channel": "チャンネル名",
+    "pattern_desc": "Wiki のパス名。 パスには <code>*</code> を使用できます。",
+    "channel_desc": "<code>#</code> を除いた Slack チャンネル名",
+    "valid_page": "通知の有効 / 無効",
+    "link_notification_help": "<strong>linkを知っている人のみ閲覧できるページ</strong>は常に通知されません。",
+    "just_me_notification_help": "<strong>'自分のみ'に閲覧制限をしているページ</strong>に変更を加えた際に通知する",
+    "group_notification_help": "<strong>'特定グループにのみ'に閲覧制限をしているページ</strong>に変更を加えた際に通知する",
+    "notification_list": "通知設定の一覧",
+    "add_notification": "通知設定の追加",
+    "trigger_path": "トリガーパス",
+    "trigger_path_help": "(<code>*</code>が使用できます)",
+    "trigger_events": "トリガーイベント",
+    "notify_to": "通知先",
+    "back_to_list": "通知設定一覧に戻る",
+    "notification_detail": "通知詳細設定",
+    "event_pageCreate": "ページが新規作成されたとき",
+    "event_pageEdit": "ページが編集されたとき",
+    "event_pageDelete": "ページが削除されたとき",
+    "event_pageMove": "ページが移動(名前が変更)されたとき",
+    "event_pageLike": "ページに「いいね」がついたとき",
+    "event_comment": "コメントが投稿されたとき",
+    "email": {
+      "ifttt_link": "IFTTT でメールトリガの新しいアプレットを作る"
+    },
+    "updated_slackApp": "SlackApp設定を更新しました",
+    "add_notification_pattern": "通知パターンを追加しました。",
+    "delete_notification_pattern": "通知パターンを削除しました。",
+    "delete_notification_pattern_desc1": "Path: {{path}} を削除します。",
+    "delete_notification_pattern_desc2": "Once deleted, it cannot be recovered",
+    "toggle_notification": "{{path}}の通知設定を変更しました"
+  },
+  "customize": "カスタマイズ",
+  "ipmport_data": "データインポート",
+  "export_archive_data": "データアーカイブ",
+  "full_text_search_management": {
+    "full_text_search_management": "全文検索管理",
+    "elasticsearch_management": "Elasticsearch 管理",
+    "connection_status": "接続の状態",
+    "connection_status_label_unconfigured": "設定されていません",
+    "connection_status_label_connected": "接続されています",
+    "connection_status_label_disconnected": "切断されています",
+    "connection_status_label_erroroccured": "SearchService でエラーが発生しています",
+    "indices_status": "インデックスの状態",
+    "indices_status_label_normalized": "正規化されています",
+    "indices_status_label_unnormalized": "リビルド中 または 破損しています",
+    "indices_summary": "インデックスのサマリ",
+    "reconnect": "再接続",
+    "reconnect_button": "再接続の試行",
+    "reconnect_description": "Elasticsearch への再接続を試みます。",
+    "normalize": "正規化",
+    "normalize_button": "インデックスの正規化",
+    "normalize_description": "破損したインデックスを修復します。",
+    "rebuild": "リビルド",
+    "rebuild_button": "インデックスのリビルド",
+    "rebuild_description_1": "全てのページのインデックスを削除し、作り直します。",
+    "rebuild_description_2": "この作業には数秒かかります。"
+  },
   "mailer_setup_required": "送信するには <a href='/admin/app'>メールの設定</a> が必要です。",
   "admin_top": {
     "management_wiki": "Wiki管理",
@@ -305,12 +601,14 @@
     "Directory_hierarchy_tag": "ディレクトリ階層タグ"
   },
   "external_notification": {
+    "external_notification": "外部ツールへの通知",
     "enabled": "有効",
     "disabled": "無効",
     "header_status": "Slack 連携の状態",
     "caution_enabled": "CAUTION: このページで設定される通知は、Primary として設定された Slack ワークスペースにのみ送信されます。 "
   },
   "slack_integration": {
+    "slack_integration": "Slack連携",
     "selecting_bot_types": {
       "slack_bot": "Slack bot",
       "detailed_explanation": "詳しい説明はこちら",
@@ -421,10 +719,12 @@
     }
   },
   "slack_integration_legacy": {
+    "slack_integration_legacy":  "Slack連携 (レガシー)",
     "alert_disabled": "<a href='/admin/slack-integration'>新しい設定</a>が有効になっているため、この 'Slack連携 (レガシー)' は現在無効になっています。",
     "alert_deplicated": "この 'Slack連携 (レガシー)' は将来廃止されます。代わりに<a href='/admin/slack-integration'>新しいSlack連携機能</a>を利用してください。"
   },
   "user_management": {
+    "user_management": "ユーザー管理",
     "invite_users": "新規ユーザーの仮発行",
     "click_twice_same_checkbox": "少なくとも一つはチェックしてください。",
     "invite_modal": {
@@ -482,6 +782,7 @@
     "current_users": "現在のユーザー数:"
   },
   "user_group_management": {
+    "user_group_management": "グループ管理",
     "create_group": "新規グループの作成",
     "add_child_group": "子グループの追加",
     "remove_child_group": "解除",
@@ -528,6 +829,8 @@
     }
   },
   "audit_log_management": {
+    "audit_log": "監査ログ",
+    "audit_log_settings": "監査ログ設定",
     "user": "ユーザー",
     "username": "ユーザー名",
     "date": "日付",

+ 0 - 298
packages/app/public/static/locales/ja_JP/translation.json

@@ -114,28 +114,13 @@
   "Input page name (optional)": "ページ名を入力(空欄OK)",
   "New Page": "新規ページ",
   "Create under": "ページを以下に作成",
-  "Wiki Management Home Page": "Wiki管理トップ",
-  "App Settings": "アプリ設定",
   "V5 Page Migration": "V5 互換形式 への変換",
   "GROWI.5.0_new_schema": "GROWI.5.0における新スキーマについて",
   "See_more_detail_on_new_schema": "詳しくは<a href='#'>{{url}}</a><i class='icon-share-alt'></i>を参照ください。",
   "Site URL settings": "サイトURL設定",
-  "Markdown Settings": "マークダウン設定",
-  "Customize": "カスタマイズ",
-  "Notification Settings": "通知設定",
-  "slack_integration": "Slack連携",
-  "External_Notification": "外部ツールへの通知",
-  "Legacy_Slack_Integration": "Slack連携 (レガシー)",
-  "User_Management": "ユーザー管理",
   "external_account_management": "外部アカウント管理",
   "UserGroup": "グループ",
   "ChildUserGroup": "子グループ",
-  "UserGroup Management": "グループ管理",
-  "AuditLog": "監査ログ",
-  "AuditLog Settings": "監査ログ設定",
-  "Full Text Search Management": "全文検索管理",
-  "Import Data": "データインポート",
-  "Export Archive Data": "データアーカイブ",
   "Basic Settings": "基本設定",
   "Register limitation": "登録の制限",
   "The contents entered here will be shown in the header etc": "ここに入力した内容は、ヘッダー等に表示されます。",
@@ -145,11 +130,6 @@
   "Only me": "自分のみ",
   "Only inside the group": "特定グループのみ",
   "page_list": "ページリスト",
-  "scope_of_page_disclosure": "ページの公開範囲",
-  "set_point": "設定値",
-  "always_displayed": "表示 (固定)",
-  "always_hidden": "非表示 (固定)",
-  "displayed_or_hidden": "表示 / 非表示",
   "Reselect the group": "グループの再選択",
   "Shareable link": "このページの共有用URL",
   "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
@@ -258,7 +238,6 @@
     "new_password_confirm": "(確認用)",
     "password_is_not_set": "パスワードが設定されていません"
   },
-  "security_settings": "セキュリティ設定",
   "share_links": {
     "Shere this page link to public": "外部に共有するリンクを発行する",
     "share_link_list": "共有リンクリスト",
@@ -690,283 +669,6 @@
       "error_duplicate_pages_found": "同名のパスを持つページが複数見つかりました。リネームまたは削除してから再度実行してください"
     }
   },
-  "security_setting": {
-    "Guest Users Access": "ゲストユーザーのアクセス",
-    "Fixed by env var": "環境変数 <code>{{forcewikimode}}={{wikimode}}</code> により固定されています。",
-    "Register limitation": "登録の制限",
-    "Register limitation desc": "新しいユーザーを登録する方法を制限します.",
-    "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
-    "users_without_account": "アカウントを持たないユーザーはアクセス不可",
-    "example": "例",
-    "restrict_emails": "登録可能なメールアドレスを制限することができます。",
-    "for_example": "例えば、",
-    "in_this_case": "と記載することで、そのドメインのメールアドレスを持っている人のみ登録可能になります。",
-    "insert_single": "1行に1メールアドレス入力してください。",
-    "page_list_and_search_results": "ページリスト・検索結果",
-    "page_listing_1": "ページのリスト表示と検索<br>'自分のみ'に閲覧制限しているページ",
-    "page_listing_1_desc": "ページのリスト表示や検索結果において、'自分のみ'に閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
-    "page_listing_2": "ページのリスト表示と検索<br>特定グループに閲覧制限しているページ",
-    "page_listing_2_desc": "ページのリスト表示や検索結果において、特定グループにのみ閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
-    "page_access_rights": "ページの閲覧権限",
-    "page_delete_rights": "ページの削除権限",
-    "page_delete": "ゴミ箱に入れる",
-    "page_delete_completely": "完全に削除する",
-    "other_options": "その他のオプション",
-    "deletion_explain": "ページをゴミ箱に入れることができるユーザーを制限します。",
-    "complete_deletion_explain": "ページを完全削除することができるユーザーを制限します。",
-    "recursive_deletion_explain": "子孫を含めたページをゴミ箱に入れることができるユーザーを制限します。",
-    "recursive_complete_deletion_explain": "子孫を含めたページを完全削除することができるユーザーを制限します。",
-    "inherit": "単体のみと同じ",
-    "admin_only": "管理者のみ可能",
-    "admin_and_author": "管理者とページ作者が可能",
-    "anyone": "誰でも可能",
-    "session": "セッション",
-    "max_age": "有効期間 (ミリ秒)",
-    "max_age_desc": "ユーザーのセッション情報の有効期間をミリ秒で指定できます。<br>デフォルト値: 2592000000 (30日間)",
-    "max_age_caution": "この値を変更した後は、サーバーを再起動する必要があります。",
-    "forced_update_desc": "設定が強制変更されました。前回の設定: ",
-    "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。 <br><br> 管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能",
-    "Authentication mechanism settings": "認証機構設定",
-    "setup_is_not_yet_complete":"セットアップはまだ完了してません",
-    "alert_siteUrl_is_not_set": "'サイトURL' が設定されていません。{{link}} から設定してください。",
-    "xss_prevent_setting": "XSS(Cross Site Scripting)対策設定",
-    "xss_prevent_setting_link": "マークダウン設定ページに移動",
-    "callback_URL": "コールバックURL",
-    "desc_of_callback_URL": "{{AuthName}} プロバイダ側の設定で利用してください。",
-    "authorization_endpoint": "認可エンドポイント",
-    "token_endpoint": "トークンエンドポイント",
-    "revocation_endpoint": "失効エンドポイント",
-    "introspection_endpoint": "検証エンドポイント",
-    "userinfo_endpoint": "ユーザ情報エンドポイント",
-    "end_session_endpoint": "セッション終了エンドポイント",
-    "registration_endpoint": "登録エンドポイント",
-    "jwks_uri": "JSON Web Key Set URL",
-    "clientID": "クライアントID",
-    "client_secret": "クライアントシークレット",
-    "updated_general_security_setting": "セキュリティ設定を更新しました。",
-    "setup_not_completed_yet": "まだセットアップは完了していません。",
-    "guest_mode": {
-      "deny": "拒否 (アカウントを持つユーザーのみ利用可能)",
-      "readonly": "許可 (ゲストユーザーも閲覧のみ可能)"
-    },
-    "registration_mode": {
-      "open": "公開 (だれでも登録可能)",
-      "restricted": "制限 (登録完了には管理者の承認が必要)",
-      "closed": "非公開 (登録には管理者による招待が必要)"
-    },
-    "share_link_rights": "シェアリンクの権限",
-    "enable_link_sharing": "リンクのシェアを許可",
-    "all_share_links": "全てのシェアリンク",
-    "configuration": "設定",
-    "optional": "オプション",
-    "Treat username matching as identical": "新規ログイン時、<code>username</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
-    "Treat username matching as identical_warn": "警告: <code>username</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください",
-    "Treat email matching as identical": "新規ログイン時、<code>email</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
-    "Treat email matching as identical_warn": "警告: <code>email</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください",
-    "Use env var if empty": "空の場合、環境変数 <code>{{env}}</code> を利用します",
-    "Use default if both are empty": "どちらの値も空の場合、デフォルト値 <code>{{target}}</code> を利用します",
-    "missing mandatory configs": "以下の必須項目の値がデータベースと環境変数のどちらにも設定されていません",
-    "Local": {
-      "name": "ID/Password",
-      "note for the only env option": "現在LOCAL認証のON/OFFは環境変数の値によって制限されています<br>この設定を変更する場合は環境変数 <code>{{env}}</code> の値をfalseに変更もしくは削除してください",
-      "enable_local": "ID/Password を有効にする",
-      "password_reset_by_users": "ユーザーによるパスワード再設定",
-      "enable_password_reset_by_users": "ユーザーによるパスワード再設定を有効にする",
-      "password_reset_desc": "ログイン時のパスワードを忘れた際に、ユーザー自身がパスワードを再設定できます。",
-      "email_authentication": "ユーザー登録時のメール認証",
-      "enable_email_authentication": "メール認証を有効にする",
-      "enable_email_authentication_desc": "ユーザー登録時にメール認証を行います。",
-      "please_enable_mailer": "メール認証を有効にするには、メール設定を完了させてください。",
-      "need_complete_mail_setting_warning": "以下の機能を使えるようにするには、メール設定を完了させてください。"
-    },
-    "ldap": {
-      "enable_ldap": "LDAP を有効にする",
-      "server_url_detail": "LDAP URLを <code>ldap://host:port/DN</code> または <code>ldaps://host:port/DN</code> の形式で入力してください。",
-      "bind_mode": "Bind モード",
-      "bind_manager": "管理者 Bind",
-      "bind_user": "ユーザー Bind",
-      "bind_DN_manager_detail": "ディレクトリーサービスに認証する際のアカウント DN",
-      "bind_DN_user_detail1": "ディレクトリーサービスに Bind するアカウント DN を決定するためのクエリ",
-      "bind_DN_user_detail2": "ログイン時に入力されるユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
-      "bind_DN_password": "Bind DN パスワード",
-      "bind_DN_password_manager_detail": "Bind DN アカウントのパスワード",
-      "bind_DN_password_user_detail": "ログイン時のパスワードが使用されます。",
-      "search_filter": "検索フィルター",
-      "search_filter_detail1": "認証されるユーザーを一意に決定するための LDAP フィルタ",
-      "search_filter_detail2": "ログイン時のユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
-      "search_filter_detail3": "空欄の場合 <code>(uid=&#123;&#123;username&#125;&#125;)</code> が使用されます。",
-      "search_filter_example1": "'uid' または 'mail' に一致",
-      "search_filter_example2": "'sAMAccountName' に一致 (Active Directory)",
-      "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
-      "name_detail": "新規ユーザーの表示名に関連付ける属性",
-      "mail_detail": "新規ユーザーのメールアドレスに関連付ける属性",
-      "group_search_base_DN": "グループ検索ベース DN",
-      "group_search_base_DN_detail": "グループ検索を実行するベース DN。利用する場合は <code>グループ検索フィルター</code> も入力する必要があります。",
-      "group_search_filter": "グループ検索フィルター",
-      "group_search_filter_detail1": "グループフィルターに用いるクエリ",
-      "group_search_filter_detail2": "このクエリにヒットするグループがあったときのみ、LDAPでのログインが成功します。",
-      "group_search_filter_detail3": "ログイン対象ユーザーオブジェクトのプロパティーで置換する場合は <code>&#123;&#123;dn&#125;&#125;</code> を用いてください。",
-      "group_search_filter_detail4": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> は <code>cn=group1</code> と、ユーザーの <code>uid</code> を含む <code>memberUid</code> を持つグループにヒットします(<code>ユーザーの DN プロパティー</code> がデフォルトから変更されていない場合)",
-      "group_search_user_DN_property": "ユーザーの DN プロパティー",
-      "group_search_user_DN_property_detail": "<code>グループ検索フィルター</code> 内の <code>&#123;&#123;dn&#125;&#125;</code> で置換される、ユーザーオブジェクトのプロパティー",
-      "test_config": "ログインテスト",
-      "updated_ldap": "LDAP設定 を更新しました"
-    },
-    "SAML": {
-      "name": "SAML",
-      "enable_saml": "SAML を有効にする",
-      "id_detail": "SAML Identity プロバイダ内で一意に識別可能な値を格納している属性",
-      "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
-      "mapping_detail": "新規ユーザーの{{target}}に関連付ける属性",
-      "cert_detail": "IdP からのレスポンスの validation を行うためのPEMエンコードされた X.509 証明書",
-      "Use env var if empty": "データベース側の値が空の場合、環境変数 <code>{{env}}</code> の値を利用します",
-      "note for the only env option": "現在SAML認証のON/OFFの設定値及びハイライトされている設定値は環境変数の値のみを使用するようになっています<br>この設定を変更する場合は環境変数 <code>{{env}}</code> の値をfalseに変更もしくは削除してください",
-      "attr_based_login_control_detail": "SAMLの <code>&lt;saml:AttributeStatement&gt;</code> 要素に含まれる <code>&lt;saml:Attribute&gt;</code> 要素と、その子要素 <code>&lt;saml:AttributeValue&gt;</code> を利用してログインの可否を制御します。",
-      "attr_based_login_control_rule_help": "<h5>利用可能なクエリ:</h5><ul><li>Terms</li><li>Fields</li><li>AND/NOT/OR Operator</li><li>Grouping</li></ul><h5>利用不可なクエリ:</h5><ul><li>Wildcard, Fuzzy, Proximity, Range and Boosting</li><li>+/- Operator</li><li>Field Grouping</li></ul><h5>特殊文字のエスケープ</h5>次の特殊文字はエスケープする必要があります。<code>+ - && || ! ( ) { } [ ] ^ &quot; &tilde; * ? : &#92;</code> and <code>/</code>",
-      "attr_based_login_control_rule_example1": "<h5>条件式の例</h5>ルールに <code>(Department: A || Department: B) && Position: Leader</code> を指定した場合, <code>Department: A</code> または <code>Department: B</code> のどちらかに該当し、かつ <code>Position: Leader</code> を持つユーザーにログインを<strong>許可</strong>します。",
-      "attr_based_login_control_rule_exampl2": "<h5>エスケープの例</h5>ルールに URL を利用したい場合は、次のようにエスケープしてください:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
-      "updated_saml": "Succeeded to update SAML setting"
-    },
-    "Basic": {
-      "enable_basic": "Basic を有効にする",
-      "name": "Basic 認証",
-      "desc_1": "Authorization ヘッダに格納されている <code>username</code> でログインします。",
-      "desc_2": "ユーザーが存在しなかった場合は自動生成します。",
-      "updated_basic": "Basic認証 を更新しました"
-    },
-    "OAuth": {
-      "enable_oidc": "OIDC を有効にする",
-      "register": "%sに登録",
-      "change_redirect_url": "承認済みのリダイレクトURLに、 <code>%s</code> を入力",
-      "Google": {
-        "enable_google": "Google OAuth を有効にする",
-        "name": "Google OAuth",
-        "register_1": "{{link}}へアクセス",
-        "register_2": "プロジェクトがない場合はプロジェクトを作成",
-        "register_3": "認証情報を作成 &rightarrow; OAuthクライアントID &rightarrow; ウェブアプリケーションを選択",
-        "register_4": "承認済みのリダイレクトURIを<code>{{url}}</code>としてGrowiを登録",
-        "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力",
-        "updated_google": "Google OAuth を更新しました"
-      },
-      "Facebook": {
-        "name": "Facebook OAuth"
-      },
-      "Twitter": {
-        "enable_twitter": "Twitter OAuth を有効にする",
-        "name": "Twitter OAuth",
-        "register_1": "{{link}} へアクセス",
-        "register_2": "Twitterにサインイン",
-        "register_3": "Create New Appをクリック &rightarrow; Application Detailsの各項目を入力",
-        "register_4": "Create your Twitter Applicationで作成",
-        "register_5": "上記フォームにクライアントIDとクライアントシークレットを入力",
-        "updated_twitter": "Twitter OAuth を更新しました"
-      },
-      "GitHub": {
-        "enable_github": "GitHub OAuth を有効にする",
-        "name": "GitHub OAuth",
-        "register_1": "{{link}} へアクセス",
-        "register_2": "\"Authorization callback URL\"を<code>{{url}}</code>としてGrowiを登録",
-        "register_3": "上記フォームにクライアントIDとクライアントシークレットを入力",
-        "updated_github": "GitHub OAuth を更新しました"
-      },
-      "OIDC": {
-        "name": "OpenID Connect",
-        "id_detail": "OIDC claims で一意に識別可能な値を格納している属性",
-        "username_detail": "新規ユーザーのアカウント名(<code>username</code>)に関連付ける属性",
-        "name_detail": "新規ユーザー名(<code>name</code>)に関連付ける属性",
-        "mapping_detail": "新規ユーザーの{{target}}に関連付ける属性",
-        "updated_oidc": "OpenID Connect を更新しました",
-        "Use discovered URL if empty": "データベース側の値が空の場合、\"Issuer Host\"から検出した値を利用します。"
-      },
-      "how_to": {
-        "google": "Google OAuth の設定方法",
-        "github": "GitHub OAuth の設定方法",
-        "twitter": "Twitter OAuth の設定方法"
-      }
-    },
-    "form_item_name": {
-      "entryPoint": "エントリーポイント",
-      "issuer": "発行者",
-      "cert": "証明書",
-      "attrMapId": "ID",
-      "attrMapUsername": "ユーザー名",
-      "attrMapMail": "メールアドレス",
-      "attrMapFirstName": "姓",
-      "attrMapLastName": "名",
-      "ABLCRule": "ルール"
-    }
-  },
-  "notification_setting": {
-    "slack_incoming_configuration": "Slack Incoming Webhooks 設定",
-    "prioritize_webhook": "Slack アプリより Incoming Webhook を優先する",
-    "prioritize_webhook_desc": "このオプションをオンにすると、 Slack App が有効になっていても GROWI は Incoming Webhook を使用します。",
-    "slack_app_configuration": "Slack App 設定",
-    "slack_app_configuration_desc": "Crowi 互換の機能です。<br /> <strong>設定が複雑すぎる</strong>のでオススメしません。",
-    "use_instead": "代わりに Slack Incoming Webhooks 設定を使用してください。",
-    "how_to": {
-      "header": "Incoming Webhooks の設定方法",
-      "workspace": "ワークスペースで Webhook を追加します。",
-      "workspace_desc1": "<a href='https://slack.com/services/new/incoming-webhook'>Incoming Webhooks Configuration page</a> にアクセスします。",
-      "workspace_desc2": "投稿するチャンネルを選びます。",
-      "workspace_desc3": "追加します。",
-      "at_growi": "GROWI 管理画面で Webhook URL を設定します。",
-      "at_growi_desc": "このページで &rdquo;Webhook URL&rdquo; を入力して送信します。"
-    },
-    "user_trigger_notification_header": "デフォルトパターンの通知設定",
-    "pattern": "パターン",
-    "channel": "チャンネル名",
-    "pattern_desc": "Wiki のパス名。 パスには <code>*</code> を使用できます。",
-    "channel_desc": "<code>#</code> を除いた Slack チャンネル名",
-    "valid_page": "通知の有効 / 無効",
-    "link_notification_help": "<strong>linkを知っている人のみ閲覧できるページ</strong>は常に通知されません。",
-    "just_me_notification_help": "<strong>'自分のみ'に閲覧制限をしているページ</strong>に変更を加えた際に通知する",
-    "group_notification_help": "<strong>'特定グループにのみ'に閲覧制限をしているページ</strong>に変更を加えた際に通知する",
-    "notification_list": "通知設定の一覧",
-    "add_notification": "通知設定の追加",
-    "trigger_path": "トリガーパス",
-    "trigger_path_help": "(<code>*</code>が使用できます)",
-    "trigger_events": "トリガーイベント",
-    "notify_to": "通知先",
-    "back_to_list": "通知設定一覧に戻る",
-    "notification_detail": "通知詳細設定",
-    "event_pageCreate": "ページが新規作成されたとき",
-    "event_pageEdit": "ページが編集されたとき",
-    "event_pageDelete": "ページが削除されたとき",
-    "event_pageMove": "ページが移動(名前が変更)されたとき",
-    "event_pageLike": "ページに「いいね」がついたとき",
-    "event_comment": "コメントが投稿されたとき",
-    "email": {
-      "ifttt_link": "IFTTT でメールトリガの新しいアプレットを作る"
-    },
-    "updated_slackApp": "SlackApp設定を更新しました",
-    "add_notification_pattern": "通知パターンを追加しました。",
-    "delete_notification_pattern": "通知パターンを削除しました。",
-    "delete_notification_pattern_desc1": "Path: {{path}} を削除します。",
-    "delete_notification_pattern_desc2": "Once deleted, it cannot be recovered",
-    "toggle_notification": "{{path}}の通知設定を変更しました"
-  },
-  "full_text_search_management": {
-    "elasticsearch_management": "Elasticsearch 管理",
-    "connection_status": "接続の状態",
-    "connection_status_label_unconfigured": "設定されていません",
-    "connection_status_label_connected": "接続されています",
-    "connection_status_label_disconnected": "切断されています",
-    "connection_status_label_erroroccured": "SearchService でエラーが発生しています",
-    "indices_status": "インデックスの状態",
-    "indices_status_label_normalized": "正規化されています",
-    "indices_status_label_unnormalized": "リビルド中 または 破損しています",
-    "indices_summary": "インデックスのサマリ",
-    "reconnect": "再接続",
-    "reconnect_button": "再接続の試行",
-    "reconnect_description": "Elasticsearch への再接続を試みます。",
-    "normalize": "正規化",
-    "normalize_button": "インデックスの正規化",
-    "normalize_description": "破損したインデックスを修復します。",
-    "rebuild": "リビルド",
-    "rebuild_button": "インデックスのリビルド",
-    "rebuild_description_1": "全てのページのインデックスを削除し、作り直します。",
-    "rebuild_description_2": "この作業には数秒かかります。"
-  },
   "to_cloud_settings": "GROWI.cloud の管理画面へ",
   "login": {
     "Sign in error": "ログインエラー",

+ 5 - 4
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -28,15 +28,16 @@ const AdminNavigation = (props) => {
       case 'app':                      return <><i className="icon-fw icon-settings"></i>        { t('app_settings') }</>;
       case 'security':                 return <><i className="icon-fw icon-shield"></i>          { t('security_settings.security_settings') }</>;
       case 'markdown':                 return <><i className="icon-fw icon-note"></i>            { t('markdown_settings') }</>;
-      case 'customize':                return <><i className="icon-fw icon-wrench"></i>          { t('Customize') }</>;
-      case 'importer':                 return <><i className="icon-fw icon-cloud-upload"></i>    { t('Import Data') }</>;
-      case 'export':                   return <><i className="icon-fw icon-cloud-download"></i>  { t('Export Archive Data') }</>;
+      case 'customize':                return <><i className="icon-fw icon-wrench"></i>          { t('customize') }</>;
+      case 'importer':                 return <><i className="icon-fw icon-cloud-upload"></i>    { t('ipmport_data') }</>;
+      case 'export':                   return <><i className="icon-fw icon-cloud-download"></i>  { t('export_archive_data') }</>;
       case 'notification':             return <><i className="icon-fw icon-bell"></i>            { t('external_notification.external_notification')}</>;
       case 'slack-integration':        return <><i className="icon-fw icon-shuffle"></i>         { t('slack_integration.slack_integration') }</>;
       case 'slack-integration-legacy': return <><i className="icon-fw icon-shuffle"></i>         { t('slack_integration_legacy.slack_integration_legacy')}</>;
       case 'users':                    return <><i className="icon-fw icon-user"></i>            { t('user_management.user_management') }</>;
       case 'user-groups':              return <><i className="icon-fw icon-people"></i>          { t('user_group_management.user_group_management') }</>;
-      case 'search':                   return <><i className="icon-fw icon-magnifier"></i>       { t('full_text_search_management') }</>;
+      case 'search':                   return <><i className="icon-fw icon-magnifier"></i>
+        { t('full_text_search_management.full_text_search_management') }</>;
       // TODO: Consider where to place the "AuditLog"
       case 'audit-log':                return <><i className="icon-fw icon-feed"></i>            { t('audit_log_management.audit_log')}</>;
       case 'cloud':                    return <><i className="icon-fw icon-share-alt"></i>       { t('to_cloud_settings')} </>;

+ 1 - 1
packages/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -15,7 +15,7 @@ import ReconnectControls from './ReconnectControls';
 import StatusTable from './StatusTable';
 
 const ElasticsearchManagement = () => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
   const { data: socket } = useAdminSocket();
 

+ 1 - 1
packages/app/src/components/Admin/ElasticsearchManagement/ReconnectControls.tsx

@@ -9,7 +9,7 @@ type Props = {
 }
 
 const ReconnectControls = (props: Props): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   const { isEnabled, isProcessing } = props;
 

+ 1 - 1
packages/app/src/components/Admin/ElasticsearchManagement/StatusTable.jsx

@@ -161,7 +161,7 @@ class StatusTable extends React.PureComponent {
 }
 
 const StatusTableWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <StatusTable t={t} {...props} />;
 };

+ 1 - 1
packages/app/src/components/Admin/ExportArchiveDataPage.jsx

@@ -212,7 +212,7 @@ class ExportArchiveDataPage extends React.Component {
 
     return (
       <div data-testid="admin-export-archive-data">
-        <h2>{t('Export Archive Data')}</h2>
+        <h2>{t('export_archive_data')}</h2>
 
         <button type="button" className="btn btn-outline-secondary" disabled={isExporting} onClick={this.openExportModal}>
           {t('admin:export_management.create_new_archive_data')}

+ 7 - 7
packages/app/src/components/Admin/Security/SecuritySetting.jsx

@@ -1,8 +1,8 @@
 /* eslint-disable react/no-danger */
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import { Collapse } from 'reactstrap';
 
 import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
@@ -340,21 +340,21 @@ class SecuritySetting extends React.Component {
           <table className="table table-bordered col-lg-9 mb-5">
             <thead>
               <tr>
-                <th scope="col">{ t('scope_of_page_disclosure') }</th>
-                <th scope="col">{ t('set_point') }</th>
+                <th scope="col">{ t('security_settings.scope_of_page_disclosure') }</th>
+                <th scope="col">{ t('security_settings.set_point') }</th>
               </tr>
             </thead>
             <tbody>
               <tr>
-                <th scope="row">{ t('Public') }</th>
+                <th scope="row">{ t('public') }</th>
                 <td>{ t('security_settings.always_displayed') }</td>
               </tr>
               <tr>
-                <th scope="row">{ t('Anyone with the link') }</th>
+                <th scope="row">{ t('anyone_with_the_link') }</th>
                 <td>{ t('security_settings.always_hidden') }</td>
               </tr>
               <tr>
-                <th scope="row">{ t('Only me') }</th>
+                <th scope="row">{ t('only_me') }</th>
                 <td>
                   <div className="custom-control custom-switch custom-checkbox-success">
                     <input
@@ -371,7 +371,7 @@ class SecuritySetting extends React.Component {
                 </td>
               </tr>
               <tr>
-                <th scope="row">{ t('Only inside the group') }</th>
+                <th scope="row">{ t('only_inside_the_group') }</th>
                 <td>
                   <div className="custom-control custom-switch custom-checkbox-success">
                     <input

+ 2 - 0
packages/app/src/components/Layout/BasicLayout.tsx

@@ -18,6 +18,7 @@ const PageDuplicateModal = dynamic(() => import('../PageDuplicateModal'), { ssr:
 const PageDeleteModal = dynamic(() => import('../PageDeleteModal'), { ssr: false });
 const PageRenameModal = dynamic(() => import('../PageRenameModal'), { ssr: false });
 const PagePresentationModal = dynamic(() => import('../PagePresentationModal'), { ssr: false });
+const PageAccessoriesModal = dynamic(() => import('../PageAccessoriesModal'), { ssr: false });
 
 
 type Props = {
@@ -54,6 +55,7 @@ export const BasicLayout = ({
       <PageDeleteModal />
       <PageRenameModal />
       <PagePresentationModal />
+      <PageAccessoriesModal />
       {/* <HotkeysManager /> */}
 
       <ShortcutsModal />

+ 24 - 24
packages/app/src/components/PageAccessoriesModal.tsx

@@ -6,8 +6,7 @@ import {
 } from 'reactstrap';
 
 
-import AppContainer from '~/client/services/AppContainer';
-import { useIsGuestUser, useIsSharedUser } from '~/stores/context';
+import { useDisableLinkSharing, useIsGuestUser, useIsSharedUser } from '~/stores/context';
 import { usePageAccessoriesModal, PageAccessoriesModalContents } from '~/stores/modal';
 
 import { CustomNavTab } from './CustomNavigation/CustomNav';
@@ -19,21 +18,10 @@ import ShareLinkIcon from './Icons/ShareLinkIcon';
 import PageAttachment from './PageAttachment';
 import PageHistory from './PageHistory';
 import ShareLink from './ShareLink/ShareLink';
-import { withUnstatedContainers } from './UnstatedUtils';
 
 import styles from './PageAccessoriesModal.module.scss';
 
-type Props = {
-  appContainer: AppContainer,
-  isLinkSharingDisabled: boolean,
-}
-
-const PageAccessoriesModal = (props: Props): JSX.Element => {
-  const {
-    appContainer,
-  } = props;
-
-  const isLinkSharingDisabled = appContainer.config.disableLinkSharing;
+const PageAccessoriesModal = (): JSX.Element => {
 
   const { t } = useTranslation();
 
@@ -42,6 +30,7 @@ const PageAccessoriesModal = (props: Props): JSX.Element => {
 
   const { data: isSharedUser } = useIsSharedUser();
   const { data: isGuestUser } = useIsGuestUser();
+  const { data: isLinkSharingDisabled } = useDisableLinkSharing();
 
   const { data: status, mutate, close } = usePageAccessoriesModal();
 
@@ -59,29 +48,45 @@ const PageAccessoriesModal = (props: Props): JSX.Element => {
   }, [mutate, status]);
 
   const navTabMapping = useMemo(() => {
+    const isOpened = status == null ? false : status.isOpened;
     return {
       [PageAccessoriesModalContents.PageHistory]: {
         Icon: HistoryIcon,
-        Content: () => <PageHistory />,
+        Content: () => {
+          if (!isOpened) {
+            return <></>;
+          }
+          return <PageHistory />;
+        },
         i18n: t('History'),
         index: 0,
         isLinkEnabled: () => !isGuestUser && !isSharedUser,
       },
       [PageAccessoriesModalContents.Attachment]: {
         Icon: AttachmentIcon,
-        Content: () => <PageAttachment />,
+        Content: () => {
+          if (!isOpened) {
+            return <></>;
+          }
+          return <PageAttachment />;
+        },
         i18n: t('attachment_data'),
         index: 1,
       },
       [PageAccessoriesModalContents.ShareLink]: {
         Icon: ShareLinkIcon,
-        Content: () => <ShareLink />,
+        Content: () => {
+          if (!isOpened) {
+            return <></>;
+          }
+          return <ShareLink />;
+        },
         i18n: t('share_links.share_link_management'),
         index: 2,
         isLinkEnabled: () => !isGuestUser && !isSharedUser && !isLinkSharingDisabled,
       },
     };
-  }, [t, isGuestUser, isSharedUser, isLinkSharingDisabled]);
+  }, [status, t, isGuestUser, isSharedUser, isLinkSharingDisabled]);
 
   const buttons = useMemo(() => (
     <div className="d-flex flex-nowrap">
@@ -128,9 +133,4 @@ const PageAccessoriesModal = (props: Props): JSX.Element => {
   );
 };
 
-/**
- * Wrapper component for using unstated
- */
-const PageAccessoriesModalWrapper = withUnstatedContainers(PageAccessoriesModal, [AppContainer]);
-
-export default PageAccessoriesModalWrapper;
+export default PageAccessoriesModal;

+ 37 - 0
packages/app/src/components/PageEditor/CodeMirrorEditor.module.scss

@@ -90,4 +90,41 @@
       }
     }
   }
+
+  // cheat sheat
+  .overlay.overlay-gfm-cheatsheet {
+    align-items: flex-end;
+    justify-content: flex-end;
+
+    pointer-events: none;
+
+    .card.gfm-cheatsheet {
+      box-shadow: unset;
+      opacity: 0.6;
+      .card-body {
+        min-width: 30em;
+        padding-bottom: 0;
+        font-family: monospace;
+        color: bs.$text-muted;
+      }
+      ul > li {
+        list-style: none;
+      }
+    }
+
+    .gfm-cheatsheet-modal-link {
+      color: bs.$text-muted;
+      pointer-events: all;
+      cursor: pointer;
+      background-color: transparent;
+      border: none;
+
+      opacity: 0.6;
+
+      &:hover,
+      &:focus {
+        opacity: 1;
+      }
+    }
+  }
 }

+ 66 - 20
packages/app/src/styles/_editor-attachment.scss → packages/app/src/components/PageEditor/Editor.module.scss

@@ -1,19 +1,29 @@
-@import 'editor-overlay';
+@use '~/styles/mixins' as ms;
+@use '~/styles/bootstrap/init' as bs;
+
+
+.editor-container :global {
+  // overlay in .editor-container
+  .overlay {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 7; // forward than .CodeMirror-vscrollbar
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  // loading keymap
+  @include ms.overlay-processing-style(overlay-loading-keymap, 2.5em, 0.3em);
 
-.editor-container {
   // for Dropzone
   .dropzone {
-    @mixin insertSimpleLineIcons($code) {
-      &:before {
-        margin-right: 0.2em;
-        font-family: 'simple-line-icons';
-        content: $code;
-      }
-    }
-
     position: relative; // against .overlay position: absolute
 
-    @include overlay-processing-style(overlay-dropzone-active, 2.5em, 0.5em);
+    @include ms.overlay-processing-style(overlay-dropzone-active, 2.5em, 0.5em);
 
     // unuploadable or rejected
     &.dropzone-unuploadable,
@@ -22,14 +32,14 @@
         background: rgba(200, 200, 200, 0.8);
 
         .overlay-content {
-          color: $gray-700;
+          color: bs.$gray-300;
         }
       }
     }
 
     // uploading
     &.dropzone-uploading {
-      @include overlay-processing-style(overlay-dropzone-active, 2.5em, 0.5em);
+      @include ms.overlay-processing-style(overlay-dropzone-active, 2.5em, 0.5em);
     }
 
     // unuploadable
@@ -37,7 +47,7 @@
       .overlay.overlay-dropzone-active {
         .overlay-content {
           // insert content
-          @include insertSimpleLineIcons('\e617'); // icon-exclamation
+          @include ms.insertSimpleLineIcons('\e617'); // icon-exclamation
 
           &:after {
             content: 'File uploading is disabled';
@@ -51,18 +61,18 @@
       // accepted
       &.dropzone-accepted:not(.dropzone-rejected) {
         .overlay.overlay-dropzone-active {
-          border: 4px dashed $gray-300;
+          border: 4px dashed bs.$gray-300;
 
           .overlay-content {
             // insert content
-            @include insertSimpleLineIcons('\e084'); // icon-cloud-upload
+            @include ms.insertSimpleLineIcons('\e084'); // icon-cloud-upload
 
             &:after {
               content: 'Drop here to upload';
             }
 
             // style
-            color: $secondary;
+            color: bs.$secondary;
             background: rgba(200, 200, 200, 0.8);
           }
         }
@@ -73,7 +83,7 @@
         .overlay.overlay-dropzone-active {
           .overlay-content {
             // insert content
-            @include insertSimpleLineIcons('\e032'); // icon-picture
+            @include ms.insertSimpleLineIcons('\e032'); // icon-picture
 
             &:after {
               content: 'Only an image file is allowed';
@@ -87,7 +97,7 @@
         .overlay.overlay-dropzone-active {
           .overlay-content {
             // insert content
-            @include insertSimpleLineIcons('\e617'); // icon-exclamation
+            @include ms.insertSimpleLineIcons('\e617'); // icon-exclamation
 
             &:after {
               content: 'Only 1 file is allowed';
@@ -106,7 +116,7 @@
     padding-bottom: 3px;
     font-size: small;
     border: none;
-    border-top: 1px dotted $gray-300;
+    border-top: 1px dotted bs.$gray-300;
     border-bottom: none;
 
     &:hover,
@@ -114,4 +124,40 @@
       border-bottom: none;
     }
   }
+
+  // for Navbar editor
+  .navbar-editor {
+    height: 30px;
+    padding: 0;
+
+    border-bottom: 1px solid transparent;
+
+    li {
+      display: inline-block;
+      i {
+        font-size: 16px;
+      }
+    }
+
+    button {
+      padding: 0px;
+      margin: 0 2px;
+      font-size: 1rem;
+      line-height: 1;
+      background-color: transparent;
+      border: none;
+    }
+
+    img {
+      vertical-align: bottom;
+    }
+  }
+}
+
+.modal-gfm-cheatsheet :global {
+  .modal-body {
+    .hljs {
+      font-family: bs.$font-family-monospace;
+    }
+  }
 }

+ 4 - 2
packages/app/src/components/PageEditor/Editor.tsx

@@ -20,6 +20,8 @@ import CodeMirrorEditor from './CodeMirrorEditor';
 import pasteHelper from './PasteHelper';
 import TextAreaEditor from './TextAreaEditor';
 
+import styles from './Editor.module.scss';
+
 type EditorPropsType = {
   value?: string,
   isGfmMode?: boolean,
@@ -252,7 +254,7 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
     };
 
     return (
-      <Modal isOpen={isCheatsheetModalShown} toggle={hideCheatsheetModal} className="modal-gfm-cheatsheet">
+      <Modal isOpen={isCheatsheetModalShown} toggle={hideCheatsheetModal} className={`${styles['modal-gfm-cheatsheet']}`} >
         <ModalHeader tag="h4" toggle={hideCheatsheetModal} className="bg-primary text-light">
           <i className="icon-fw icon-question" />Markdown help
         </ModalHeader>
@@ -275,7 +277,7 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
 
   return (
     <>
-      <div style={flexContainer} className="editor-container">
+      <div style={flexContainer} className={`editor-container ${styles['editor-container']}`} >
         <Dropzone
           ref={dropzoneRef}
           accept={getAcceptableType()}

+ 4 - 2
packages/app/src/components/PageEditor/GridEditModal.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
@@ -10,6 +10,8 @@ import BootstrapGrid from '~/client/models/BootstrapGrid';
 
 import geu from './GridEditorUtil';
 
+import styles from './GridEditModal.module.scss';
+
 const resSizes = BootstrapGrid.ResponsiveSize;
 const resSizeObj = {
   [resSizes.XS_SIZE]: { displayText: 'grid_edit.smart_no' },
@@ -188,7 +190,7 @@ class GridEditModal extends React.Component {
   render() {
     const { t } = this.props;
     return (
-      <Modal isOpen={this.state.show} toggle={this.cancel} size="xl" className="grw-grid-edit-modal">
+      <Modal isOpen={this.state.show} toggle={this.cancel} size="xl" className={`${styles['grw-grid-edit-modal']}`}>
         <ModalHeader tag="h4" toggle={this.cancel} className="bg-primary text-light">
           {t('grid_edit.create_bootstrap_4_grid')}
         </ModalHeader>

+ 43 - 0
packages/app/src/components/PageEditor/GridEditModal.module.scss

@@ -0,0 +1,43 @@
+@use '~/styles/bootstrap/init' as bs;
+
+.grw-grid-edit-modal {
+  .desktop-preview,
+  .tablet-preview,
+  .mobile-preview {
+    .row {
+      height: 140px;
+      margin: 0px;
+    }
+  }
+  .desktop-preview {
+    .row {
+      div {
+        padding: 0px;
+      }
+    }
+  }
+
+  .tablet-preview {
+    .row {
+      div {
+        padding: 0px;
+      }
+    }
+  }
+
+  .mobile-preview {
+    width: 75%;
+    .row {
+      div {
+        padding: 0px;
+      }
+    }
+  }
+
+  .grid-division-menu {
+    width: 60vw;
+    @include bs.media-breakpoint-down(lg) {
+      width: 80vw;
+    }
+  }
+}

+ 1 - 1
packages/app/src/components/PageEditor/Preview.tsx

@@ -39,7 +39,7 @@ const Preview = React.forwardRef((props: Props, ref: RefObject<HTMLDivElement>):
         }
       }}
     >
-      <ReactMarkdown {...rendererOptions} >{markdown || ''}</ReactMarkdown>
+      <ReactMarkdown {...rendererOptions} className='wiki'>{markdown || ''}</ReactMarkdown>
     </div>
   );
 

+ 75 - 72
packages/app/src/pages/[[...path]].page.tsx

@@ -291,89 +291,92 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
       </Head>
       {/* <BasicLayout title={useCustomTitle(props, t('GROWI'))} className={classNames.join(' ')}> */}
       <BasicLayout title={useCustomTitle(props, 'GROWI')} className={classNames.join(' ')} expandContainer={props.isContainerFluid}>
-        <header className="py-0 position-relative">
-          <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />
-        </header>
-        <div className="d-edit-none">
-          <GrowiSubNavigationSwitcher />
-        </div>
+        <div className="h-100 d-flex flex-column justify-content-between">
+          <header className="py-0 position-relative">
+            <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />
+          </header>
+          <div className="d-edit-none">
+            <GrowiSubNavigationSwitcher />
+          </div>
 
-        <div id="grw-subnav-sticky-trigger" className="sticky-top"></div>
-        <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
-
-        <div id="main" className={`main ${isUsersHomePage(props.currentPathname) && 'user-page'}`}>
-          <div id="content-main" className="content-main grw-container-convertible">
-            <div className="row">
-              <div className="col">
-                { props.isIdenticalPathPage && <IdenticalPathPage /> }
-
-                { !props.isIdenticalPathPage && (
-                  <>
-                    <PageAlerts />
-                    { props.isForbidden && <ForbiddenPage /> }
-                    { props.IsNotCreatable && <NotCreatablePage />}
-                    { !props.isForbidden && !props.IsNotCreatable && <DisplaySwitcher />}
-                    {/* <DisplaySwitcher /> */}
-                    <div id="page-editor-navbar-bottom-container" className="d-none d-edit-block"></div>
-                    {/* <PageStatusAlert /> */}
-                  </>
-                ) }
+          <div id="grw-subnav-sticky-trigger" className="sticky-top"></div>
+          <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
+
+          <div className="flex-grow-1">
+            <div id="main" className={`main ${isUsersHomePage(props.currentPathname) && 'user-page'}`}>
+              <div id="content-main" className="content-main grw-container-convertible">
+                <div className="row">
+                  <div className="col">
+                    { props.isIdenticalPathPage && <IdenticalPathPage /> }
+
+                    { !props.isIdenticalPathPage && (
+                      <>
+                        <PageAlerts />
+                        { props.isForbidden && <ForbiddenPage /> }
+                        { props.IsNotCreatable && <NotCreatablePage />}
+                        { !props.isForbidden && !props.IsNotCreatable && <DisplaySwitcher />}
+                        {/* <DisplaySwitcher /> */}
+                        <div id="page-editor-navbar-bottom-container" className="d-none d-edit-block"></div>
+                        {/* <PageStatusAlert /> */}
+                      </>
+                    ) }
 
-              </div>
-            </div>
+                  </div>
+                </div>
 
-            {/* <div className="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container">
-              <div id="revision-toc" className="revision-toc mt-3 sps sps--abv" data-sps-offset="123">
-                <div id="revision-toc-content" className="revision-toc-content"></div>
+                {/* <div className="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container">
+                  <div id="revision-toc" className="revision-toc mt-3 sps sps--abv" data-sps-offset="123">
+                    <div id="revision-toc-content" className="revision-toc-content"></div>
+                  </div>
+                </div> */}
               </div>
-            </div> */}
+            </div>
           </div>
-        </div>
-        {/* TODO: Check CSS import */}
-        <footer className="footer d-edit-none">
-          {/* TODO: Enable page_list.html */}
-          {/* TODO: Enable isIdenticalPathPage or useIdenticalPath */}
-          {/* { !props.isIdenticalPathPage && ( */}
-          <Comments pageId={pageId} />
-          {/* )} */}
-          {/* TODO: Create UsersHomePageFooter conponent */}
-          { isUsersHomePage(props.currentPathname) && (
-            <div className="container-lg user-page-footer py-5">
-              <div className="grw-user-page-list-m d-edit-none">
-                <h2 id="bookmarks-list" className="grw-user-page-header border-bottom pb-2 mb-3">
-                  <i style={{ fontSize: '1.3em' }} className="fa fa-fw fa-bookmark-o"></i>
-                  Bookmarks
-                </h2>
-                <div id="user-bookmark-list" className={`page-list ${styles['page-list']}`}>
-                  {/* TODO: No need page-list-container class ? */}
-                  <div className="page-list-container">
-                    {/* <BookmarkList userId={pageContainer.state.creator._id} /> */}
+          {/* TODO: Check CSS import */}
+          <footer className="footer d-edit-none">
+            {/* TODO: Enable page_list.html */}
+            {/* TODO: Enable isIdenticalPathPage or useIdenticalPath */}
+            {/* { !props.isIdenticalPathPage && ( */}
+            <Comments pageId={pageId} />
+            {/* )} */}
+            {/* TODO: Create UsersHomePageFooter conponent */}
+            { isUsersHomePage(props.currentPathname) && (
+              <div className="container-lg user-page-footer py-5">
+                <div className="grw-user-page-list-m d-edit-none">
+                  <h2 id="bookmarks-list" className="grw-user-page-header border-bottom pb-2 mb-3">
+                    <i style={{ fontSize: '1.3em' }} className="fa fa-fw fa-bookmark-o"></i>
+                    Bookmarks
+                  </h2>
+                  <div id="user-bookmark-list" className={`page-list ${styles['page-list']}`}>
+                    {/* TODO: No need page-list-container class ? */}
+                    <div className="page-list-container">
+                      {/* <BookmarkList userId={pageContainer.state.creator._id} /> */}
+                    </div>
                   </div>
                 </div>
-              </div>
-              <div className="grw-user-page-list-m mt-5 d-edit-none">
-                <h2 id="recently-created-list" className="grw-user-page-header border-bottom pb-2 mb-3">
-                  <i id="recent-created-icon" className="mr-1">
-                    {/* <RecentlyCreatedIcon /> */}
-                  </i>
-                  Recently Created
-                </h2>
-                <div id="user-created-list" className={`page-list ${styles['page-list']}`}>
-                  {/* TODO: No need page-list-container class ? */}
-                  <div className="page-list-container">
-                    {/* <RecentCreated userId={pageContainer.state.creator._id} /> */}
+                <div className="grw-user-page-list-m mt-5 d-edit-none">
+                  <h2 id="recently-created-list" className="grw-user-page-header border-bottom pb-2 mb-3">
+                    <i id="recent-created-icon" className="mr-1">
+                      {/* <RecentlyCreatedIcon /> */}
+                    </i>
+                    Recently Created
+                  </h2>
+                  <div id="user-created-list" className={`page-list ${styles['page-list']}`}>
+                    {/* TODO: No need page-list-container class ? */}
+                    <div className="page-list-container">
+                      {/* <RecentCreated userId={pageContainer.state.creator._id} /> */}
+                    </div>
                   </div>
                 </div>
               </div>
-            </div>
-          )}
-          <PageContentFooter />
-        </footer>
-
-        <UnsavedAlertDialog />
-        <DescendantsPageListModal />
-        {shouldRenderPutbackPageModal && <PutbackPageModal />}
+            )}
+            <PageContentFooter />
+          </footer>
 
+          <UnsavedAlertDialog />
+          <DescendantsPageListModal />
+          {shouldRenderPutbackPageModal && <PutbackPageModal />}
+        </div>
       </BasicLayout>
     </>
   );

+ 4 - 4
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -38,7 +38,7 @@ import {
 } from '~/stores/context';
 
 import {
-  CommonProps, getServerSideCommonProps, useCustomTitle, getNextI18NextConfig,
+  CommonProps, getServerSideCommonProps, getNextI18NextConfig,
 } from '../utils/commons';
 
 
@@ -132,7 +132,7 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
       component: <DataImportPageContents />,
     },
     export: {
-      title: t('Export Archive Data'),
+      title: t('export_archive_data'),
       component: <ExportArchiveDataPage />,
     },
     notification: {
@@ -170,7 +170,7 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
       },
     },
     search: {
-      title: t('full_text_search_management'),
+      title: t('full_text_search_management.full_text_search_management'),
       component: <ElasticsearchManagement />,
     },
     'audit-log': {
@@ -324,7 +324,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   }
 
   injectServerConfigurations(context, props);
-  injectNextI18NextConfigurations(context, props, ['admin']);
+  await injectNextI18NextConfigurations(context, props, ['admin']);
 
   return {
     props,

+ 2 - 2
packages/app/src/server/views/admin/customize.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Customize')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('admin:customize')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('Customize') }}</h1>
+<h1 class="title">{{ t('admin:customize') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 2 - 2
packages/app/src/server/views/admin/export.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Export Archive Data')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('export_archive_data')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('Export Archive Data') }}</h1>
+<h1 class="title">{{ t('export_archive_data') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 2 - 2
packages/app/src/server/views/admin/importer.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Import Data')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('ipmport_data')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('Import Data') }}</h1>
+<h1 class="title">{{ t('ipmport_data') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 2 - 2
packages/app/src/server/views/admin/search.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('full_text_search_management')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('full_text_search_management.full_text_search_management')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('full_text_search_management') }}</h1>
+<h1 class="title">{{ t('full_text_search_management.full_text_search_management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 0 - 28
packages/app/src/styles/_editor-navbar.scss

@@ -1,28 +0,0 @@
-.editor-container {
-  .navbar-editor {
-    height: 30px;
-    padding: 0;
-
-    border-bottom: 1px solid transparent;
-
-    li {
-      display: inline-block;
-      i {
-        font-size: 16px;
-      }
-    }
-
-    button {
-      padding: 0px;
-      margin: 0 2px;
-      font-size: 1rem;
-      line-height: 1;
-      background-color: transparent;
-      border: none;
-    }
-
-    img {
-      vertical-align: bottom;
-    }
-  }
-}

+ 0 - 72
packages/app/src/styles/_editor-overlay.scss

@@ -1,72 +0,0 @@
-@mixin overlay-processing-style($additionalSelector, $contentFontSize: inherit, $contentPadding: inherit) {
-  .overlay.#{$additionalSelector} {
-    background: rgba(255, 255, 255, 0.5);
-    .overlay-content {
-      padding: $contentPadding;
-      font-size: $contentFontSize;
-      color: $gray-700;
-      background: rgba(200, 200, 200, 0.5);
-    }
-  }
-}
-
-// overlay in .editor-container
-.editor-container {
-  .overlay {
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    z-index: 7; // forward than .CodeMirror-vscrollbar
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  }
-
-  // loading keymap
-  @include overlay-processing-style(overlay-loading-keymap, 2.5em, 0.3em);
-
-  // cheat sheat
-  .overlay.overlay-gfm-cheatsheet {
-    align-items: flex-end;
-    justify-content: flex-end;
-
-    pointer-events: none;
-
-    .card.gfm-cheatsheet {
-      box-shadow: unset;
-      opacity: 0.6;
-      .card-body {
-        min-width: 30em;
-        padding-bottom: 0;
-        font-family: monospace;
-        color: $text-muted;
-      }
-      ul > li {
-        list-style: none;
-      }
-    }
-
-    .gfm-cheatsheet-modal-link {
-      color: $text-muted;
-      pointer-events: all;
-      cursor: pointer;
-      background-color: transparent;
-      border: none;
-
-      opacity: 0.6;
-
-      &:hover,
-      &:focus {
-        opacity: 1;
-      }
-    }
-  }
-}
-
-.modal-gfm-cheatsheet .modal-body {
-  .hljs {
-    font-family: $font-family-monospace;
-  }
-}

+ 20 - 0
packages/app/src/styles/_mixins.scss

@@ -199,3 +199,23 @@
     }
   }
 }
+
+@mixin overlay-processing-style($additionalSelector, $contentFontSize: inherit, $contentPadding: inherit) {
+  .overlay.#{$additionalSelector} {
+    background: rgba(255, 255, 255, 0.5);
+    .overlay-content {
+      padding: $contentPadding;
+      font-size: $contentFontSize;
+      color: bs.$gray-700;
+      background: rgba(200, 200, 200, 0.5);
+    }
+  }
+}
+
+@mixin insertSimpleLineIcons($code) {
+  &:before {
+    margin-right: 0.2em;
+    font-family: 'simple-line-icons';
+    content: $code;
+  }
+}

+ 2 - 48
packages/app/src/styles/_on-edit.scss

@@ -2,9 +2,8 @@
 @import './variables' ;
 @import './mixins' ;
 @import 'sidebar-wiki';
-@import 'editor-overlay';
-
 
+// global imported
 body.on-edit {
   overflow-y: hidden !important;
 
@@ -306,6 +305,7 @@ body.on-edit {
   }
 }
 
+// TODO: Never used this id class
 #tag-edit-button-tooltip {
   .tooltip-inner {
     color: black;
@@ -317,49 +317,3 @@ body.on-edit {
     border-bottom: 5px solid $gray-300;
   }
 }
-
-/*
- Grid Edit Modal
-*/
-
-.grw-grid-edit-modal {
-  .desktop-preview,
-  .tablet-preview,
-  .mobile-preview {
-    .row {
-      height: 140px;
-      margin: 0px;
-    }
-  }
-  .desktop-preview {
-    .row {
-      div {
-        padding: 0px;
-      }
-    }
-  }
-
-  .tablet-preview {
-    .row {
-      div {
-        padding: 0px;
-      }
-    }
-  }
-
-  .mobile-preview {
-    width: 75%;
-    .row {
-      div {
-        padding: 0px;
-      }
-    }
-  }
-
-  .grid-division-menu {
-    width: 60vw;
-    @include media-breakpoint-down(lg) {
-      width: 80vw;
-    }
-  }
-}