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

Merge pull request #9992 from weseek/imprv/153742-security-settings-pagelist-content-improvement

imprv: Security settings search results redesign
mergify[bot] 10 месяцев назад
Родитель
Сommit
0d57a9d64d

+ 2 - 2
apps/app/playwright/40-admin/access-to-admin-page.spec.ts

@@ -21,8 +21,8 @@ test('admin/security is successfully loaded', async({ page }) => {
   await page.goto('/admin/security');
 
   await expect(page.getByTestId('admin-security')).toBeVisible();
-  await expect(page.locator('#isShowRestrictedByOwner')).not.toBeChecked();
-  await expect(page.locator('#isShowRestrictedByGroup')).not.toBeChecked();
+  await expect(page.locator('#isShowRestrictedByOwner')).toHaveText('Always displayed');
+  await expect(page.locator('#isShowRestrictedByGroup')).toHaveText('Always displayed');
 });
 
 test('admin/markdown is successfully loaded', async({ page }) => {

+ 0 - 1
apps/app/public/static/locales/en_US/admin.json

@@ -19,7 +19,6 @@
     "readonly_users_access": "Read only users' access",
     "always_hidden": "Always hidden",
     "always_displayed": "Always displayed",
-    "displayed_or_hidden": "Hidden / Displayed",
     "Fixed by env var": "This is fixed by the env var <code>{{key}}={{value}}</code>.",
     "register_limitation": "Register limitation",
     "register_limitation_desc": "Restriction of new users' registration",

+ 0 - 1
apps/app/public/static/locales/fr_FR/admin.json

@@ -19,7 +19,6 @@
     "readonly_users_access": "Accès des utilisateurs lecture seule",
     "always_hidden": "Toujours caché",
     "always_displayed": "Toujours affiché",
-    "displayed_or_hidden": "Caché / Affiché",
     "Fixed by env var": "Configuré par la variable d'environnement <code>{{key}}={{value}}</code>.",
     "register_limitation": "Paramètres d'inscription",
     "register_limitation_desc": "Restreindre l'inscription de nouveaux utilisateurs",

+ 20 - 21
apps/app/public/static/locales/ja_JP/admin.json

@@ -13,22 +13,21 @@
   "Execute": "実行",
   "last_login": "最終ログイン",
   "wiki_management_homepage": "Wiki管理トップ",
-  "public": "公開",
-  "anyone_with_the_link": "リンクを知っている人のみ",
+  "public": "公開」のページ",
+  "anyone_with_the_link": "リンクを知っている人のみ」のページ",
   "specified_users": "特定ユーザーのみ",
-  "only_me": "自分のみ",
-  "only_inside_the_group": "特定グループのみ",
+  "only_me": "自分のみ」のページ",
+  "only_inside_the_group": "特定グループのみ」のページ",
   "optional": "オプション",
   "days": "日",
   "security_settings": {
     "security_settings": "セキュリティ設定",
     "scope_of_page_disclosure": "ページの公開範囲",
     "set_point": "設定値",
-    "Guest Users Access":"ゲストユーザーのアクセス",
+    "Guest Users Access": "ゲストユーザーのアクセス",
     "readonly_users_access": "閲覧のみユーザーのアクセス",
-    "always_hidden": "非表示 (固定)",
-    "always_displayed": "表示 (固定)",
-    "displayed_or_hidden": "非表示 / 表示",
+    "always_hidden": "表示しない",
+    "always_displayed": "表示する",
     "Fixed by env var": "環境変数 <code>{{forcewikimode}}={{wikimode}}</code> により固定されています。",
     "register_limitation": "登録の制限",
     "register_limitation_desc": "新しいユーザーを登録する方法を制限します。",
@@ -73,7 +72,7 @@
     "forced_update_desc": "設定が強制変更されました。前回の設定: ",
     "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。 <br><br> 管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能",
     "Authentication mechanism settings": "認証機構設定",
-    "setup_is_not_yet_complete":"セットアップはまだ完了してません",
+    "setup_is_not_yet_complete": "セットアップはまだ完了してません",
     "xss_prevent_setting": "XSS(Cross Site Scripting)対策設定",
     "xss_prevent_setting_link": "マークダウン設定ページに移動",
     "callback_URL": "コールバックURL",
@@ -107,9 +106,9 @@
       "closed": "非公開 (登録には管理者による招待が必要)"
     },
     "share_link_management": "共有リンク管理",
-    "No_share_links":"共有リンクが存在しません",
-    "share_link_notice":"共有リンクを全て削除します",
-    "delete_all_share_links":"全ての共有リンクを削除します",
+    "No_share_links": "共有リンクが存在しません",
+    "share_link_notice": "共有リンクを全て削除します",
+    "delete_all_share_links": "全ての共有リンクを削除します",
     "share_link_rights": "シェアリンクの権限",
     "enable_link_sharing": "リンクのシェアを許可",
     "all_share_links": "全てのシェアリンク",
@@ -512,13 +511,13 @@
       "show_page_side_authors": "作成者・更新者を目次上部に常時表示する",
       "show_page_side_authors_desc": "ページサイドバーの目次上部に作成者と最終更新者の情報を表示します。"
     },
-    "presentation":"プレゼンテーション",
-    "presentation_options":{
+    "presentation": "プレゼンテーション",
+    "presentation_options": {
       "enable_marp": "Marp を有効化する",
       "enable_marp_desc": "プレゼンテーション表示に Marp を利用できるようになります。ただし、XSS に対して脆弱になる恐れがあります。",
       "marp_official_site": "参考:Marp 公式サイト",
       "marp_official_site_link": "https://marp.app",
-      "marp_in_growi" : "参考:GROWI Docs - Marp でスライドを作成する",
+      "marp_in_growi": "参考:GROWI Docs - Marp でスライドを作成する",
       "marp_in_growi_link": "https://docs.growi.org/ja/guide/features/marp.html"
     },
     "custom_title": "カスタム Title",
@@ -532,7 +531,7 @@
     "write_css": " システム全体に適用されるCSSを記述できます。",
     "ctrl_space": "Ctrl+Space でコード補完",
     "custom_script": "カスタムスクリプト",
-    "custom_presentation":"プレゼンテーション",
+    "custom_presentation": "プレゼンテーション",
     "write_java": "システム全体に適用されるJavaScriptを記述できます。",
     "reflect_change": "変更の反映はページの更新が必要です。",
     "custom_logo": "カスタムロゴ",
@@ -541,7 +540,7 @@
     "current_logo": "現在のロゴ",
     "upload_new_logo": "新しいロゴをアップロードする",
     "delete_logo": "ロゴを削除"
-   },
+  },
   "importer_management": {
     "import_data": "データインポート",
     "article": "記事",
@@ -681,7 +680,7 @@
     "delete": "削除",
     "integration_procedure": "連携手順",
     "custom_bot_without_proxy_settings": "Custom Bot without proxy 設定",
-    "integration_failed":"連携に失敗しました",
+    "integration_failed": "連携に失敗しました",
     "reset": "リセット",
     "reset_all_settings": "全ての設定をリセット",
     "delete_slackbot_settings": "Slack Bot 設定を削除する",
@@ -728,7 +727,7 @@
       "allow_specified_long": "特定のチャンネルを許可 (テキストボックスに入力されたチャンネルのみ許可されます)",
       "test_connection": "連携状況のテストをする",
       "test_connection_by_pressing_button": "以下のテストボタンを押して、Slack連携が完了しているかの確認をしましょう",
-      "test_connection_only_public_channel":"連携テストは public チャンネルで確認してください",
+      "test_connection_only_public_channel": "連携テストは public チャンネルで確認してください",
       "error_check_logs_below": "エラーが発生しました。下記のログを確認してください。",
       "send_message_to_slack_work_space": "Slack ワークスペースに送信しました",
       "add_slack_workspace": "Slackワークスペースを追加"
@@ -752,7 +751,7 @@
     }
   },
   "slack_integration_legacy": {
-    "slack_integration_legacy":  "Slack連携 (レガシー)",
+    "slack_integration_legacy": "Slack連携 (レガシー)",
     "alert_disabled": "<a href='/admin/slack-integration'>新しい設定</a>が有効になっているため、この 'Slack連携 (レガシー)' は現在無効になっています。",
     "alert_deplicated": "この 'Slack連携 (レガシー)' は将来廃止されます。代わりに<a href='/admin/slack-integration'>新しいSlack連携機能</a>を利用してください。"
   },
