From 480925781691de456abc427fbbba161be11a3a8a Mon Sep 17 00:00:00 2001 From: sct Date: Fri, 25 Dec 2020 09:58:29 +0900 Subject: [PATCH] feat: add separate auto approve permissions for Movies/Series closes #268 --- server/lib/permissions.ts | 2 + server/routes/request.ts | 40 +++--- src/components/PermissionOption/index.tsx | 97 +++++++++++++++ .../RequestModal/MovieRequestModal.tsx | 3 +- src/components/Settings/SettingsMain.tsx | 104 +++++----------- src/components/UserEdit/index.tsx | 115 ++++++------------ 6 files changed, 194 insertions(+), 167 deletions(-) create mode 100644 src/components/PermissionOption/index.tsx diff --git a/server/lib/permissions.ts b/server/lib/permissions.ts index dae8b9640..6d328f8c7 100644 --- a/server/lib/permissions.ts +++ b/server/lib/permissions.ts @@ -7,6 +7,8 @@ export enum Permission { REQUEST = 32, VOTE = 64, AUTO_APPROVE = 128, + AUTO_APPROVE_MOVIE = 256, + AUTO_APPROVE_TV = 512, } /** diff --git a/server/routes/request.ts b/server/routes/request.ts index 2e5f6ae17..857d11f74 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -127,12 +127,16 @@ requestRoutes.post( media, requestedBy: req.user, // If the user is an admin or has the "auto approve" permission, automatically approve the request - status: req.user?.hasPermission(Permission.AUTO_APPROVE) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, - modifiedBy: req.user?.hasPermission(Permission.AUTO_APPROVE) - ? req.user - : undefined, + status: + req.user?.hasPermission(Permission.AUTO_APPROVE) || + req.user?.hasPermission(Permission.AUTO_APPROVE_MOVIE) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, + modifiedBy: + req.user?.hasPermission(Permission.AUTO_APPROVE) || + req.user?.hasPermission(Permission.AUTO_APPROVE_MOVIE) + ? req.user + : undefined, }); await requestRepository.save(request); @@ -172,19 +176,25 @@ requestRoutes.post( } as Media, requestedBy: req.user, // If the user is an admin or has the "auto approve" permission, automatically approve the request - status: req.user?.hasPermission(Permission.AUTO_APPROVE) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, - modifiedBy: req.user?.hasPermission(Permission.AUTO_APPROVE) - ? req.user - : undefined, + status: + req.user?.hasPermission(Permission.AUTO_APPROVE) || + req.user?.hasPermission(Permission.AUTO_APPROVE_TV) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, + modifiedBy: + req.user?.hasPermission(Permission.AUTO_APPROVE) || + req.user?.hasPermission(Permission.AUTO_APPROVE_TV) + ? req.user + : undefined, seasons: finalSeasons.map( (sn) => new SeasonRequest({ seasonNumber: sn, - status: req.user?.hasPermission(Permission.AUTO_APPROVE) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, + status: + req.user?.hasPermission(Permission.AUTO_APPROVE) || + req.user?.hasPermission(Permission.AUTO_APPROVE_TV) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, }) ), }); diff --git a/src/components/PermissionOption/index.tsx b/src/components/PermissionOption/index.tsx new file mode 100644 index 000000000..d779669db --- /dev/null +++ b/src/components/PermissionOption/index.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { hasPermission } from '../../../server/lib/permissions'; +import { Permission, User } from '../../hooks/useUser'; + +export interface PermissionItem { + id: string; + name: string; + description: string; + permission: Permission; + children?: PermissionItem[]; +} + +interface PermissionOptionProps { + option: PermissionItem; + currentPermission: number; + user?: User; + parent?: PermissionItem; + onUpdate: (newPermissions: number) => void; +} + +const PermissionOption: React.FC = ({ + option, + currentPermission, + onUpdate, + user, + parent, +}) => { + return ( + <> +
+
+ { + onUpdate( + hasPermission(option.permission, currentPermission) + ? currentPermission - option.permission + : currentPermission + option.permission + ); + }} + checked={ + hasPermission(option.permission, currentPermission) || + (!!parent?.permission && + hasPermission(parent.permission, currentPermission)) + } + /> +
+
+ +

{option.description}

