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

Merge pull request #10623 from growilabs/feat/174791-default-user-role-setting-api

feat: Can set default user role as read-only for new users
Yuki Takei 3 месяцев назад
Родитель
Сommit
56eebc9bab

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

@@ -356,6 +356,8 @@
     "confidential_example": "ex): internal use only",
     "confidential_example": "ex): internal use only",
     "default_language": "Default language for new users",
     "default_language": "Default language for new users",
     "default_mail_visibility": "Disclose e-mail for new users",
     "default_mail_visibility": "Disclose e-mail for new users",
+    "default_read_only_for_new_user": "Editing Restrictions for New Users",
+    "set_read_only_for_new_user": "Set new users to read-only mode",
     "file_uploading": "File uploading",
     "file_uploading": "File uploading",
     "page_bulk_export_settings": "Page Bulk Export Settings",
     "page_bulk_export_settings": "Page Bulk Export Settings",
     "enable_page_bulk_export": "Enable bulk export",
     "enable_page_bulk_export": "Enable bulk export",

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

@@ -356,6 +356,8 @@
     "confidential_example": "ex): usage interne seulement",
     "confidential_example": "ex): usage interne seulement",
     "default_language": "Langue par défaut",
     "default_language": "Langue par défaut",
     "default_mail_visibility": "Mode d'affichage de l'adresse courriel",
     "default_mail_visibility": "Mode d'affichage de l'adresse courriel",
+    "default_read_only_for_new_user": "Restriction d'édition pour les nouveaux utilisateurs",
+    "set_read_only_for_new_user": "Rendre les nouveaux utilisateurs en lecture seule",
     "file_uploading": "Téléversement de fichiers",
     "file_uploading": "Téléversement de fichiers",
     "page_bulk_export_settings": "Paramètres d'exportation de pages par lots",
     "page_bulk_export_settings": "Paramètres d'exportation de pages par lots",
     "enable_page_bulk_export": "Activer l'exportation groupée",
     "enable_page_bulk_export": "Activer l'exportation groupée",

+ 2 - 0
apps/app/public/static/locales/ja_JP/admin.json

@@ -365,6 +365,8 @@
     "confidential_example": "例: 社外秘",
     "confidential_example": "例: 社外秘",
     "default_language": "新規ユーザーのデフォルト設定言語",
     "default_language": "新規ユーザーのデフォルト設定言語",
     "default_mail_visibility": "新規ユーザーの初期メール公開設定",
     "default_mail_visibility": "新規ユーザーの初期メール公開設定",
+    "default_read_only_for_new_user": "新規ユーザーの編集制限",
+    "set_read_only_for_new_user": "新規ユーザーを閲覧専用にする",
     "file_uploading": "ファイルアップロード",
     "file_uploading": "ファイルアップロード",
     "page_bulk_export_settings": "ページ一括エクスポート設定",
     "page_bulk_export_settings": "ページ一括エクスポート設定",
     "enable_page_bulk_export": "一括エクスポートを有効にする",
     "enable_page_bulk_export": "一括エクスポートを有効にする",

+ 2 - 0
apps/app/public/static/locales/ko_KR/admin.json

@@ -356,6 +356,8 @@
     "confidential_example": "예): 내부 전용",
     "confidential_example": "예): 내부 전용",
     "default_language": "새 사용자를 위한 기본 언어",
     "default_language": "새 사용자를 위한 기본 언어",
     "default_mail_visibility": "새 사용자를 위한 이메일 공개",
     "default_mail_visibility": "새 사용자를 위한 이메일 공개",
+    "default_read_only_for_new_user": "신규 사용자의 편집 제한",
+    "set_read_only_for_new_user": "신규 사용자를 열람 전용으로 설정",
     "file_uploading": "파일 업로드",
     "file_uploading": "파일 업로드",
     "page_bulk_export_settings": "페이지 대량 내보내기 설정",
     "page_bulk_export_settings": "페이지 대량 내보내기 설정",
     "enable_page_bulk_export": "대량 내보내기 활성화",
     "enable_page_bulk_export": "대량 내보내기 활성화",

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

