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

Merge pull request #7610 from weseek/feat/120698-120954-rom-user-client

feat: Create ReadOnly user components
Ryoji Shimizu 2 лет назад
Родитель
Сommit
3443f8b913

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

@@ -725,6 +725,7 @@
     },
     "user_table": {
       "administrator": "Administrator",
+      "read_only_user": "Read Only User",
       "edit_menu": "Edit menu",
       "reset_password": "Reset password",
       "administrator_menu": "Administrator Menu",
@@ -734,6 +735,8 @@
       "remove_admin_access": "Remove admin access",
       "cannot_remove": "You cannot remove yourself from administrator",
       "give_admin_access": "Give admin access",
+      "remove_read_only_access": "Remove read only access",
+      "give_read_only_access": "Give read only access",
       "send_invitation_email": "Send invitation email",
       "resend_invitation_email": "Resend invitation email"
     },
@@ -1024,6 +1027,8 @@
   "toaster": {
     "give_user_admin": "Succeeded to give {{username}} admin",
     "remove_user_admin": "Succeeded to remove {{username}} admin",
+    "give_user_read_only": "Succeeded to give {{username}} read only",
+    "remove_user_read_only": "Succeeded to remove {{username}} read only",
     "activate_user_success": "Succeeded to activating {{username}}",
     "deactivate_user_success": "Succeeded to deactivate {{username}}",
     "remove_user_success": "Succeeded to removing {{username}}",

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

@@ -733,6 +733,7 @@
     },
     "user_table": {
       "administrator": "管理者",
+      "read_only_user": "閲覧ユーザー",
       "edit_menu": "編集メニュー",
       "reset_password": "パスワードの再発行",
       "administrator_menu": "管理者メニュー",
@@ -742,6 +743,8 @@
       "remove_admin_access": "管理者から外す",
       "cannot_remove": "自分自身を管理者から外すことはできません",
       "give_admin_access": "管理者にする",
+      "remove_read_only_access": "閲覧ユーザーから外す",
+      "give_read_only_access": "閲覧ユーザーにする",
       "send_invitation_email": "招待メールの送信",
       "resend_invitation_email": "招待メールの再送信"
     },
@@ -1032,6 +1035,8 @@
   "toaster": {
     "give_user_admin": "{{username}}を管理者に設定しました",
     "remove_user_admin": "{{username}}を管理者から外しました",
+    "give_user_read_only": "{{username}}を閲覧ユーザーに設定しました",
+    "remove_user_read_only": "{{username}}を閲覧ユーザーから外しました",
     "activate_user_success": "{{username}}を有効化しました",
     "deactivate_user_success": "{{username}}を無効化しました",
     "remove_user_success": "{{username}}を削除しました",

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

@@ -733,6 +733,7 @@
     },
     "user_table": {
       "administrator": "管理员",
+      "read_only_user": "浏览的用户",
       "edit_menu": "编辑菜单",
       "reset_password": "重置密码",
       "administrator_menu": "管理员菜单",
@@ -742,6 +743,8 @@
       "remove_admin_access": "删除管理员访问权限",
       "cannot_remove": "您不能从管理员中删除自己",
       "give_admin_access": "授予管理员访问权限",
+      "remove_read_only_access": "删除一个用户作为浏览用户",
+      "give_read_only_access": "使一个用户成为阅读用户",
       "send_invitation_email": "发送邀请邮件",
       "resend_invitation_email": "重发邀请函"
     },