+
+
+ {(option.children ?? []).map((child) => ( +
+ onUpdate(newPermission)} + parent={option} + /> +
+ ))} + + ); +}; + +export default PermissionOption; diff --git a/src/components/RequestModal/MovieRequestModal.tsx b/src/components/RequestModal/MovieRequestModal.tsx index 58d891804..3a9abf1ed 100644 --- a/src/components/RequestModal/MovieRequestModal.tsx +++ b/src/components/RequestModal/MovieRequestModal.tsx @@ -68,7 +68,8 @@ const MovieRequestModal: React.FC = ({ if (response.data) { if (onComplete) { onComplete( - hasPermission(Permission.AUTO_APPROVE) + hasPermission(Permission.AUTO_APPROVE) || + hasPermission(Permission.AUTO_APPROVE_MOVIE) ? MediaStatus.PROCESSING : MediaStatus.PENDING ); diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index b5d163aff..8875cbea4 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -10,7 +10,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useUser, Permission } from '../../hooks/useUser'; import { useToasts } from 'react-toast-notifications'; import { messages as permissionMessages } from '../UserEdit'; -import { hasPermission } from '../../../server/lib/permissions'; +import PermissionOption, { PermissionItem } from '../PermissionOption'; const messages = defineMessages({ generalsettings: 'General Settings', @@ -27,13 +27,6 @@ const messages = defineMessages({ defaultPermissions: 'Default User Permissions', }); -interface PermissionOption { - id: string; - name: string; - description: string; - permission: Permission; -} - const SettingsMain: React.FC = () => { const { addToast } = useToasts(); const { hasPermission: userHasPermission } = useUser(); @@ -63,7 +56,7 @@ const SettingsMain: React.FC = () => { return ; } - const permissionList: PermissionOption[] = [ + const permissionList: PermissionItem[] = [ { id: 'admin', name: intl.formatMessage(permissionMessages.admin), @@ -96,12 +89,6 @@ const SettingsMain: React.FC = () => { description: intl.formatMessage(permissionMessages.requestDescription), permission: Permission.REQUEST, }, - { - id: 'vote', - name: intl.formatMessage(permissionMessages.vote), - description: intl.formatMessage(permissionMessages.voteDescription), - permission: Permission.VOTE, - }, { id: 'autoapprove', name: intl.formatMessage(permissionMessages.autoapprove), @@ -109,6 +96,24 @@ const SettingsMain: React.FC = () => { permissionMessages.autoapproveDescription ), permission: Permission.AUTO_APPROVE, + children: [ + { + id: 'autoapprovemovies', + name: intl.formatMessage(permissionMessages.autoapproveMovies), + description: intl.formatMessage( + permissionMessages.autoapproveMoviesDescription + ), + permission: Permission.AUTO_APPROVE_MOVIE, + }, + { + id: 'autoapprovetv', + name: intl.formatMessage(permissionMessages.autoapproveSeries), + description: intl.formatMessage( + permissionMessages.autoapproveSeriesDescription + ), + permission: Permission.AUTO_APPROVE_TV, + }, + ], }, ]; @@ -230,65 +235,18 @@ const SettingsMain: React.FC = () => {
- {permissionList.map((permissionOption) => ( -
( + + setFieldValue( + 'defaultPermissions', + newPermissions ) - ? 'opacity-50' - : '' - }`} - key={`permission-option-${permissionOption.id}`} - > -
- { - setFieldValue( - 'defaultPermissions', - hasPermission( - permissionOption.permission, - values.defaultPermissions - ) - ? values.defaultPermissions - - permissionOption.permission - : values.defaultPermissions + - permissionOption.permission - ); - }} - checked={hasPermission( - permissionOption.permission, - values.defaultPermissions - )} - /> -
-
- -

- {permissionOption.description} -

-
-
+ } + /> ))}
diff --git a/src/components/UserEdit/index.tsx b/src/components/UserEdit/index.tsx index df7f36151..5996bd44a 100644 --- a/src/components/UserEdit/index.tsx +++ b/src/components/UserEdit/index.tsx @@ -2,12 +2,12 @@ import React, { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import LoadingSpinner from '../Common/LoadingSpinner'; import { Permission, useUser } from '../../hooks/useUser'; -import { hasPermission } from '../../../server/lib/permissions'; import Button from '../Common/Button'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import axios from 'axios'; import { useToasts } from 'react-toast-notifications'; import Header from '../Common/Header'; +import PermissionOption, { PermissionItem } from '../PermissionOption'; export const messages = defineMessages({ edituser: 'Edit User', @@ -35,25 +35,24 @@ export const messages = defineMessages({ autoapprove: 'Auto Approve', autoapproveDescription: 'Grants auto approval for any requests made by this user.', + autoapproveMovies: 'Auto Approve Movies', + autoapproveMoviesDescription: + 'Grants auto approve for movie requests made by this user.', + autoapproveSeries: 'Auto Approve Series', + autoapproveSeriesDescription: + 'Grants auto approve for series requests made by this user.', save: 'Save', saving: 'Saving...', usersaved: 'User saved', userfail: 'Something went wrong saving the user.', }); -interface PermissionOption { - id: string; - name: string; - description: string; - permission: Permission; -} - const UserEdit: React.FC = () => { const router = useRouter(); const intl = useIntl(); const { addToast } = useToasts(); const [isUpdating, setIsUpdating] = useState(false); - const { user: currentUser, hasPermission: currentHasPermission } = useUser(); + const { user: currentUser } = useUser(); const { user, error, revalidate } = useUser({ id: Number(router.query.userId), }); @@ -97,7 +96,7 @@ const UserEdit: React.FC = () => { return ; } - const permissionList: PermissionOption[] = [ + const permissionList: PermissionItem[] = [ { id: 'admin', name: intl.formatMessage(messages.admin), @@ -139,6 +138,24 @@ const UserEdit: React.FC = () => { name: intl.formatMessage(messages.autoapprove), description: intl.formatMessage(messages.autoapproveDescription), permission: Permission.AUTO_APPROVE, + children: [ + { + id: 'autoapprovemovies', + name: intl.formatMessage(messages.autoapproveMovies), + description: intl.formatMessage( + messages.autoapproveMoviesDescription + ), + permission: Permission.AUTO_APPROVE_MOVIE, + }, + { + id: 'autoapprovetv', + name: intl.formatMessage(messages.autoapproveSeries), + description: intl.formatMessage( + messages.autoapproveSeriesDescription + ), + permission: Permission.AUTO_APPROVE_TV, + }, + ], }, ]; @@ -231,74 +248,16 @@ const UserEdit: React.FC = () => {
- {permissionList.map((permissionOption) => ( -
-
- { - setCurrentPermission((current) => - hasPermission( - permissionOption.permission, - currentPermission - ) - ? current - permissionOption.permission - : current + permissionOption.permission - ); - }} - checked={hasPermission( - permissionOption.permission, - currentPermission - )} - /> -
-
- -

- {permissionOption.description} -

-
-
+ {permissionList.map((permissionItem) => ( + + setCurrentPermission(newPermission) + } + /> ))}