@@ -365,6 +365,8 @@
     "confidential_example": "ex):仅供内部使用",
     "confidential_example": "ex):仅供内部使用",
     "default_language": "新用户的默认语言",
     "default_language": "新用户的默认语言",
     "default_mail_visibility": "新用户的默认电子邮件可见性",
     "default_mail_visibility": "新用户的默认电子邮件可见性",
+    "default_read_only_for_new_user": "新用戶編輯限制",
+    "set_read_only_for_new_user": "將新用戶設為僅限瀏覽",
     "file_uploading": "文件上传",
     "file_uploading": "文件上传",
     "page_bulk_export_settings": "页面批量导出设置",
     "page_bulk_export_settings": "页面批量导出设置",
     "enable_page_bulk_export": "启用批量导出",
     "enable_page_bulk_export": "启用批量导出",

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

@@ -35,12 +35,14 @@ const AppSetting = (props) => {
       globalLang: adminAppContainer.state.globalLang || 'en-US',
       globalLang: adminAppContainer.state.globalLang || 'en-US',
       // Convert boolean to string for radio button value
       // Convert boolean to string for radio button value
       isEmailPublishedForNewUser: String(adminAppContainer.state.isEmailPublishedForNewUser ?? true),
       isEmailPublishedForNewUser: String(adminAppContainer.state.isEmailPublishedForNewUser ?? true),
+      isReadOnlyForNewUser: adminAppContainer.state.isReadOnlyForNewUser ?? false,
     });
     });
   }, [
   }, [
     adminAppContainer.state.title,
     adminAppContainer.state.title,
     adminAppContainer.state.confidential,
     adminAppContainer.state.confidential,
     adminAppContainer.state.globalLang,
     adminAppContainer.state.globalLang,
     adminAppContainer.state.isEmailPublishedForNewUser,
     adminAppContainer.state.isEmailPublishedForNewUser,
+    adminAppContainer.state.isReadOnlyForNewUser,
     reset,
     reset,
   ]);
   ]);
 
 
@@ -55,6 +57,7 @@ const AppSetting = (props) => {
       // Convert string 'true'/'false' to boolean
       // Convert string 'true'/'false' to boolean
       const isEmailPublished = data.isEmailPublishedForNewUser === 'true' || data.isEmailPublishedForNewUser === true;
       const isEmailPublished = data.isEmailPublishedForNewUser === 'true' || data.isEmailPublishedForNewUser === true;
       await adminAppContainer.changeIsEmailPublishedForNewUserShow(isEmailPublished);
       await adminAppContainer.changeIsEmailPublishedForNewUserShow(isEmailPublished);
+      await adminAppContainer.changeIsReadOnlyForNewUserShow(data.isReadOnlyForNewUser);
 
 
       await adminAppContainer.updateAppSettingHandler();
       await adminAppContainer.updateAppSettingHandler();
       toastSuccess(t('commons:toaster.update_successed', { target: t('commons:headers.app_settings') }));
       toastSuccess(t('commons:toaster.update_successed', { target: t('commons:headers.app_settings') }));
@@ -160,6 +163,26 @@ const AppSetting = (props) => {
         </div>
         </div>
       </div>
       </div>
 
 
+      <div className="row mb-5">
+        <label
+          className="text-start text-md-end col-md-3 col-form-label"
+        >
+          {t('admin:app_setting.default_read_only_for_new_user')}
+        </label>
+        <div className="col-md-6 py-2">
+
+          <div className="form-check form-check-inline">
+            <input
+              type="checkbox"
+              id="checkbox-read-only-for-new-user"
+              className="form-check-input"
+              {...register('isReadOnlyForNewUser')}
+            />
+            <label className="form-label form-check-label" htmlFor="checkbox-read-only-for-new-user">{t('admin:app_setting.set_read_only_for_new_user')}</label>
+          </div>
+        </div>
+      </div>
+
       <AdminUpdateButtonRow type="submit" disabled={adminAppContainer.state.retrieveError != null} />
       <AdminUpdateButtonRow type="submit" disabled={adminAppContainer.state.retrieveError != null} />
     </form>
     </form>
   );
   );