@@ -989,7 +988,7 @@
     "ADMIN_SITE_URL_UPDATE": "サイトURL設定の更新",
     "ADMIN_MAIL_SMTP_UPDATE": "メール設定(SMTP)の更新",
     "ADMIN_MAIL_SES_UPDATE": "メール設定(SES)の更新",
-    "ADMIN_MAIL_TEST_SUBMIT" : "テストメールの送信",
+    "ADMIN_MAIL_TEST_SUBMIT": "テストメールの送信",
     "ADMIN_FILE_UPLOAD_CONFIG_UPDATE": "ファイルアップロード設定の更新",
     "ADMIN_PLUGIN_UPDATE": "プラグイン設定の更新",
     "ADMIN_MAINTENANCEMODE_ENABLED": "メンテナンスモードの開始",

+ 0 - 1
apps/app/public/static/locales/zh_CN/admin.json

@@ -26,7 +26,6 @@
     "set_point": "设定值",
     "always_displayed": "始终显示",
     "always_hidden": "总是隐藏",
-    "displayed_or_hidden": "隐藏 / 显示",
     "Guest Users Access": "来宾用户访问",
     "readonly_users_access": "只浏览用户的访问",
 		"Fixed by env var": "这是由env var<code>%s=%s</code>修复的。",

+ 94 - 52
apps/app/src/client/components/Admin/Security/SecuritySetting.jsx

