diff --git a/apps/app/public/static/locales/en_US/admin.json b/apps/app/public/static/locales/en_US/admin.json index b81dcb2b933..b7a2acae111 100644 --- a/apps/app/public/static/locales/en_US/admin.json +++ b/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 {{key}}={{value}}.", "register_limitation": "Register limitation", "register_limitation_desc": "Restriction of new users' registration", diff --git a/apps/app/public/static/locales/fr_FR/admin.json b/apps/app/public/static/locales/fr_FR/admin.json index 121a1f6870a..c04fc568e06 100644 --- a/apps/app/public/static/locales/fr_FR/admin.json +++ b/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 {{key}}={{value}}.", "register_limitation": "Paramètres d'inscription", "register_limitation_desc": "Restreindre l'inscription de nouveaux utilisateurs", diff --git a/apps/app/public/static/locales/ja_JP/admin.json b/apps/app/public/static/locales/ja_JP/admin.json index 892646e74a4..d5d4c45c5fa 100644 --- a/apps/app/public/static/locales/ja_JP/admin.json +++ b/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": "環境変数 {{forcewikimode}}={{wikimode}} により固定されています。", "register_limitation": "登録の制限", "register_limitation_desc": "新しいユーザーを登録する方法を制限します。", @@ -73,7 +72,7 @@ "forced_update_desc": "設定が強制変更されました。前回の設定: ", "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。