+ 10 - 0
apps/app/src/client/services/AdminAppContainer.js

@@ -21,6 +21,7 @@ export default class AdminAppContainer extends Container {
       confidential: '',
       confidential: '',
       globalLang: '',
       globalLang: '',
       isEmailPublishedForNewUser: true,
       isEmailPublishedForNewUser: true,
+      isReadOnlyForNewUser: false,
 
 
       isV5Compatible: null,
       isV5Compatible: null,
       siteUrl: '',
       siteUrl: '',
@@ -61,6 +62,7 @@ export default class AdminAppContainer extends Container {
       confidential: appSettingsParams.confidential,
       confidential: appSettingsParams.confidential,
       globalLang: appSettingsParams.globalLang,
       globalLang: appSettingsParams.globalLang,
       isEmailPublishedForNewUser: appSettingsParams.isEmailPublishedForNewUser,
       isEmailPublishedForNewUser: appSettingsParams.isEmailPublishedForNewUser,
+      isReadOnlyForNewUser: appSettingsParams.isReadOnlyForNewUser,
       isV5Compatible: appSettingsParams.isV5Compatible,
       isV5Compatible: appSettingsParams.isV5Compatible,
       siteUrl: appSettingsParams.siteUrl,
       siteUrl: appSettingsParams.siteUrl,
       siteUrlUseOnlyEnvVars: appSettingsParams.siteUrlUseOnlyEnvVars,
       siteUrlUseOnlyEnvVars: appSettingsParams.siteUrlUseOnlyEnvVars,
@@ -108,6 +110,13 @@ export default class AdminAppContainer extends Container {
     this.setState({ isEmailPublishedForNewUser });
     this.setState({ isEmailPublishedForNewUser });
   }
   }
 
 
+  /**
+   * Change isReadOnlyForNewUser
+   */
+  changeIsReadOnlyForNewUserShow(isReadOnlyForNewUser) {
+    this.setState({ isReadOnlyForNewUser });
+  }
+
   /**
   /**
    * Change site url
    * Change site url
    */
    */
@@ -189,6 +198,7 @@ export default class AdminAppContainer extends Container {
       confidential: this.state.confidential,
       confidential: this.state.confidential,
       globalLang: this.state.globalLang,
       globalLang: this.state.globalLang,
       isEmailPublishedForNewUser: this.state.isEmailPublishedForNewUser,
       isEmailPublishedForNewUser: this.state.isEmailPublishedForNewUser,
+      isReadOnlyForNewUser: this.state.isReadOnlyForNewUser,
     });
     });
     const { appSettingParams } = response.data;
     const { appSettingParams } = response.data;
     return appSettingParams;
     return appSettingParams;

+ 5 - 0
apps/app/src/server/models/user.js