@@ -1031,7 +1034,9 @@
   },
   "toaster": {
     "give_user_admin": "Succeeded to give {{username}} admin",
-    "remove_user_admin": "Succeeded to remove {{username}} admin ",
+    "remove_user_admin": "Succeeded to remove {{username}} admin",
+    "give_user_read_only": "Succeeded to give {{username}} read only",
+    "remove_user_read_only": "Succeeded to remove {{username}} read only",
 		"activate_user_success": "Succeeded to activating {{username}}",
 		"deactivate_user_success": "Succeeded to deactivate {{username}}",
     "remove_user_success": "Succeeded to removing {{username}}",

+ 26 - 0
apps/app/src/client/services/AdminUsersContainer.js

@@ -230,6 +230,32 @@ export default class AdminUsersContainer extends Container {
     return username;
   }
 
+  /**
+   * Give user read only
+   * @memberOf AdminUsersContainer
+   * @param {string} userId
+   * @return {string} username
+   */
+  async giveUserReadOnly(userId) {
+    const response = await apiv3Put(`/users/${userId}/give-read-only`);
+    const { username } = response.data.userData;
+    await this.retrieveUsersByPagingNum(this.state.activePage);
+    return username;
+  }
+
+  /**
+   * Remove user read only
+   * @memberOf AdminUsersContainer
+   * @param {string} userId
+   * @return {string} username
+   */
+  async removeUserReadOnly(userId) {
+    const response = await apiv3Put(`/users/${userId}/remove-read-only`);
+    const { username } = response.data.userData;
+    await this.retrieveUsersByPagingNum(this.state.activePage);
+    return username;
+  }
+
   /**
    * Activate user
    * @memberOf AdminUsersContainer

+ 40 - 0
apps/app/src/components/Admin/Users/GiveReadOnlyButton.tsx

@@ -0,0 +1,40 @@
+import React, { useCallback } from 'react';
+
+import type { IUserHasId } from '@growi/core';
+import { useTranslation } from 'next-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+const GiveReadOnlyButton: React.FC<{
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}> = ({ adminUsersContainer, user }): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const onClickGiveReadOnlyBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.giveUserReadOnly(user._id);
+      toastSuccess(t('toaster.give_user_read_only', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return (
+    <button className="dropdown-item" type="button" onClick={onClickGiveReadOnlyBtnHandler}>
+      <i className="icon-fw icon-user-following"></i> {t('user_management.user_table.give_read_only_access')}
+    </button>
+  );
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+// eslint-disable-next-line max-len
+const GiveReadOnlyButtonWrapper: React.ForwardRefExoticComponent<Pick<any, string | number | symbol> & React.RefAttributes<any>> = withUnstatedContainers(GiveReadOnlyButton, [AdminUsersContainer]);
+
+export default GiveReadOnlyButtonWrapper;

+ 40 - 0
apps/app/src/components/Admin/Users/RemoveReadOnlyMenuItem.tsx

@@ -0,0 +1,40 @@
+import React, { useCallback } from 'react';
+
+import type { IUserHasId } from '@growi/core';
+import { useTranslation } from 'next-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+const RemoveReadOnlyMenuItem: React.FC<{
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}> = ({ adminUsersContainer, user }): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const clickRemoveReadOnlyBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.removeUserReadOnly(user._id);
+      toastSuccess(t('toaster.remove_user_read_only', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return (
+    <button className="dropdown-item" type="button" onClick={clickRemoveReadOnlyBtnHandler}>
+      <i className="icon-fw icon-user-unfollow"></i> {t('user_management.user_table.remove_read_only_access')}
+    </button>
+  );
+};
+
+/**
+* Wrapper component for using unstated
+*/
+// eslint-disable-next-line max-len
+const RemoveReadOnlyMenuItemWrapper: React.ForwardRefExoticComponent<Pick<any, string | number | symbol> & React.RefAttributes<any>> = withUnstatedContainers(RemoveReadOnlyMenuItem, [AdminUsersContainer]);
+
+export default RemoveReadOnlyMenuItemWrapper;

+ 6 - 2
apps/app/src/components/Admin/Users/UserMenu.tsx

@@ -11,7 +11,9 @@ import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import GiveAdminButton from './GiveAdminButton';
+import GiveReadOnlyButton from './GiveReadOnlyButton';
 import RemoveAdminMenuItem from './RemoveAdminMenuItem';
+import RemoveReadOnlyMenuItem from './RemoveReadOnlyMenuItem';
 import SendInvitationEmailButton from './SendInvitationEmailButton';
 import StatusActivateButton from './StatusActivateButton';
 import StatusSuspendedMenuItem from './StatusSuspendMenuItem';
@@ -81,8 +83,10 @@ const UserMenu = (props: UserMenuProps) => {
         <li className="dropdown-divider pl-0"></li>
         <li className="dropdown-header">{t('user_management.user_table.administrator_menu')}</li>
         <li>
-          {user.admin === true && <RemoveAdminMenuItem user={user} />}
-          {user.admin === false && <GiveAdminButton user={user} />}
+          {user.admin ? <RemoveAdminMenuItem user={user} /> : <GiveAdminButton user={user} />}
+        </li>
+        <li>
+          {user.readOnly ? <RemoveReadOnlyMenuItem user={user} /> : <GiveReadOnlyButton user={user} />}
         </li>
       </>
     );

+ 5 - 0
apps/app/src/components/Admin/Users/UserTable.tsx

@@ -157,6 +157,11 @@ const UserTable = (props: UserTableProps) => {
                       {t('admin:user_management.user_table.administrator')}
                     </span>
                   )}
+                  {(user.readOnly) && (
+                    <span className="badge badge-light badge-pill ml-2">
+                      {t('admin:user_management.user_table.read_only_user')}
+                    </span>
+                  )}
                 </td>
                 <td>
                   <strong>{user.username}</strong>

+ 1 - 0
packages/core/src/interfaces/user.ts

@@ -13,6 +13,7 @@ export type IUser = {
   imageUrlCached: string,
   isGravatarEnabled: boolean,
   admin: boolean,
+  readOnly: boolean,
   apiToken?: string,
   isEmailPublished: boolean,
   isInvitationEmailSended: boolean,