@@ -297,7 +297,7 @@ class SecuritySetting extends React.Component {
                     onClick={() => this.setExpantOtherDeleteOptionsState(deletionType, !expantDeleteOptionsState)}
                   >
                     <span className={`material-symbols-outlined me-1 ${expantDeleteOptionsState ? 'rotate-90' : ''}`}>navigate_next</span>
-                    { t('security_settings.other_options') }
+                    {t('security_settings.other_options')}
                   </button>
                   <Collapse isOpen={expantDeleteOptionsState}>
                     <div className="pb-4">
@@ -308,7 +308,7 @@ class SecuritySetting extends React.Component {
                           <span dangerouslySetInnerHTML={{ __html: t('security_settings.page_delete_rights_caution') }} />
                         </span>
                       </p>
-                      { this.previousPageRecursiveAuthorityState(deletionType) !== null && (
+                      {this.previousPageRecursiveAuthorityState(deletionType) !== null && (
                         <div className="mb-3">
                           <strong>
                             {t('security_settings.forced_update_desc')}
@@ -356,60 +356,102 @@ class SecuritySetting extends React.Component {
           </div>
         )}
 
-        <h4 className="mt-4">{ t('security_settings.page_list_and_search_results') }</h4>
-        <div className="row justify-content-md-center">
-          <table className="table table-bordered col-lg-9 mb-5">
-            <thead>
-              <tr>
-                <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>
-                <td><span className="material-symbols-outlined text-success me-1">check_circle</span>{ t('security_settings.always_displayed') }</td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('anyone_with_the_link') }</th>
-                <td><span className="material-symbols-outlined text-danger me-1">cancel</span>{ t('security_settings.always_hidden') }</td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('only_me') }</th>
-                <td>
-                  <div className="form-check form-switch form-check-success">
-                    <input
-                      type="checkbox"
-                      className="form-check-input"
+        <h4 className="alert-anchor border-bottom mt-4">{t('security_settings.page_list_and_search_results')}</h4>
+        <div className="row mb-4">
+          <div className="col-md-10">
+            <div className="row">
+
+              {/* Left Column: Labels */}
+              <div className="col-5 d-flex flex-column align-items-end p-4">
+                <div className="fw-bold mb-4">{t('public')}</div>
+                <div className="fw-bold mb-4">{t('anyone_with_the_link')}</div>
+                <div className="fw-bold mb-4">{t('only_me')}</div>
+                <div className="fw-bold">{t('only_inside_the_group')}</div>
+              </div>
+
+              {/* Right Column: Content */}
+              <div className="col-7 d-flex flex-column align-items-start pt-4 pb-4">
+                <div className="mb-4 d-flex align-items-center">
+                  <span className="material-symbols-outlined text-success me-1"></span>
+                  {t('security_settings.always_displayed')}
+                </div>
+                <div className="mb-3 d-flex align-items-center">
+                  <span className="material-symbols-outlined text-danger me-1"></span>
+                  {t('security_settings.always_hidden')}
+                </div>
+
+                {/* Owner Restriction Dropdown */}
+                <div className="mb-3">
+                  <div className="dropdown">
+                    <button
+                      className="btn btn-outline-secondary dropdown-toggle text-end col-12 col-md-auto"
+                      type="button"
                       id="isShowRestrictedByOwner"
-                      checked={!adminGeneralSecurityContainer.state.isShowRestrictedByOwner}
-                      onChange={() => { adminGeneralSecurityContainer.switchIsShowRestrictedByOwner() }}
-                    />
-                    <label className="form-label form-check-label" htmlFor="isShowRestrictedByOwner">
-                      {t('security_settings.displayed_or_hidden')}
-                    </label>
+                      data-bs-toggle="dropdown"
+                      aria-haspopup="true"
+                      aria-expanded="true"
+                    >
+                      <span className="float-start">
+                        {adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Displayed' && t('security_settings.always_displayed')}
+                        {adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Hidden' && t('security_settings.always_hidden')}
+                      </span>
+                    </button>
+                    <div className="dropdown-menu" aria-labelledby="isShowRestrictedByOwner">
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeOwnerRestrictionDisplayMode('Displayed') }}
+                      >
+                        {t('security_settings.always_displayed')}
+                      </button>
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeOwnerRestrictionDisplayMode('Hidden') }}
+                      >
+                        {t('security_settings.always_hidden')}
+                      </button>
+                    </div>
                   </div>
-                </td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('only_inside_the_group') }</th>
-                <td>
-                  <div className="form-check form-switch form-check-success">
-                    <input
-                      type="checkbox"
-                      className="form-check-input"
+                </div>
+
+                {/* Group Restriction Dropdown */}
+                <div className="">
+                  <div className="dropdown">
+                    <button
+                      className="btn btn-outline-secondary dropdown-toggle text-end col-12 col-md-auto"
+                      type="button"
                       id="isShowRestrictedByGroup"