@@ -294,6 +294,7 @@ const factory = (crowi) => {
     this.isEmailPublished = configManager.getConfig(
     this.isEmailPublished = configManager.getConfig(
       'customize:isEmailPublishedForNewUser',
       'customize:isEmailPublishedForNewUser',
     );
     );
+    this.readOnly = configManager.getConfig('app:isReadOnlyForNewUser');
 
 
     this.save((err, userData) => {
     this.save((err, userData) => {
       userEvent.emit('activated', userData);
       userEvent.emit('activated', userData);
@@ -613,6 +614,8 @@ const factory = (crowi) => {
       newUser.lang = globalLang;
       newUser.lang = globalLang;
     }
     }
 
 
+    newUser.readOnly = configManager.getConfig('app:isReadOnlyForNewUser');
+
     try {
     try {
       const newUserData = await newUser.save();
       const newUserData = await newUser.save();
       return {
       return {
@@ -703,6 +706,8 @@ const factory = (crowi) => {
       'customize:isEmailPublishedForNewUser',
       'customize:isEmailPublishedForNewUser',
     );
     );
 
 
+    newUser.readOnly = configManager.getConfig('app:isReadOnlyForNewUser');
+
     const globalLang = configManager.getConfig('app:globalLang');
     const globalLang = configManager.getConfig('app:globalLang');
     if (globalLang != null) {
     if (globalLang != null) {
       newUser.lang = globalLang;
       newUser.lang = globalLang;

+ 7 - 0
apps/app/src/server/routes/apiv3/app-settings/index.ts

@@ -400,6 +400,9 @@ module.exports = (crowi) => {
         isEmailPublishedForNewUser: configManager.getConfig(
         isEmailPublishedForNewUser: configManager.getConfig(
           'customize:isEmailPublishedForNewUser',
           'customize:isEmailPublishedForNewUser',
         ),
         ),
+        isReadOnlyForNewUser: configManager.getConfig(
+          'app:isReadOnlyForNewUser',
+        ),
         useOnlyEnvVarsForIsBulkExportPagesEnabled: configManager.getConfig(
         useOnlyEnvVarsForIsBulkExportPagesEnabled: configManager.getConfig(
           'env:useOnlyEnvVars:app:isBulkExportPagesEnabled',
           'env:useOnlyEnvVars:app:isBulkExportPagesEnabled',
         ),
         ),
@@ -562,6 +565,7 @@ module.exports = (crowi) => {
         'app:globalLang': req.body.globalLang,
         'app:globalLang': req.body.globalLang,
         'customize:isEmailPublishedForNewUser':
         'customize:isEmailPublishedForNewUser':
           req.body.isEmailPublishedForNewUser,
           req.body.isEmailPublishedForNewUser,
+        'app:isReadOnlyForNewUser': req.body.isReadOnlyForNewUser,
       };
       };
 
 
       try {
       try {
@@ -573,6 +577,9 @@ module.exports = (crowi) => {
           isEmailPublishedForNewUser: configManager.getConfig(
           isEmailPublishedForNewUser: configManager.getConfig(
             'customize:isEmailPublishedForNewUser',
             'customize:isEmailPublishedForNewUser',
           ),
           ),
+          isReadOnlyForNewUser: configManager.getConfig(
+            'app:isReadOnlyForNewUser',
+          ),
         };
         };
 
 
         const parameters = {
         const parameters = {

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

@@ -77,6 +77,7 @@ export const CONFIG_KEYS = [
   'app:wipPageExpirationSeconds',
   'app:wipPageExpirationSeconds',
   'app:openaiThreadDeletionCronMaxMinutesUntilRequest',
   'app:openaiThreadDeletionCronMaxMinutesUntilRequest',
   'app:openaiVectorStoreFileDeletionCronMaxMinutesUntilRequest',
   'app:openaiVectorStoreFileDeletionCronMaxMinutesUntilRequest',
+  'app:isReadOnlyForNewUser',
 
 
   // Security Settings
   // Security Settings
   'security:wikiMode',
   'security:wikiMode',
@@ -515,6 +516,10 @@ export const CONFIG_DEFINITIONS = {
         'OPENAI_VECTOR_STORE_FILE_DELETION_CRON_MAX_MINUTES_UNTIL_REQUEST',
         'OPENAI_VECTOR_STORE_FILE_DELETION_CRON_MAX_MINUTES_UNTIL_REQUEST',
       defaultValue: 30,
       defaultValue: 30,
     }),
     }),
+  'app:isReadOnlyForNewUser': defineConfig<boolean>({
+    envVarName: 'DEFAULT_USER_READONLY',
+    defaultValue: false,
+  }),
 
 
   // Security Settings
   // Security Settings
   'security:wikiMode': defineConfig<string | undefined>({
   'security:wikiMode': defineConfig<string | undefined>({