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

Merge branch 'master' into feat/enhanced-access-token

Shun Miyazawa 10 месяцев назад
Родитель
Сommit
cdfcd3b38c
46 измененных файлов с 268 добавлено и 209 удалено
  1. 31 1
      CHANGELOG.md
  2. 1 1
      apps/app/package.json
  3. 2 2
      apps/app/src/client/components/Admin/App/AppSetting.jsx
  4. 4 4
      apps/app/src/client/components/Admin/App/AwsSetting.tsx
  5. 8 8
      apps/app/src/client/components/Admin/App/AzureSetting.tsx
  6. 3 3
      apps/app/src/client/components/Admin/App/GcsSetting.tsx
  7. 1 1
      apps/app/src/client/components/Admin/App/MailSetting.tsx
  8. 3 3
      apps/app/src/client/components/Admin/App/MaskedInput.tsx
  9. 2 2
      apps/app/src/client/components/Admin/App/SesSetting.tsx
  10. 1 1
      apps/app/src/client/components/Admin/App/SiteUrlSetting.tsx
  11. 4 4
      apps/app/src/client/components/Admin/App/SmtpSetting.tsx
  12. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeCssSetting.tsx
  13. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeNoscriptSetting.tsx
  14. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeScriptSetting.tsx
  15. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeTitle.tsx
  16. 2 2
      apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx
  17. 2 2
      apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx
  18. 1 1
      apps/app/src/client/components/Admin/Security/GitHubSecuritySettingContents.jsx
  19. 3 3
      apps/app/src/client/components/Admin/Security/GoogleSecuritySettingContents.jsx
  20. 10 10
      apps/app/src/client/components/Admin/Security/LdapSecuritySettingContents.jsx
  21. 1 1
      apps/app/src/client/components/Admin/Security/LocalSecuritySettingContents.jsx
  22. 16 16
      apps/app/src/client/components/Admin/Security/OidcSecuritySettingContents.jsx
  23. 9 9
      apps/app/src/client/components/Admin/Security/SamlSecuritySettingContents.jsx
  24. 1 1
      apps/app/src/client/components/Admin/Security/SecuritySetting.jsx
  25. 1 1
      apps/app/src/client/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx
  26. 2 2
      apps/app/src/client/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx
  27. 1 1
      apps/app/src/client/components/Admin/SlackIntegration/ManageCommandsProcess.jsx
  28. 0 1
      apps/app/src/client/components/Sidebar/InAppNotification/PrimaryItemForNotification.tsx
  29. 15 4
      apps/app/src/client/components/Sidebar/SidebarContents.tsx
  30. 3 2
      apps/app/src/client/components/Sidebar/SidebarNav/PrimaryItems.tsx
  31. 4 2
      apps/app/src/client/services/page-operation.ts
  32. 11 6
      apps/app/src/features/openai/client/components/AiAssistant/OpenDefaultAiAssistantButton.tsx
  33. 10 11
      apps/app/src/server/routes/apiv3/security-settings/index.js
  34. 2 2
      apps/app/src/server/service/config-manager/config-definition.ts
  35. 1 1
      apps/slackbot-proxy/package.json
  36. 1 2
      biome.json
  37. 1 1
      package.json
  38. 1 2
      packages/remark-drawio/.eslintignore
  39. 0 5
      packages/remark-drawio/.eslintrc.cjs
  40. 1 1
      packages/remark-drawio/package.json
  41. 49 39
      packages/remark-drawio/src/components/DrawioViewer.tsx
  42. 12 9
      packages/remark-drawio/src/interfaces/graph-viewer.ts
  43. 17 17
      packages/remark-drawio/src/services/renderer/remark-drawio.ts
  44. 18 16
      packages/remark-drawio/src/utils/embed.ts
  45. 8 2
      packages/remark-drawio/src/utils/global.ts
  46. 1 3
      packages/remark-drawio/tsconfig.json

+ 31 - 1
CHANGELOG.md

@@ -1,9 +1,39 @@
 # Changelog
 
-## [Unreleased](https://github.com/weseek/growi/compare/v7.2.5...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v7.2.6...HEAD)
 
 *Please do not manually update this file. We've automated the process.*
 