-                      checked={!adminGeneralSecurityContainer.state.isShowRestrictedByGroup}
-                      onChange={() => { adminGeneralSecurityContainer.switchIsShowRestrictedByGroup() }}
-                    />
-                    <label className="form-label form-check-label" htmlFor="isShowRestrictedByGroup">
-                      {t('security_settings.displayed_or_hidden')}
-                    </label>
+                      data-bs-toggle="dropdown"
+                      aria-haspopup="true"
+                      aria-expanded="true"
+                    >
+                      <span className="float-start">
+                        {adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Displayed' && t('security_settings.always_displayed')}
+                        {adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Hidden' && t('security_settings.always_hidden')}
+                      </span>
+                    </button>
+                    <div className="dropdown-menu" aria-labelledby="isShowRestrictedByGroup">
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeGroupRestrictionDisplayMode('Displayed') }}
+                      >
+                        {t('security_settings.always_displayed')}
+                      </button>
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeGroupRestrictionDisplayMode('Hidden') }}
+                      >
+                        {t('security_settings.always_hidden')}
+                      </button>
+                    </div>
                   </div>
-                </td>
-              </tr>
-            </tbody>
-          </table>
+                </div>
+              </div>
+            </div>
+          </div>
         </div>
 
         <h4 className="mb-3">{t('security_settings.page_access_rights')}</h4>