管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能", "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": "新しい設定が有効になっているため、この 'Slack連携 (レガシー)' は現在無効になっています。", "alert_deplicated": "この 'Slack連携 (レガシー)' は将来廃止されます。代わりに新しいSlack連携機能を利用してください。" }, @@ -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": "メンテナンスモードの開始", diff --git a/apps/app/public/static/locales/zh_CN/admin.json b/apps/app/public/static/locales/zh_CN/admin.json index efeb3d47684..262b2be4f45 100644 --- a/apps/app/public/static/locales/zh_CN/admin.json +++ b/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%s=%s修复的。", diff --git a/apps/app/src/client/components/Admin/Security/SecuritySetting.jsx b/apps/app/src/client/components/Admin/Security/SecuritySetting.jsx index 5569aff6419..b61f12610bc 100644 --- a/apps/app/src/client/components/Admin/Security/SecuritySetting.jsx +++ b/apps/app/src/client/components/Admin/Security/SecuritySetting.jsx @@ -297,7 +297,7 @@ class SecuritySetting extends React.Component { onClick={() => this.setExpantOtherDeleteOptionsState(deletionType, !expantDeleteOptionsState)} > navigate_next - { t('security_settings.other_options') } + {t('security_settings.other_options')}
@@ -308,7 +308,7 @@ class SecuritySetting extends React.Component {

- { this.previousPageRecursiveAuthorityState(deletionType) !== null && ( + {this.previousPageRecursiveAuthorityState(deletionType) !== null && (
{t('security_settings.forced_update_desc')} @@ -356,60 +356,102 @@ class SecuritySetting extends React.Component {
)} -

{ t('security_settings.page_list_and_search_results') }

-
- - - - - - - - - - - - - - - - - - - - - - - - - -
{ t('security_settings.scope_of_page_disclosure') }{ t('security_settings.set_point') }
{ t('public') }check_circle{ t('security_settings.always_displayed') }
{ t('anyone_with_the_link') }cancel{ t('security_settings.always_hidden') }
{ t('only_me') } -
- { adminGeneralSecurityContainer.switchIsShowRestrictedByOwner() }} - /> - +

{t('security_settings.page_list_and_search_results')}

+
+
+
+ + {/* Left Column: Labels */} +
+
{t('public')}
+
{t('anyone_with_the_link')}
+
{t('only_me')}
+
{t('only_inside_the_group')}
+
+ + {/* Right Column: Content */} +
+
+ + {t('security_settings.always_displayed')} +
+
+ + {t('security_settings.always_hidden')} +
+ + {/* Owner Restriction Dropdown */} +
+
+ +
+ + +
-
{ t('only_inside_the_group') } -
- { adminGeneralSecurityContainer.switchIsShowRestrictedByGroup() }} - /> - +
+ + {/* Group Restriction Dropdown */} +
+
+ +
+ + +
-
+
+
+ +

{t('security_settings.page_access_rights')}

diff --git a/apps/app/src/client/components/PageControls/PageControls.tsx b/apps/app/src/client/components/PageControls/PageControls.tsx index 2920697147c..b6f27e1d290 100644 --- a/apps/app/src/client/components/PageControls/PageControls.tsx +++ b/apps/app/src/client/components/PageControls/PageControls.tsx @@ -8,6 +8,7 @@ import type { import { isIPageInfoForEntity, isIPageInfoForOperation, } from '@growi/core'; +import { pagePathUtils } from '@growi/core/dist/utils'; import { useRect } from '@growi/ui/dist/utils'; import { useTranslation } from 'next-i18next'; import { DropdownItem } from 'reactstrap'; @@ -17,7 +18,9 @@ import { } from '~/client/services/page-operation'; import { toastError } from '~/client/util/toastr'; import OpenDefaultAiAssistantButton from '~/features/openai/client/components/AiAssistant/OpenDefaultAiAssistantButton'; -import { useIsGuestUser, useIsReadOnlyUser, useIsSearchPage } from '~/stores-universal/context'; +import { + useIsGuestUser, useIsReadOnlyUser, useIsSearchPage, useIsUsersHomepageDeletionEnabled, +} from '~/stores-universal/context'; import { EditorMode, useEditorMode, } from '~/stores-universal/ui'; @@ -27,7 +30,7 @@ import { } from '~/stores/ui'; import loggerFactory from '~/utils/logger'; -import { useSWRxPageInfo, useSWRxTagsInfo } from '../../../stores/page'; +import { useSWRxPageInfo, useSWRxTagsInfo, useCurrentPagePath } from '../../../stores/page'; import { useSWRxUsersList } from '../../../stores/user'; import type { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl'; import { @@ -134,6 +137,10 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element = const { data: editorMode } = useEditorMode(); const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd(); const { data: isSearchPage } = useIsSearchPage(); + const { data: isUsersHomepageDeletionEnabled } = useIsUsersHomepageDeletionEnabled(); + const { data: currentPagePath } = useCurrentPagePath(); + + const isUsersHomepage = currentPagePath == null ? false : pagePathUtils.isUsersHomepage(currentPagePath); const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId, shareLinkId); @@ -249,6 +256,22 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element = } }, [expandContentWidth, isGuestUser, isReadOnlyUser, onClickSwitchContentWidth, pageId, pageInfo]); + const isEnableActions = useMemo(() => { + if (isGuestUser) { + return false; + } + + if (currentPagePath == null) { + return false; + } + + if (isUsersHomepage && !isUsersHomepageDeletionEnabled) { + return false; + } + + return true; + }, [isGuestUser, isUsersHomepage, isUsersHomepageDeletionEnabled]); + const additionalMenuItemOnTopRenderer = useMemo(() => { if (!isIPageInfoForEntity(pageInfo)) { return undefined; @@ -332,7 +355,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element = = (props: Props) => { @@ -258,6 +259,9 @@ const Page: NextPageWithLayout = (props: Props) => { useIsAiEnabled(props.aiEnabled); useLimitLearnablePageCountPerAssistant(props.limitLearnablePageCountPerAssistant); + useIsUsersHomepageDeletionEnabled(props.isUsersHomepageDeletionEnabled); + + const { pageWithMeta } = props; const pageId = pageWithMeta?.data._id; @@ -576,7 +580,7 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P props.aiEnabled = configManager.getConfig('app:aiEnabled'); props.limitLearnablePageCountPerAssistant = configManager.getConfig('openai:limitLearnablePageCountPerAssistant'); - + props.isUsersHomepageDeletionEnabled = configManager.getConfig('security:user-homepage-deletion:isEnabled'); props.isSearchServiceConfigured = searchService.isConfigured; props.isSearchServiceReachable = searchService.isReachable; props.isSearchScopeChildrenAsDefault = configManager.getConfig('customize:isSearchScopeChildrenAsDefault'); diff --git a/apps/app/src/stores-universal/context.tsx b/apps/app/src/stores-universal/context.tsx index af9b84b3853..0ac730a153d 100644 --- a/apps/app/src/stores-universal/context.tsx +++ b/apps/app/src/stores-universal/context.tsx @@ -224,8 +224,14 @@ export const useLimitLearnablePageCountPerAssistant = (initialData?: number): SW return useContextSWR('limitLearnablePageCountPerAssistant', initialData); }; + +export const useIsUsersHomepageDeletionEnabled = (initialData?: boolean): SWRResponse => { + return useContextSWR('isUsersHomepageDeletionEnabled', initialData); +}; + export const useIsEnableUnifiedMergeView = (initialData?: boolean): SWRResponse => { return useSWRStatic('isEnableUnifiedMergeView', initialData, { fallbackData: false }); + }; /** **********************************************************