+## [v7.2.6](https://github.com/weseek/growi/compare/v7.2.5...v7.2.6) - 2025-06-10
+
+### 💎 Features
+
+* feat(ai): Display spinner while creating diff (#9991) @miya
+
+### 🚀 Improvement
+
+* imprv: Message card markdown header size (#10038) @miya
+* imprv: Type safe configuration for file uploading (#10032) @yuki-takei
+* imprv: EditorAssistant instruction (#10030) @miya
+* imprv: Add NonEmptyString type (#10031) @yuki-takei
+* imprv: Security settings search results redesign (#9992) @arvid-e
+* imprv: OpenAPI spec properties ref (#10023) @yuki-takei
+* imprv(ai): Make input form position sticky (#10002) @miya
+* imprv: Prevent path traversal attack in pdf converter (#9993) @arafubeatbox
+* imprv: Discard when form is submitted without Accept/Discard after showing diff (#9980) @miya
+
+### 🐛 Bug Fixes
+
+* imprv:  The delete button on the user home page is now hidden for unauthorized users. (#9915) @taikou-m
+* fix: OpenAI threads can be retrieved regardless of assistant's public permissions (#9994) @miya
+* fix: Editor assistant button is being displayed when ai functionality is not enabled (#9985) @miya
+* fix: Improve attribute handling in Lsx (#9989) @yuki-takei
+
+### 🧰 Maintenance
+
+* support: OpenAPI operationId generation (#10009) @yuki-takei
+* support: Configure biome for remark-growi-directive (#9999) @arafubeatbox
+
 ## [v7.2.5](https://github.com/weseek/growi/compare/v7.2.4...v7.2.5) - 2025-05-28
 
 ### 💎 Features

+ 1 - 1
apps/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "7.2.6-RC.0",
+  "version": "7.2.7-RC.0",
   "license": "MIT",
   "private": "true",
   "scripts": {

+ 2 - 2
apps/app/src/client/components/Admin/App/AppSetting.jsx

@@ -40,7 +40,7 @@ const AppSetting = (props) => {
           <input
             className="form-control"
             type="text"
-            defaletValue={adminAppContainer.state.title || ''}
+            value={adminAppContainer.state.title || ''}
             onChange={(e) => {
               adminAppContainer.changeTitle(e.target.value);
             }}
@@ -60,7 +60,7 @@ const AppSetting = (props) => {
           <input
             className="form-control"
             type="text"
-            defaultValue={adminAppContainer.state.confidential || ''}
+            value={adminAppContainer.state.confidential || ''}
             onChange={(e) => {
               adminAppContainer.changeConfidential(e.target.value);
             }}

+ 4 - 4
apps/app/src/client/components/Admin/App/AwsSetting.tsx

@@ -76,7 +76,7 @@ export const AwsSettingMolecule = (props: AwsSettingMoleculeProps): JSX.Element
           <input
             className="form-control"
             placeholder={`${t('eg')} ap-northeast-1`}
-            defaultValue={props.s3Region || ''}
+            value={props.s3Region || ''}
             onChange={(e) => {
               props?.onChangeS3Region(e.target.value);
             }}
@@ -93,7 +93,7 @@ export const AwsSettingMolecule = (props: AwsSettingMoleculeProps): JSX.Element
             className="form-control"
             type="text"
             placeholder={`${t('eg')} http://localhost:9000`}
-            defaultValue={props.s3CustomEndpoint || ''}
+            value={props.s3CustomEndpoint || ''}
             onChange={(e) => {
               props?.onChangeS3CustomEndpoint(e.target.value);
             }}
@@ -111,7 +111,7 @@ export const AwsSettingMolecule = (props: AwsSettingMoleculeProps): JSX.Element
             className="form-control"
             type="text"
             placeholder={`${t('eg')} crowi`}
-            defaultValue={props.s3Bucket || ''}
+            value={props.s3Bucket || ''}
             onChange={(e) => {
               props.onChangeS3Bucket(e.target.value);
             }}
@@ -127,7 +127,7 @@ export const AwsSettingMolecule = (props: AwsSettingMoleculeProps): JSX.Element
           <input
             className="form-control"
             type="text"
-            defaultValue={props.s3AccessKeyId || ''}
+            value={props.s3AccessKeyId || ''}
             onChange={(e) => {
               props?.onChangeS3AccessKeyId(e.target.value);
             }}

+ 8 - 8
apps/app/src/client/components/Admin/App/AzureSetting.tsx

@@ -118,12 +118,12 @@ export const AzureSettingMolecule = (props: AzureSettingMoleculeProps): JSX.Elem
               <MaskedInput
                 name="azureTenantId"
                 readOnly={azureUseOnlyEnvVars}
-                defaultValue={azureTenantId}
+                value={azureTenantId}
                 onChange={e => props?.onChangeAzureTenantId(e.target.value)}
               />
             </td>
             <td>
-              <MaskedInput name="envAzureTenantId" defaultValue={envAzureTenantId || ''} readOnly tabIndex={-1} />
+              <MaskedInput name="envAzureTenantId" value={envAzureTenantId || ''} readOnly tabIndex={-1} />
               <p className="form-text text-muted">
                 {/* eslint-disable-next-line react/no-danger */}
                 <small dangerouslySetInnerHTML={{ __html: t('admin:app_setting.use_env_var_if_empty', { variable: 'AZURE_TENANT_ID' }) }} />
@@ -136,12 +136,12 @@ export const AzureSettingMolecule = (props: AzureSettingMoleculeProps): JSX.Elem
               <MaskedInput
                 name="azureClientId"
                 readOnly={azureUseOnlyEnvVars}
-                defaultValue={azureClientId}
+                value={azureClientId}
                 onChange={e => props?.onChangeAzureClientId(e.target.value)}
               />
             </td>
             <td>
-              <MaskedInput name="envAzureClientId" defaultValue={envAzureClientId || ''} readOnly tabIndex={-1} />
+              <MaskedInput name="envAzureClientId" value={envAzureClientId || ''} readOnly tabIndex={-1} />
               <p className="form-text text-muted">
                 {/* eslint-disable-next-line react/no-danger */}
                 <small dangerouslySetInnerHTML={{ __html: t('admin:app_setting.use_env_var_if_empty', { variable: 'AZURE_CLIENT_ID' }) }} />
@@ -154,12 +154,12 @@ export const AzureSettingMolecule = (props: AzureSettingMoleculeProps): JSX.Elem
               <MaskedInput
                 name="azureClientSecret"
                 readOnly={azureUseOnlyEnvVars}
-                defaultValue={azureClientSecret}
+                value={azureClientSecret}
                 onChange={e => props?.onChangeAzureClientSecret(e.target.value)}
               />
             </td>
             <td>
-              <MaskedInput name="envAzureClientSecret" defaultValue={envAzureClientSecret || ''} readOnly tabIndex={-1} />
+              <MaskedInput name="envAzureClientSecret" value={envAzureClientSecret || ''} readOnly tabIndex={-1} />
               <p className="form-text text-muted">
                 {/* eslint-disable-next-line react/no-danger */}
                 <small dangerouslySetInnerHTML={{ __html: t('admin:app_setting.use_env_var_if_empty', { variable: 'AZURE_CLIENT_SECRET' }) }} />
@@ -174,7 +174,7 @@ export const AzureSettingMolecule = (props: AzureSettingMoleculeProps): JSX.Elem
                 type="text"
                 name="azureStorageAccountName"
                 readOnly={azureUseOnlyEnvVars}
-                defaultValue={azureStorageAccountName}
+                value={azureStorageAccountName}
                 onChange={e => props?.onChangeAzureStorageAccountName(e.target.value)}
               />
             </td>
@@ -194,7 +194,7 @@ export const AzureSettingMolecule = (props: AzureSettingMoleculeProps): JSX.Elem
                 type="text"
                 name="azureStorageContainerName"
                 readOnly={azureUseOnlyEnvVars}
-                defaultValue={azureStorageContainerName}
+                value={azureStorageContainerName}
                 onChange={e => props?.onChangeAzureStorageContainerName(e.target.value)}
               />
             </td>

+ 3 - 3
apps/app/src/client/components/Admin/App/GcsSetting.tsx

@@ -108,7 +108,7 @@ export const GcsSettingMolecule = (props: GcsSettingMoleculeProps): JSX.Element
                 type="text"
                 name="gcsApiKeyJsonPath"
                 readOnly={gcsUseOnlyEnvVars}
-                defaultValue={gcsApiKeyJsonPath}
+                value={gcsApiKeyJsonPath}
                 onChange={e => props?.onChangeGcsApiKeyJsonPath(e.target.value)}
               />
             </td>
@@ -128,7 +128,7 @@ export const GcsSettingMolecule = (props: GcsSettingMoleculeProps): JSX.Element
                 type="text"
                 name="gcsBucket"
                 readOnly={gcsUseOnlyEnvVars}
-                defaultValue={gcsBucket}
+                value={gcsBucket}
                 onChange={e => props?.onChangeGcsBucket(e.target.value)}
               />
             </td>
@@ -148,7 +148,7 @@ export const GcsSettingMolecule = (props: GcsSettingMoleculeProps): JSX.Element
                 type="text"
                 name="gcsUploadNamespace"
                 readOnly={gcsUseOnlyEnvVars}
-                defaultValue={gcsUploadNamespace}
+                value={gcsUploadNamespace}
                 onChange={e => props?.onChangeGcsUploadNamespace(e.target.value)}
               />
             </td>

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

@@ -56,7 +56,7 @@ const MailSetting = (props: Props) => {
             className="form-control"
             type="text"
             placeholder={`${t('eg')} mail@growi.org`}
-            defaultValue={adminAppContainer.state.fromAddress || ''}
+            value={adminAppContainer.state.fromAddress || ''}
             onChange={(e) => { adminAppContainer.changeFromAddress(e.target.value) }}
           />
         </div>

+ 3 - 3
apps/app/src/client/components/Admin/App/MaskedInput.tsx

@@ -5,7 +5,7 @@ import styles from './MaskedInput.module.scss';
 type Props = {
   name: string
   readOnly: boolean
-  defaultValue: string
+  value: string
   onChange?: (e: any) => void
   tabIndex?: number | undefined
 };
@@ -17,7 +17,7 @@ export default function MaskedInput(props: Props): JSX.Element {
   };
 
   const {
-    name, readOnly, defaultValue, onChange, tabIndex,
+    name, readOnly, value, onChange, tabIndex,
   } = props;
 
   return (
@@ -27,7 +27,7 @@ export default function MaskedInput(props: Props): JSX.Element {
         type={passwordShown ? 'text' : 'password'}
         name={name}
         readOnly={readOnly}
-        defaultValue={defaultValue}
+        value={value}
         onChange={onChange}
         tabIndex={tabIndex}
       />

+ 2 - 2
apps/app/src/client/components/Admin/App/SesSetting.tsx

@@ -24,7 +24,7 @@ const SmtpSetting = (props: Props) => {
             <input
               className="form-control"
               type="text"
-              defaultValue={adminAppContainer.state.sesAccessKeyId || ''}
+              value={adminAppContainer.state.sesAccessKeyId || ''}
               onChange={(e) => {
                 adminAppContainer.changeSesAccessKeyId(e.target.value);
               }}
@@ -40,7 +40,7 @@ const SmtpSetting = (props: Props) => {
             <input
               className="form-control"
               type="text"
-              defaultValue={adminAppContainer.state.sesSecretAccessKey || ''}
+              value={adminAppContainer.state.sesSecretAccessKey || ''}
               onChange={(e) => {
                 adminAppContainer.changeSesSecretAccessKey(e.target.value);
               }}

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

@@ -70,7 +70,7 @@ const SiteUrlSetting = (props: Props) => {
                   className="form-control"
                   type="text"
                   name="settingForm[app:siteUrl]"
-                  defaultValue={adminAppContainer.state.siteUrl || ''}
+                  value={adminAppContainer.state.siteUrl || ''}
                   disabled={adminAppContainer.state.siteUrlUseOnlyEnvVars ?? true}
                   onChange={(e) => { adminAppContainer.changeSiteUrl(e.target.value) }}
                   placeholder="e.g. https://my.growi.org"

+ 4 - 4
apps/app/src/client/components/Admin/App/SmtpSetting.tsx

@@ -27,7 +27,7 @@ const SmtpSetting = (props: Props) => {
             <input
               className="form-control"
               type="text"
-              defaultValue={adminAppContainer.state.smtpHost || ''}
+              value={adminAppContainer.state.smtpHost || ''}
               onChange={(e) => { adminAppContainer.changeSmtpHost(e.target.value) }}
             />
           </div>
@@ -40,7 +40,7 @@ const SmtpSetting = (props: Props) => {
           <div className="col-md-6">
             <input
               className="form-control"
-              defaultValue={adminAppContainer.state.smtpPort || ''}
+              value={adminAppContainer.state.smtpPort || ''}
               onChange={(e) => { adminAppContainer.changeSmtpPort(e.target.value) }}
             />
           </div>
@@ -54,7 +54,7 @@ const SmtpSetting = (props: Props) => {
             <input
               className="form-control"
               type="text"
-              defaultValue={adminAppContainer.state.smtpUser || ''}
+              value={adminAppContainer.state.smtpUser || ''}
               onChange={(e) => { adminAppContainer.changeSmtpUser(e.target.value) }}
             />
           </div>
@@ -68,7 +68,7 @@ const SmtpSetting = (props: Props) => {
             <input
               className="form-control"
               type="password"
-              defaultValue={adminAppContainer.state.smtpPassword || ''}
+              value={adminAppContainer.state.smtpPassword || ''}
               onChange={(e) => { adminAppContainer.changeSmtpPassword(e.target.value) }}
             />
           </div>

+ 1 - 1
apps/app/src/client/components/Admin/Customize/CustomizeCssSetting.tsx

@@ -46,7 +46,7 @@ const CustomizeCssSetting = (props: Props): JSX.Element => {
               className="form-control"
               name="customizeCss"
               rows={8}
-              defaultValue={adminCustomizeContainer.state.currentCustomizeCss || ''}
+              value={adminCustomizeContainer.state.currentCustomizeCss || ''}
               onChange={(e) => { adminCustomizeContainer.changeCustomizeCss(e.target.value) }}
             />
           </div>

+ 1 - 1
apps/app/src/client/components/Admin/Customize/CustomizeNoscriptSetting.tsx

@@ -50,7 +50,7 @@ const CustomizeNoscriptSetting = (props: Props): JSX.Element => {
               className="form-control mb-2"
               name="customizeNoscript"
               rows={8}
-              defaultValue={adminCustomizeContainer.state.currentCustomizeNoscript || ''}
+              value={adminCustomizeContainer.state.currentCustomizeNoscript || ''}
               onChange={(e) => { adminCustomizeContainer.changeCustomizeNoscript(e.target.value) }}
             />
           </div>

+ 1 - 1
apps/app/src/client/components/Admin/Customize/CustomizeScriptSetting.tsx

@@ -47,7 +47,7 @@ const CustomizeScriptSetting = (props: Props): JSX.Element => {
               className="form-control mb-2"
               name="customizeScript"
               rows={8}
-              defaultValue={adminCustomizeContainer.state.currentCustomizeScript || ''}
+              value={adminCustomizeContainer.state.currentCustomizeScript || ''}
               onChange={(e) => { adminCustomizeContainer.changeCustomizeScript(e.target.value) }}
             />
           </div>

+ 1 - 1
apps/app/src/client/components/Admin/Customize/CustomizeTitle.tsx

@@ -67,7 +67,7 @@ export const CustomizeTitle: FC = () => {
         <div className="col-12">
           <input
             className="form-control"
-            defaultValue={currentCustomizeTitle}
+            value={currentCustomizeTitle}
             onChange={(e) => { setCrrentCustomizeTitle(e.target.value) }}
           />
         </div>

+ 2 - 2
apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx

@@ -70,7 +70,7 @@ class SlackConfiguration extends React.Component {
                 <input
                   className="form-control"
                   type="text"
-                  defaultValue={adminSlackIntegrationLegacyContainer.state.webhookUrl || ''}
+                  value={adminSlackIntegrationLegacyContainer.state.webhookUrl || ''}
                   onChange={e => adminSlackIntegrationLegacyContainer.changeWebhookUrl(e.target.value)}
                 />
               </div>
@@ -122,7 +122,7 @@ class SlackConfiguration extends React.Component {
                   <input
                     className="form-control"
                     type="text"
-                    defaultValue={adminSlackIntegrationLegacyContainer.state.slackToken || ''}
+                    value={adminSlackIntegrationLegacyContainer.state.slackToken || ''}
                     onChange={e => adminSlackIntegrationLegacyContainer.changeSlackToken(e.target.value)}
                   />
                 </div>

+ 2 - 2
apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx

@@ -52,7 +52,7 @@ export const WhitelistInput = (props: Props): JSX.Element => {
           name="recommendedTags"
           rows={6}
           cols={40}
-          defaultValue={adminMarkDownContainer.state.tagWhitelist}
+          value={adminMarkDownContainer.state.tagWhitelist}
           onChange={(e) => { adminMarkDownContainer.setState({ tagWhitelist: e.target.value }) }}
         />
       </div>
@@ -69,7 +69,7 @@ export const WhitelistInput = (props: Props): JSX.Element => {
           name="recommendedAttrs"
           rows={6}
           cols={40}
-          defaultValue={adminMarkDownContainer.state.attrWhitelist}
+          value={adminMarkDownContainer.state.attrWhitelist}
           onChange={(e) => { adminMarkDownContainer.setState({ attrWhitelist: e.target.value }) }}
         />
       </div>

+ 1 - 1
apps/app/src/client/components/Admin/Security/GitHubSecuritySettingContents.jsx

@@ -125,7 +125,7 @@ class GitHubSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="githubClientSecret"
-                  defaultValue={adminGitHubSecurityContainer.state.githubClientSecret || ''}
+                  value={adminGitHubSecurityContainer.state.githubClientSecret || ''}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientSecret(e.target.value)}
                 />
                 <p className="form-text text-muted">

+ 3 - 3
apps/app/src/client/components/Admin/Security/GoogleSecuritySettingContents.jsx

@@ -108,7 +108,7 @@ class GoogleSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="googleClientId"
-                  defaultValue={adminGoogleSecurityContainer.state.googleClientId || ''}
+                  value={adminGoogleSecurityContainer.state.googleClientId || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientId(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -122,9 +122,9 @@ class GoogleSecurityManagementContents extends React.Component {
               <div className="col-6">
                 <input
                   className="form-control"
-                  type="text"
+                  type="password"
                   name="googleClientSecret"
-                  defaultValue={adminGoogleSecurityContainer.state.googleClientSecret || ''}
+                  value={adminGoogleSecurityContainer.state.googleClientSecret || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientSecret(e.target.value)}
                 />
                 <p className="form-text text-muted">

+ 10 - 10
apps/app/src/client/components/Admin/Security/LdapSecuritySettingContents.jsx

@@ -92,7 +92,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="serverUrl"
-                  defaultValue={adminLdapSecurityContainer.state.serverUrl || ''}
+                  value={adminLdapSecurityContainer.state.serverUrl || ''}
                   onChange={e => adminLdapSecurityContainer.changeServerUrl(e.target.value)}
                 />
                 <small>
@@ -145,7 +145,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="bindDN"
-                  defaultValue={adminLdapSecurityContainer.state.ldapBindDN || ''}
+                  value={adminLdapSecurityContainer.state.ldapBindDN || ''}
                   onChange={e => adminLdapSecurityContainer.changeBindDN(e.target.value)}
                 />
                 {(adminLdapSecurityContainer.state.isUserBind === true) ? (
@@ -194,7 +194,7 @@ class LdapSecuritySettingContents extends React.Component {
                         className="form-control passport-ldap-managerbind"
                         type="password"
                         name="bindDNPassword"
-                        defaultValue={adminLdapSecurityContainer.state.ldapBindDNPassword || ''}
+                        value={adminLdapSecurityContainer.state.ldapBindDNPassword || ''}
                         onChange={e => adminLdapSecurityContainer.changeBindDNPassword(e.target.value)}
                       />
                     </>
@@ -211,7 +211,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="searchFilter"
-                  defaultValue={adminLdapSecurityContainer.state.ldapSearchFilter || ''}
+                  value={adminLdapSecurityContainer.state.ldapSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeSearchFilter(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -248,7 +248,7 @@ class LdapSecuritySettingContents extends React.Component {
                   type="text"
                   placeholder="Default: uid"
                   name="attrMapUsername"
-                  defaultValue={adminLdapSecurityContainer.state.ldapAttrMapUsername || ''}
+                  value={adminLdapSecurityContainer.state.ldapAttrMapUsername || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapUsername(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -292,7 +292,7 @@ class LdapSecuritySettingContents extends React.Component {
                   type="text"
                   placeholder="Default: mail"
                   name="attrMapMail"
-                  defaultValue={adminLdapSecurityContainer.state.ldapAttrMapMail || ''}
+                  value={adminLdapSecurityContainer.state.ldapAttrMapMail || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapMail(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -312,7 +312,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="attrMapName"
-                  defaultValue={adminLdapSecurityContainer.state.ldapAttrMapName || ''}
+                  value={adminLdapSecurityContainer.state.ldapAttrMapName || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapName(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -337,7 +337,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="groupSearchBase"
-                  defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchBase || ''}
+                  value={adminLdapSecurityContainer.state.ldapGroupSearchBase || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchBase(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -359,7 +359,7 @@ class LdapSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="groupSearchFilter"
-                  defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchFilter || ''}
+                  value={adminLdapSecurityContainer.state.ldapGroupSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchFilter(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -391,7 +391,7 @@ class LdapSecuritySettingContents extends React.Component {
                   type="text"
                   placeholder="Default: uid"
                   name="groupDnProperty"
-                  defaultValue={adminLdapSecurityContainer.state.ldapGroupDnProperty || ''}
+                  value={adminLdapSecurityContainer.state.ldapGroupDnProperty || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupDnProperty(e.target.value)}
                 />
                 <p className="form-text text-muted">

+ 1 - 1
apps/app/src/client/components/Admin/Security/LocalSecuritySettingContents.jsx

@@ -147,7 +147,7 @@ class LocalSecuritySettingContents extends React.Component {
                   className="form-control"
                   type="textarea"
                   name="registrationWhitelist"
-                  defaultValue={adminLocalSecurityContainer.state.registrationWhitelist.join('\n')}
+                  value={adminLocalSecurityContainer.state.registrationWhitelist.join('\n')}
                   onChange={e => adminLocalSecurityContainer.changeRegistrationWhitelist(e.target.value)}
                 />
                 <p className="form-text text-muted small">

+ 16 - 16
apps/app/src/client/components/Admin/Security/OidcSecuritySettingContents.jsx

@@ -101,7 +101,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcProviderName"
-                  defaultValue={adminOidcSecurityContainer.state.oidcProviderName || ''}
+                  value={adminOidcSecurityContainer.state.oidcProviderName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcProviderName(e.target.value)}
                 />
               </div>
@@ -114,7 +114,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcIssuerHost"
-                  defaultValue={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
+                  value={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -130,7 +130,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcClientId"
-                  defaultValue={adminOidcSecurityContainer.state.oidcClientId || ''}
+                  value={adminOidcSecurityContainer.state.oidcClientId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -146,7 +146,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcClientSecret"
-                  defaultValue={adminOidcSecurityContainer.state.oidcClientSecret || ''}
+                  value={adminOidcSecurityContainer.state.oidcClientSecret || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -164,7 +164,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcAuthorizationEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcAuthorizationEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcAuthorizationEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAuthorizationEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -180,7 +180,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcTokenEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcTokenEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcTokenEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcTokenEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -198,7 +198,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcRevocationEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcRevocationEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcRevocationEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcRevocationEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -216,7 +216,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcIntrospectionEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcIntrospectionEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcIntrospectionEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcIntrospectionEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -234,7 +234,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcUserInfoEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcUserInfoEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcUserInfoEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcUserInfoEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -252,7 +252,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcEndSessionEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcEndSessionEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcEndSessionEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcEndSessionEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -270,7 +270,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcRegistrationEndpoint"
-                  defaultValue={adminOidcSecurityContainer.state.oidcRegistrationEndpoint || ''}
+                  value={adminOidcSecurityContainer.state.oidcRegistrationEndpoint || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcRegistrationEndpoint(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -286,7 +286,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcJWKSUri"
-                  defaultValue={adminOidcSecurityContainer.state.oidcJWKSUri || ''}
+                  value={adminOidcSecurityContainer.state.oidcJWKSUri || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcJWKSUri(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -306,7 +306,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcAttrMapId"
-                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
+                  value={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -322,7 +322,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcAttrMapUserName"
-                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
+                  value={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -338,7 +338,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcAttrMapName"
-                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
+                  value={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                 />
                 <p className="form-text text-muted">
@@ -354,7 +354,7 @@ class OidcSecurityManagementContents extends React.Component {
                   className="form-control"
                   type="text"
                   name="oidcAttrMapEmail"
-                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
+                  value={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                 />
                 <p className="form-text text-muted">

+ 9 - 9
apps/app/src/client/components/Admin/Security/SamlSecuritySettingContents.jsx

@@ -150,7 +150,7 @@ class SamlSecurityManagementContents extends React.Component {
                       type="text"
                       name="samlEntryPoint"
                       readOnly={useOnlyEnvVars}
-                      defaultValue={adminSamlSecurityContainer.state.samlEntryPoint}
+                      value={adminSamlSecurityContainer.state.samlEntryPoint}
                       onChange={e => adminSamlSecurityContainer.changeSamlEntryPoint(e.target.value)}
                     />
                   </td>
@@ -174,7 +174,7 @@ class SamlSecurityManagementContents extends React.Component {
                       type="text"
                       name="samlEnvVarissuer"
                       readOnly={useOnlyEnvVars}
-                      defaultValue={adminSamlSecurityContainer.state.samlIssuer}
+                      value={adminSamlSecurityContainer.state.samlIssuer}
                       onChange={e => adminSamlSecurityContainer.changeSamlIssuer(e.target.value)}
                     />
                   </td>
@@ -199,7 +199,7 @@ class SamlSecurityManagementContents extends React.Component {
                       rows="5"
                       name="samlCert"
                       readOnly={useOnlyEnvVars}
-                      defaultValue={adminSamlSecurityContainer.state.samlCert}
+                      value={adminSamlSecurityContainer.state.samlCert}
                       onChange={e => adminSamlSecurityContainer.changeSamlCert(e.target.value)}
                     />
                     <p>
@@ -258,7 +258,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <input
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlAttrMapId}
+                      value={adminSamlSecurityContainer.state.samlAttrMapId}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapId(e.target.value)}
                     />
                     <p className="form-text text-muted">
@@ -285,7 +285,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <input
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlAttrMapUsername}
+                      value={adminSamlSecurityContainer.state.samlAttrMapUsername}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapUserName(e.target.value)}
                     />
                     <p className="form-text text-muted">
@@ -310,7 +310,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <input
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlAttrMapMail}
+                      value={adminSamlSecurityContainer.state.samlAttrMapMail}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapMail(e.target.value)}
                     />
                     <p className="form-text text-muted">
@@ -335,7 +335,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <input
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlAttrMapFirstName}
+                      value={adminSamlSecurityContainer.state.samlAttrMapFirstName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapFirstName(e.target.value)}
                     />
                     <p className="form-text text-muted">
@@ -365,7 +365,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <input
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlAttrMapLastName}
+                      value={adminSamlSecurityContainer.state.samlAttrMapLastName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapLastName(e.target.value)}
                     />
                     <p className="form-text text-muted">
@@ -462,7 +462,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     <textarea
                       className="form-control"
                       type="text"
-                      defaultValue={adminSamlSecurityContainer.state.samlABLCRule || ''}
+                      value={adminSamlSecurityContainer.state.samlABLCRule || ''}
                       onChange={(e) => { adminSamlSecurityContainer.changeSamlABLCRule(e.target.value) }}
                     />
                     <div className="mt-2">

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

@@ -591,7 +591,7 @@ class SecuritySetting extends React.Component {
             <input
               className="form-control col-md-4"
               type="text"
-              defaultValue={adminGeneralSecurityContainer.state.sessionMaxAge || ''}
+              value={adminGeneralSecurityContainer.state.sessionMaxAge || ''}
               onChange={(e) => {
                 adminGeneralSecurityContainer.setSessionMaxAge(e.target.value);
               }}

+ 1 - 1
apps/app/src/client/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx

@@ -111,7 +111,7 @@ const CustomBotWithProxySettings = (props) => {
                 className="form-control"
                 type="text"
                 name="settingForm[proxyUrl]"
-                defaultValue={newProxyServerUri}
+                value={newProxyServerUri}
                 onChange={(e) => { setNewProxyServerUri(e.target.value) }}
               />
             </div>

+ 2 - 2
apps/app/src/client/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx

@@ -65,7 +65,7 @@ const CustomBotWithoutProxySecretTokenSection = (props) => {
           <input
             className="form-control"
             type="text"
-            defaultValue={slackSigningSecretEnv}
+            value={slackSigningSecretEnv}
             readOnly
           />
           <p className="form-text text-muted">
@@ -94,7 +94,7 @@ const CustomBotWithoutProxySecretTokenSection = (props) => {
           <input
             className="form-control"
             type="text"
-            defaultValue={slackBotTokenEnv}
+            value={slackBotTokenEnv}
             readOnly
           />
           <p className="form-text text-muted">

+ 1 - 1
apps/app/src/client/components/Admin/SlackIntegration/ManageCommandsProcess.jsx

@@ -147,7 +147,7 @@ const PermissionSettingForEachPermissionTypeComponent = ({
             className="form-control"
             type="textarea"
             name={keyName}
-            defaultValue={textareaDefaultValue}
+            value={textareaDefaultValue}
             onChange={onUpdateChannels}
           />
           <p className="form-text text-muted small">

+ 0 - 1
apps/app/src/client/components/Sidebar/InAppNotification/PrimaryItemForNotification.tsx

@@ -8,7 +8,6 @@ import { PrimaryItem, type PrimaryItemProps } from '../SidebarNav/PrimaryItem';
 
 type PrimaryItemForNotificationProps = Omit<PrimaryItemProps, 'onClick' | 'label' | 'iconName' | 'contents' | 'badgeContents' >
 
-// TODO(after v7 release): https://redmine.weseek.co.jp/issues/138463
 export const PrimaryItemForNotification = memo((props: PrimaryItemForNotificationProps) => {
   const { sidebarMode, onHover } = props;
 

+ 15 - 4
apps/app/src/client/components/Sidebar/SidebarContents.tsx

@@ -2,6 +2,7 @@ import React, { memo, useMemo } from 'react';
 
 import { AiAssistant } from '~/features/openai/client/components/AiAssistant/Sidebar/AiAssistant';
 import { SidebarContentsType } from '~/interfaces/ui';
+import { useIsAiEnabled, useIsGuestUser } from '~/stores-universal/context';
 import { useCollapsedContentsOpened, useCurrentSidebarContents, useSidebarMode } from '~/stores/ui';
 
 
@@ -17,8 +18,10 @@ import styles from './SidebarContents.module.scss';
 
 export const SidebarContents = memo(() => {
   const { isCollapsedMode } = useSidebarMode();
-  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const { data: isGuestUser } = useIsGuestUser();
+  const { data: isAiEnabled } = useIsAiEnabled();
 
+  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
   const { data: currentSidebarContents } = useCurrentSidebarContents();
 
   const Contents = useMemo(() => {
@@ -32,13 +35,21 @@ export const SidebarContents = memo(() => {
       case SidebarContentsType.BOOKMARKS:
         return Bookmarks;
       case SidebarContentsType.NOTIFICATION:
-        return InAppNotification;
+        if (isGuestUser == null) return () => <></>; // wait for isGuestUser to be determined
+        if (!isGuestUser) {
+          return InAppNotification;
+        }
+        return PageTree;
       case SidebarContentsType.AI_ASSISTANT:
-        return AiAssistant;
+        if (isAiEnabled == null) return () => <></>; // wait for isAiEnabled to be determined
+        if (isAiEnabled) {
+          return AiAssistant;
+        }
+        return PageTree;
       default:
         return PageTree;
     }
-  }, [currentSidebarContents]);
+  }, [currentSidebarContents, isAiEnabled, isGuestUser]);
 
   const isHidden = isCollapsedMode() && !isCollapsedContentsOpened;
   const classToHide = isHidden ? 'd-none' : '';

+ 3 - 2
apps/app/src/client/components/Sidebar/SidebarNav/PrimaryItems.tsx

@@ -3,7 +3,7 @@ import { memo } from 'react';
 import dynamic from 'next/dynamic';
 
 import { SidebarContentsType } from '~/interfaces/ui';
-import { useIsAiEnabled } from '~/stores-universal/context';
+import { useIsAiEnabled, useIsGuestUser } from '~/stores-universal/context';
 import { useSidebarMode } from '~/stores/ui';
 
 import { PrimaryItem } from './PrimaryItem';
@@ -24,6 +24,7 @@ export const PrimaryItems = memo((props: Props) => {
 
   const { data: sidebarMode } = useSidebarMode();
   const { data: isAiEnabled } = useIsAiEnabled();
+  const { data: isGuestUser } = useIsGuestUser();
 
   if (sidebarMode == null) {
     return <></>;
@@ -36,7 +37,7 @@ export const PrimaryItems = memo((props: Props) => {
       <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onHover={onItemHover} />
       <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmarks" onHover={onItemHover} />
       <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onHover={onItemHover} />
-      <PrimaryItemForNotification sidebarMode={sidebarMode} onHover={onItemHover} />
+      {isGuestUser === false && <PrimaryItemForNotification sidebarMode={sidebarMode} onHover={onItemHover} />}
       {isAiEnabled && (
         <PrimaryItem
           sidebarMode={sidebarMode}

+ 4 - 2
apps/app/src/client/services/page-operation.ts

@@ -5,6 +5,7 @@ import { SubscriptionStatusType } from '@growi/core';
 import urljoin from 'url-join';
 
 import type { SyncLatestRevisionBody } from '~/interfaces/yjs';
+import { useIsGuestUser } from '~/stores-universal/context';
 import { useEditingMarkdown, usePageTagsForEditors } from '~/stores/editor';
 import {
   useCurrentPageId, useSWRMUTxCurrentPage, useSWRxApplicableGrant, useSWRxTagsInfo,
@@ -103,8 +104,9 @@ export const useUpdateStateAfterSave = (pageId: string|undefined|null, opts?: Up
   const { mutate: mutateTagsInfo } = useSWRxTagsInfo(pageId);
   const { sync: syncTagsInfoForEditor } = usePageTagsForEditors(pageId);
   const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
-  const { mutate: mutateCurrentGrantData } = useSWRxCurrentGrantData(pageId);
-  const { mutate: mutateApplicableGrant } = useSWRxApplicableGrant(pageId);
+  const { data: isGuestUser } = useIsGuestUser();
+  const { mutate: mutateCurrentGrantData } = useSWRxCurrentGrantData(isGuestUser ? null : pageId);
+  const { mutate: mutateApplicableGrant } = useSWRxApplicableGrant(isGuestUser ? null : pageId);
 
   // update swr 'currentPageId', 'currentPage', remote states
   return useCallback(async() => {

+ 11 - 6
apps/app/src/features/openai/client/components/AiAssistant/OpenDefaultAiAssistantButton.tsx

@@ -10,9 +10,8 @@ import { useAiAssistantSidebar, useSWRxAiAssistants } from '../../stores/ai-assi
 
 import styles from './OpenDefaultAiAssistantButton.module.scss';
 
-const OpenDefaultAiAssistantButton = (): JSX.Element => {
+const OpenDefaultAiAssistantButtonSubstance = (): JSX.Element => {
   const { t } = useTranslation();
-  const { data: isAiEnabled } = useIsAiEnabled();
   const { data: aiAssistantData } = useSWRxAiAssistants();
   const { openChat } = useAiAssistantSidebar();
 
@@ -33,10 +32,6 @@ const OpenDefaultAiAssistantButton = (): JSX.Element => {
     openChat(defaultAiAssistant);
   }, [defaultAiAssistant, openChat]);
 
-  if (!isAiEnabled) {
-    return <></>;
-  }
-
   return (
     <NotAvailableForGuest>
       <NotAvailable isDisabled={defaultAiAssistant == null} title={t('default_ai_assistant.not_set')}>
@@ -52,4 +47,14 @@ const OpenDefaultAiAssistantButton = (): JSX.Element => {
   );
 };
 
+const OpenDefaultAiAssistantButton = (): JSX.Element => {
+  const { data: isAiEnabled } = useIsAiEnabled();
+
+  if (!isAiEnabled) {
+    return <></>;
+  }
+
+  return <OpenDefaultAiAssistantButtonSubstance />;
+};
+
 export default OpenDefaultAiAssistantButton;

+ 10 - 11
apps/app/src/server/routes/apiv3/security-settings/index.js

@@ -1,4 +1,4 @@
-import { ConfigSource, SCOPE } from '@growi/core/dist/interfaces';
+import { ConfigSource, toNonBlankStringOrUndefined, SCOPE } from '@growi/core/dist/interfaces';
 import { ErrorV3 } from '@growi/core/dist/models';
 import xss from 'xss';
 
@@ -409,11 +409,11 @@ module.exports = (crowi) => {
 
   const activityEvent = crowi.event('activity');
 
-  async function updateAndReloadStrategySettings(authId, params) {
+  async function updateAndReloadStrategySettings(authId, params, opts = { removeIfUndefined: false }) {
     const { passportService } = crowi;
 
     // update config without publishing S2sMessage
-    await configManager.updateConfigs(params, { skipPubsub: true });
+    await configManager.updateConfigs(params, { skipPubsub: true, removeIfUndefined: opts.removeIfUndefined });
 
     await passportService.setupStrategyById(authId);
     passportService.publishUpdatedMessage(authId);
@@ -1278,15 +1278,14 @@ module.exports = (crowi) => {
   router.put('/google-oauth', accessTokenParser([SCOPE.WRITE.ADMIN.SECURITY]), loginRequiredStrictly, adminRequired, addActivity,
     validator.googleOAuth, apiV3FormValidator,
     async(req, res) => {
-      const requestParams = {
-        'security:passport-google:clientId': req.body.googleClientId,
-        'security:passport-google:clientSecret': req.body.googleClientSecret,
-        'security:passport-google:isSameEmailTreatedAsIdenticalUser': req.body.isSameEmailTreatedAsIdenticalUser,
-      };
-
-
       try {
-        await updateAndReloadStrategySettings('google', requestParams);
+        await updateAndReloadStrategySettings('google', {
+          'security:passport-google:isSameEmailTreatedAsIdenticalUser': req.body.isSameEmailTreatedAsIdenticalUser,
+        });
+        await updateAndReloadStrategySettings('google', {
+          'security:passport-google:clientId': toNonBlankStringOrUndefined(req.body.googleClientId),
+          'security:passport-google:clientSecret': toNonBlankStringOrUndefined(req.body.googleClientSecret),
+        }, { removeIfUndefined: true });
 
         const securitySettingParams = {
           googleClientId: await configManager.getConfig('security:passport-google:clientId'),

+ 2 - 2
apps/app/src/server/service/config-manager/config-definition.ts

@@ -731,10 +731,10 @@ export const CONFIG_DEFINITIONS = {
   'security:passport-google:isEnabled': defineConfig<boolean>({
     defaultValue: false,
   }),
-  'security:passport-google:clientId': defineConfig<string | undefined>({
+  'security:passport-google:clientId': defineConfig<NonBlankString | undefined>({
     defaultValue: undefined,
   }),
-  'security:passport-google:clientSecret': defineConfig<string | undefined>({
+  'security:passport-google:clientSecret': defineConfig<NonBlankString | undefined>({
     defaultValue: undefined,
   }),
   'security:passport-google:isSameUsernameTreatedAsIdenticalUser': defineConfig<boolean>({

+ 1 - 1
apps/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "7.2.6-slackbot-proxy.0",
+  "version": "7.2.7-slackbot-proxy.0",
   "license": "MIT",
   "private": "true",
   "scripts": {

+ 1 - 2
biome.json

@@ -28,8 +28,7 @@
       "./packages/presentation/**",
       "./packages/preset-templates/**",
       "./packages/preset-themes/**",
-      "./packages/remark-attachment-refs/**",
-      "./packages/remark-drawio/**"
+      "./packages/remark-attachment-refs/**"
     ]
   },
   "formatter": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "7.2.6-RC.0",
+  "version": "7.2.7-RC.0",
   "description": "Team collaboration software using markdown",
   "license": "MIT",
   "private": "true",

+ 1 - 2
packages/remark-drawio/.eslintignore

@@ -1,2 +1 @@
-/dist/**
-/types/**
+*

+ 0 - 5
packages/remark-drawio/.eslintrc.cjs

@@ -1,5 +0,0 @@
-module.exports = {
-  extends: [
-    'weseek/react',
-  ],
-};

+ 1 - 1
packages/remark-drawio/package.json

@@ -25,7 +25,7 @@
     "clean": "shx rm -rf dist",
     "dev": "vite build --mode dev",
     "watch": "pnpm run dev -w --emptyOutDir=false",
-    "lint:js": "eslint **/*.{js,jsx,ts,tsx}",
+    "lint:js": "biome check",
     "lint:styles": "stylelint --allow-empty-input \"src/**/*.scss\" \"src/**/*.css\"",
     "lint:typecheck": "vue-tsc --noEmit",
     "lint": "run-p lint:*"

+ 49 - 39
packages/remark-drawio/src/components/DrawioViewer.tsx

@@ -1,6 +1,12 @@
 import {
-  type ReactNode, type JSX,
-  memo, useCallback, useEffect, useMemo, useRef, useState,
+  type JSX,
+  type ReactNode,
+  memo,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
 } from 'react';
 
 import { debounce } from 'throttle-debounce';
@@ -9,35 +15,36 @@ import type { IGraphViewerGlobal } from '..';
 import { generateMxgraphData } from '../utils/embed';
 import { isGraphViewerGlobal } from '../utils/global';
 
-
 import styles from './DrawioViewer.module.scss';
 
-
 declare global {
   // eslint-disable-next-line vars-on-top, no-var
   var GraphViewer: IGraphViewerGlobal;
 }
 
-
 export type DrawioViewerProps = {
-  diagramIndex: number,
-  bol: number,
-  eol: number,
-  children?: ReactNode,
-  onRenderingStart?: () => void,
-  onRenderingUpdated?: (mxfile: string | null) => void,
-}
+  diagramIndex: number;
+  bol: number;
+  eol: number;
+  children?: ReactNode;
+  onRenderingStart?: () => void;
+  onRenderingUpdated?: (mxfile: string | null) => void;
+};
 
 export type DrawioEditByViewerProps = {
-  bol: number,
-  eol: number,
-  drawioMxFile: string,
-}
+  bol: number;
+  eol: number;
+  drawioMxFile: string;
+};
 
 export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
   const {
-    diagramIndex, bol, eol, children,
-    onRenderingStart, onRenderingUpdated,
+    diagramIndex,
+    bol,
+    eol,
+    children,
+    onRenderingStart,
+    onRenderingUpdated,
   } = props;
 
   const drawioContainerRef = useRef<HTMLDivElement>(null);
@@ -56,7 +63,9 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
       return;
     }
 
-    const mxgraphs = drawioContainerRef.current.getElementsByClassName('mxgraph') as HTMLCollectionOf<HTMLElement>;
+    const mxgraphs = drawioContainerRef.current.getElementsByClassName(
+      'mxgraph',
+    ) as HTMLCollectionOf<HTMLElement>;
     if (mxgraphs.length > 0) {
       // This component should have only one '.mxgraph' element
       const div = mxgraphs[0];
@@ -73,15 +82,17 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
           GraphViewer.prototype.lightboxZIndex = 1055; // set $zindex-modal
           GraphViewer.prototype.toolbarZIndex = 1055; // set $zindex-modal
           GraphViewer.createViewerForElement(div);
-        }
-        catch (err) {
+        } catch (err) {
           setError(err);
         }
       }
     }
   }, []);
 
-  const renderDrawioWithDebounce = useMemo(() => debounce(200, renderDrawio), [renderDrawio]);
+  const renderDrawioWithDebounce = useMemo(
+    () => debounce(200, renderDrawio),
+    [renderDrawio],
+  );
 
   const mxgraphHtml = useMemo(() => {
     setError(undefined);
@@ -90,17 +101,16 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
       return '';
     }
 
-    const code = children instanceof Array
+    const code = Array.isArray(children)
       ? children
-        .filter(elem => (typeof elem === 'string')) // omit non-string elements (e.g. br element generated by line-breaks option)
-        .join('')
+          .filter((elem) => typeof elem === 'string') // omit non-string elements (e.g. br element generated by line-breaks option)
+          .join('')
       : children.toString();
 
-    let mxgraphData;
+    let mxgraphData: string | undefined;
     try {
       mxgraphData = generateMxgraphData(code);
-    }
-    catch (err) {
+    } catch (err) {
       setError(err);
     }
 
@@ -125,8 +135,8 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
     const container = drawioContainerRef.current;
     if (container == null) return;
 
-    const observerCallback = (mutationRecords:MutationRecord[]) => {
-      mutationRecords.forEach((record:MutationRecord) => {
+    const observerCallback = (mutationRecords: MutationRecord[]) => {
+      for (const record of mutationRecords) {
         const target = record.target as HTMLElement;
 
         const mxgraphData = target.dataset.mxgraph;
@@ -134,7 +144,7 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
           const mxgraph = JSON.parse(mxgraphData);
           onRenderingUpdated?.(mxgraph.xml);
         }
-      });
+      }
     };
 
     const observer = new MutationObserver(observerCallback);
@@ -152,11 +162,11 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
     }
 
     const observer = new ResizeObserver((entries) => {
-      entries.forEach(() => {
-        // setElementWidth(el.contentRect.width);
+      for (const entry of entries) {
+        // setElementWidth(entry.contentRect.width);
         onRenderingStart?.();
         renderDrawioWithDebounce();
-      });
+      }
     });
     observer.observe(drawioContainerRef.current);
     return () => {
@@ -174,18 +184,18 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
       data-end-line-number-of-markdown={eol}
     >
       {/* show error */}
-      { error != null && (
+      {error != null && (
         <span className="text-muted">
           <span className="material-symbols-outlined me-1">error</span>
           {error.name && <strong>{error.name}: </strong>}
           {error.message}
         </span>
-      ) }
+      )}
 
-      { error == null && (
-        // eslint-disable-next-line react/no-danger
+      {error == null && (
+        // biome-ignore lint/security/noDangerouslySetInnerHtml: ignore
         <div dangerouslySetInnerHTML={{ __html: mxgraphHtml }} />
-      ) }
+      )}
     </div>
   );
 });

+ 12 - 9
packages/remark-drawio/src/interfaces/graph-viewer.ts

@@ -1,15 +1,18 @@
 export interface IGraphViewer {
-  checkVisibleState: boolean,
-  responsive: boolean,
-  lightboxZIndex: number,
-  toolbarZIndex: number,
-  xml: string,
+  checkVisibleState: boolean;
+  responsive: boolean;
+  lightboxZIndex: number;
+  toolbarZIndex: number;
+  xml: string;
 }
 
 export interface IGraphViewerGlobal {
-  processElements: () => void,
-  createViewerForElement: (element: Element, callback?: (viewer: IGraphViewer) => void) => void,
+  processElements: () => void;
+  createViewerForElement: (
+    element: Element,
+    callback?: (viewer: IGraphViewer) => void,
+  ) => void;
 
-  useResizeSensor: boolean,
-  prototype: IGraphViewer,
+  useResizeSensor: boolean;
+  prototype: IGraphViewer;
 }

+ 17 - 17
packages/remark-drawio/src/services/renderer/remark-drawio.ts

@@ -1,16 +1,14 @@
 import type { Properties } from 'hast';
 import type { Schema as SanitizeOption } from 'hast-util-sanitize';
-import type {
-  Code, Node, Paragraph,
-} from 'mdast';
+import type { Code, Node, Paragraph } from 'mdast';
 import type { Plugin } from 'unified';
 import { visit } from 'unist-util-visit';
 
 const SUPPORTED_ATTRIBUTES = ['diagramIndex', 'bol', 'eol'];
 
 interface Data {
-  hName?: string,
-  hProperties?: Properties,
+  hName?: string;
+  hProperties?: Properties;
 }
 
 type Lang = 'drawio';
@@ -20,11 +18,15 @@ function isDrawioBlock(lang?: string | null): lang is Lang {
 }
 
 function rewriteNode(node: Node, index: number) {
-
   node.type = 'paragraph';
-  (node as Paragraph).children = [{ type: 'text', value: (node as Code).value }];
-
-  const data: Data = node.data ?? (node.data = {});
+  (node as Paragraph).children = [
+    { type: 'text', value: (node as Code).value },
+  ];
+
+  if (node.data == null) {
+    node.data = {};
+  }
+  const data: Data = node.data;
   data.hName = 'drawio';
   data.hProperties = {
     diagramIndex: index,
@@ -34,14 +36,12 @@ function rewriteNode(node: Node, index: number) {
   };
 }
 
-export const remarkPlugin: Plugin = function() {
-  return (tree) => {
-    visit(tree, 'code', (node: Code, index) => {
-      if (isDrawioBlock(node.lang)) {
-        rewriteNode(node, index ?? 0);
-      }
-    });
-  };
+export const remarkPlugin: Plugin = () => (tree) => {
+  visit(tree, 'code', (node: Code, index) => {
+    if (isDrawioBlock(node.lang)) {
+      rewriteNode(node, index ?? 0);
+    }
+  });
 };
 
 export const sanitizeOption: SanitizeOption = {

+ 18 - 16
packages/remark-drawio/src/utils/embed.ts

@@ -1,7 +1,7 @@
 // transplanted from https://github.com/jgraph/drawio-tools/blob/d46977060ffad70cae5a9059a2cbfcd8bcf420de/tools/convert.html
 import pako from 'pako';
 
-const unconpressedDataRegexp = new RegExp('<mxGraphModel');
+const unconpressedDataRegexp = /<mxGraphModel/;
 const validateUncompressedData = (input: string): boolean => {
   return unconpressedDataRegexp.test(input);
 };
@@ -11,24 +11,24 @@ const validateCompressedData = (input: string): boolean => {
 
   try {
     data = Buffer.from(data, 'base64').toString('binary');
-  }
-  catch (e) {
+  } catch (e) {
     throw new Error(`Base64 to binary failed: ${e}`);
   }
 
   if (data.length > 0) {
     try {
-      data = pako.inflateRaw(Uint8Array.from(data, c => c.charCodeAt(0)), { to: 'string' });
-    }
-    catch (e) {
+      data = pako.inflateRaw(
+        Uint8Array.from(data, (c) => c.charCodeAt(0)),
+        { to: 'string' },
+      );
+    } catch (e) {
       throw new Error(`inflateRaw failed: ${e}`);
     }
   }
 
   try {
     data = decodeURIComponent(data);
-  }
-  catch (e) {
+  } catch (e) {
     throw new Error(`decodeURIComponent failed: ${e}`);
   }
 
@@ -40,14 +40,16 @@ const escapeHTML = (string): string => {
     return string;
   }
   return string.replace(/[&'`"<>]/g, (match): string => {
-    return {
-      '&': '&amp;',
-      "'": '&#x27;',
-      '`': '&#x60;',
-      '"': '&quot;',
-      '<': '&lt;',
-      '>': '&gt;',
-    }[match] ?? match;
+    return (
+      {
+        '&': '&amp;',
+        "'": '&#x27;',
+        '`': '&#x60;',
+        '"': '&quot;',
+        '<': '&lt;',
+        '>': '&gt;',
+      }[match] ?? match
+    );
   });
 };
 

+ 8 - 2
packages/remark-drawio/src/utils/global.ts

@@ -1,5 +1,11 @@
 import type { IGraphViewerGlobal } from '../interfaces/graph-viewer';
 
-export const isGraphViewerGlobal = (val: unknown): val is IGraphViewerGlobal => {
-  return (typeof val === 'function' && 'createViewerForElement' in val && 'processElements' in val);
+export const isGraphViewerGlobal = (
+  val: unknown,
+): val is IGraphViewerGlobal => {
+  return (
+    typeof val === 'function' &&
+    'createViewerForElement' in val &&
+    'processElements' in val
+  );
 };

+ 1 - 3
packages/remark-drawio/tsconfig.json

@@ -11,7 +11,5 @@
     "noImplicitAny": false,
     "noImplicitOverride": true
   },
-  "include": [
-    "src"
-  ]
+  "include": ["src"]
 }