+ 23 - 19
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -32,13 +32,14 @@ export default class AdminGeneralSecurityContainer extends Container {
       currentPageRecursiveDeletionAuthority: PageRecursiveDeleteConfigValue.Inherit,
       currentPageCompleteDeletionAuthority: PageSingleDeleteCompConfigValue.AdminOnly,
       currentPageRecursiveCompleteDeletionAuthority: PageRecursiveDeleteCompConfigValue.Inherit,
+      currentGroupRestrictionDisplayMode: 'Hidden',
+      currentOwnerRestrictionDisplayMode: 'Hidden',
       isAllGroupMembershipRequiredForPageCompleteDeletion: true,
       previousPageRecursiveDeletionAuthority: null,
       previousPageRecursiveCompleteDeletionAuthority: null,
       expandOtherOptionsForDeletion: false,
       expandOtherOptionsForCompleteDeletion: false,
       isShowRestrictedByOwner: false,
-      isShowRestrictedByGroup: false,
       isUsersHomepageDeletionEnabled: false,
       isForceDeleteUserHomepageOnUserDeletion: false,
       isRomUserAllowedToComment: false,
@@ -56,6 +57,8 @@ export default class AdminGeneralSecurityContainer extends Container {
       shareLinksActivePage: 1,
     };
 
+    this.changeOwnerRestrictionDisplayMode = this.changeOwnerRestrictionDisplayMode.bind(this);
+    this.changeGroupRestrictionDisplayMode = this.changeGroupRestrictionDisplayMode.bind(this);
     this.changePageDeletionAuthority = this.changePageDeletionAuthority.bind(this);
     this.changePageCompleteDeletionAuthority = this.changePageCompleteDeletionAuthority.bind(this);
     this.changePageRecursiveDeletionAuthority = this.changePageRecursiveDeletionAuthority.bind(this);
@@ -76,8 +79,9 @@ export default class AdminGeneralSecurityContainer extends Container {
       currentPageRecursiveDeletionAuthority: generalSetting.pageRecursiveDeletionAuthority,
       currentPageRecursiveCompleteDeletionAuthority: generalSetting.pageRecursiveCompleteDeletionAuthority,
       isAllGroupMembershipRequiredForPageCompleteDeletion: generalSetting.isAllGroupMembershipRequiredForPageCompleteDeletion,
-      isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
-      isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
+      // Set display to 'Hidden' if hideRestrictedByOwner is anything but false.
+      currentOwnerRestrictionDisplayMode: generalSetting.hideRestrictedByOwner === false ? 'Displayed' : 'Hidden',
+      currentGroupRestrictionDisplayMode: generalSetting.hideRestrictedByGroup === false ? 'Displayed' : 'Hidden',
       isUsersHomepageDeletionEnabled: generalSetting.isUsersHomepageDeletionEnabled,
       isForceDeleteUserHomepageOnUserDeletion: generalSetting.isForceDeleteUserHomepageOnUserDeletion,
       isRomUserAllowedToComment: generalSetting.isRomUserAllowedToComment,
@@ -123,6 +127,20 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ disableLinkSharing });
   }
 
+  /**
+   * Change ownerRestrictionDisplayMode
+   */
+  changeOwnerRestrictionDisplayMode(mode) {
+    this.setState({ currentOwnerRestrictionDisplayMode: mode });
+  }
+
+  /**
+   * Change groupRestrictionDisplayMode
+   */
+  changeGroupRestrictionDisplayMode(mode) {
+    this.setState({ currentGroupRestrictionDisplayMode: mode });
+  }
+
   /**
    * Change restrictGuestMode
    */
@@ -194,20 +212,6 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ expandOtherOptionsForCompleteDeletion: bool });
   }
 
-  /**
-   * Switch showRestrictedByOwner
-   */
-  switchIsShowRestrictedByOwner() {
-    this.setState({ isShowRestrictedByOwner:  !this.state.isShowRestrictedByOwner });
-  }
-
-  /**
-   * Switch showRestrictedByGroup
-   */
-  switchIsShowRestrictedByGroup() {
-    this.setState({ isShowRestrictedByGroup:  !this.state.isShowRestrictedByGroup });
-  }
-
   /**
    * Switch isUsersHomepageDeletionEnabled
    */
@@ -245,8 +249,8 @@ export default class AdminGeneralSecurityContainer extends Container {
       pageRecursiveDeletionAuthority: this.state.currentPageRecursiveDeletionAuthority,
       pageRecursiveCompleteDeletionAuthority: this.state.currentPageRecursiveCompleteDeletionAuthority,
       isAllGroupMembershipRequiredForPageCompleteDeletion: this.state.isAllGroupMembershipRequiredForPageCompleteDeletion,
-      hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
-      hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
+      hideRestrictedByGroup: this.state.currentGroupRestrictionDisplayMode === 'Hidden',
+      hideRestrictedByOwner: this.state.currentOwnerRestrictionDisplayMode === 'Hidden',
       isUsersHomepageDeletionEnabled: this.state.isUsersHomepageDeletionEnabled,
       isForceDeleteUserHomepageOnUserDeletion: this.state.isForceDeleteUserHomepageOnUserDeletion,
       isRomUserAllowedToComment: this.state.isRomUserAllowedToComment,