mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Setup backend permissions system
This commit is contained in:
parent
18c09ecd93
commit
4357fbf83f
77
rsconcept/backend/apps/rsform/permissions.py
Normal file
77
rsconcept/backend/apps/rsform/permissions.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
''' Custom Permission classes.
|
||||
Hierarchy: Anonymous -> User -> Editor -> Owner -> Admin
|
||||
'''
|
||||
from typing import Any, cast
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from rest_framework.permissions import AllowAny as Anyone # pylint: disable=unused-import
|
||||
from rest_framework.permissions import BasePermission as _Base
|
||||
from rest_framework.permissions import \
|
||||
IsAuthenticated as GlobalUser # pylint: disable=unused-import
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from . import models as m
|
||||
|
||||
|
||||
def _extract_item(obj: Any) -> m.LibraryItem:
|
||||
if isinstance(obj, m.LibraryItem):
|
||||
return obj
|
||||
elif isinstance(obj, m.Constituenta):
|
||||
return cast(m.LibraryItem, obj.schema)
|
||||
elif isinstance(obj, (m.Version, m.Subscription, m.Editor)):
|
||||
return cast(m.LibraryItem, obj.item)
|
||||
raise PermissionDenied({
|
||||
'message': 'Invalid type error. Please contact developers',
|
||||
'object_id': obj.id
|
||||
})
|
||||
|
||||
|
||||
class GlobalAdmin(_Base):
|
||||
''' Item permission: Admin or higher. '''
|
||||
|
||||
def has_permission(self, request: Request, view: APIView) -> bool:
|
||||
if not hasattr(request.user, 'is_staff'):
|
||||
return False
|
||||
return request.user.is_staff # type: ignore
|
||||
|
||||
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
||||
if not hasattr(request.user, 'is_staff'):
|
||||
return False
|
||||
return request.user.is_staff # type: ignore
|
||||
|
||||
|
||||
class ItemOwner(GlobalAdmin):
|
||||
''' Item permission: Owner or higher. '''
|
||||
|
||||
def has_permission(self, request: Request, view: APIView) -> bool:
|
||||
return not request.user.is_anonymous
|
||||
|
||||
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
||||
if request.user == _extract_item(obj).owner:
|
||||
return True
|
||||
return super().has_object_permission(request, view, obj)
|
||||
|
||||
|
||||
class ItemEditor(ItemOwner):
|
||||
''' Item permission: Editor or higher. '''
|
||||
|
||||
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
||||
if m.Editor.objects.filter(
|
||||
item=_extract_item(obj),
|
||||
editor=cast(m.User, request.user)
|
||||
).exists():
|
||||
return True
|
||||
return super().has_object_permission(request, view, obj)
|
||||
|
||||
|
||||
class EditorMixin(APIView):
|
||||
''' Editor permissions mixin for API views. '''
|
||||
|
||||
def get_permissions(self):
|
||||
result = super().get_permissions()
|
||||
if self.request.method.upper() == 'GET':
|
||||
result.append(Anyone())
|
||||
else:
|
||||
result.append(ItemEditor())
|
||||
return result
|
|
@ -1,16 +1,16 @@
|
|||
''' Testing API: Library. '''
|
||||
from rest_framework import status
|
||||
|
||||
from apps.rsform.models import LibraryItem, LibraryItemType, LibraryTemplate, RSForm, Subscription
|
||||
from apps.users.models import User
|
||||
from apps.rsform.models import LibraryItem, LibraryItemType, Subscription, LibraryTemplate, RSForm
|
||||
|
||||
from ..testing_utils import response_contains
|
||||
|
||||
from .EndpointTester import decl_endpoint, EndpointTester
|
||||
from .EndpointTester import EndpointTester, decl_endpoint
|
||||
|
||||
|
||||
class TestLibraryViewset(EndpointTester):
|
||||
''' Testing Library view. '''
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.owned = LibraryItem.objects.create(
|
||||
|
@ -71,30 +71,6 @@ class TestLibraryViewset(EndpointTester):
|
|||
self.assertTrue(response.status_code in [status.HTTP_202_ACCEPTED, status.HTTP_204_NO_CONTENT])
|
||||
|
||||
|
||||
@decl_endpoint('/api/library/{item}/claim', method='post')
|
||||
def test_claim(self):
|
||||
self.assertNotFound(item=self.invalid_item)
|
||||
self.assertForbidden(item=self.owned.id)
|
||||
|
||||
self.owned.is_common = True
|
||||
self.owned.save()
|
||||
self.assertNotModified(item=self.owned.id)
|
||||
self.assertForbidden(item=self.unowned.id)
|
||||
|
||||
self.assertFalse(self.user in self.unowned.subscribers())
|
||||
self.unowned.is_common = True
|
||||
self.unowned.save()
|
||||
|
||||
self.assertOK(item=self.unowned.id)
|
||||
self.unowned.refresh_from_db()
|
||||
self.assertEqual(self.unowned.owner, self.user)
|
||||
self.assertEqual(self.unowned.owner, self.user)
|
||||
self.assertTrue(self.user in self.unowned.subscribers())
|
||||
|
||||
self.logout()
|
||||
self.assertForbidden(item=self.owned.id)
|
||||
|
||||
|
||||
@decl_endpoint('/api/library/active', method='get')
|
||||
def test_retrieve_common(self):
|
||||
response = self.execute()
|
||||
|
@ -132,7 +108,7 @@ class TestLibraryViewset(EndpointTester):
|
|||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertFalse(response_contains(response, self.unowned))
|
||||
|
||||
user2 = User.objects.create(username='UserTest2')
|
||||
user2 = User.objects.create(username='UserTest2')
|
||||
Subscription.subscribe(user=self.user, item=self.unowned)
|
||||
Subscription.subscribe(user=user2, item=self.unowned)
|
||||
Subscription.subscribe(user=user2, item=self.owned)
|
||||
|
@ -183,13 +159,13 @@ class TestLibraryViewset(EndpointTester):
|
|||
def test_clone_rsform(self):
|
||||
x12 = self.schema.insert_new(
|
||||
alias='X12',
|
||||
term_raw = 'человек',
|
||||
term_resolved = 'человек'
|
||||
term_raw='человек',
|
||||
term_resolved='человек'
|
||||
)
|
||||
d2 = self.schema.insert_new(
|
||||
alias='D2',
|
||||
term_raw = '@{X12|plur}',
|
||||
term_resolved = 'люди'
|
||||
term_raw='@{X12|plur}',
|
||||
term_resolved='люди'
|
||||
)
|
||||
|
||||
data = {'title': 'Title1337'}
|
||||
|
|
|
@ -4,8 +4,6 @@ import re
|
|||
from io import BytesIO
|
||||
from zipfile import ZipFile
|
||||
|
||||
from rest_framework.permissions import BasePermission, IsAuthenticated
|
||||
|
||||
# Name for JSON inside Exteor files archive
|
||||
EXTEOR_INNER_FILENAME = 'document.json'
|
||||
|
||||
|
@ -13,48 +11,6 @@ EXTEOR_INNER_FILENAME = 'document.json'
|
|||
_REF_OLD_PATTERN = re.compile(r'@{([^0-9\-][^\}\|\{]*?)\|([^\}\|\{]*?)\|([^\}\|\{]*?)}')
|
||||
|
||||
|
||||
class ObjectOwnerOrAdmin(BasePermission):
|
||||
''' Permission for object ownership restriction '''
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.user == obj.owner:
|
||||
return True
|
||||
if not hasattr(request.user, 'is_staff'):
|
||||
return False
|
||||
return request.user.is_staff # type: ignore
|
||||
|
||||
|
||||
class IsClaimable(IsAuthenticated):
|
||||
''' Permission for object ownership restriction '''
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if not super().has_permission(request, view):
|
||||
return False
|
||||
return obj.is_common
|
||||
|
||||
|
||||
class SchemaOwnerOrAdmin(BasePermission):
|
||||
''' Permission for object ownership restriction '''
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.user == obj.schema.owner:
|
||||
return True
|
||||
if not hasattr(request.user, 'is_staff'):
|
||||
return False
|
||||
return request.user.is_staff # type: ignore
|
||||
|
||||
|
||||
class ItemOwnerOrAdmin(BasePermission):
|
||||
''' Permission for object ownership restriction '''
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.user == obj.item.owner:
|
||||
return True
|
||||
if not hasattr(request.user, 'is_staff'):
|
||||
return False
|
||||
return request.user.is_staff # type: ignore
|
||||
|
||||
|
||||
def read_zipped_json(data, json_filename: str) -> dict:
|
||||
''' Read JSON from zipped data '''
|
||||
with ZipFile(data, 'r') as archive:
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
''' Endpoints for Constituenta. '''
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
|
||||
from .. import models as m
|
||||
from .. import permissions
|
||||
from .. import serializers as s
|
||||
from .. import utils
|
||||
|
||||
|
||||
@extend_schema(tags=['Constituenta'])
|
||||
@extend_schema_view()
|
||||
class ConstituentAPIView(generics.RetrieveUpdateAPIView):
|
||||
class ConstituentAPIView(generics.RetrieveUpdateAPIView, permissions.EditorMixin):
|
||||
''' Endpoint: Get / Update Constituenta. '''
|
||||
queryset = m.Constituenta.objects.all()
|
||||
serializer_class = s.CstSerializer
|
||||
|
||||
def get_permissions(self):
|
||||
result = super().get_permissions()
|
||||
if self.request.method.upper() == 'GET':
|
||||
result.append(permissions.AllowAny())
|
||||
else:
|
||||
result.append(utils.SchemaOwnerOrAdmin())
|
||||
return result
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.db import transaction
|
|||
from django.db.models import Q
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import filters, generics, permissions
|
||||
from rest_framework import filters, generics
|
||||
from rest_framework import status as c
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action
|
||||
|
@ -14,15 +14,15 @@ from rest_framework.request import Request
|
|||
from rest_framework.response import Response
|
||||
|
||||
from .. import models as m
|
||||
from .. import permissions
|
||||
from .. import serializers as s
|
||||
from .. import utils
|
||||
|
||||
|
||||
@extend_schema(tags=['Library'])
|
||||
@extend_schema_view()
|
||||
class LibraryActiveView(generics.ListAPIView):
|
||||
''' Endpoint: Get list of library items available for active user. '''
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
permission_classes = (permissions.Anyone,)
|
||||
serializer_class = s.LibraryItemSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -40,7 +40,7 @@ class LibraryActiveView(generics.ListAPIView):
|
|||
@extend_schema_view()
|
||||
class LibraryAdminView(generics.ListAPIView):
|
||||
''' Endpoint: Get list of all library items. Admin only '''
|
||||
permission_classes = (permissions.IsAdminUser,)
|
||||
permission_classes = (permissions.GlobalAdmin,)
|
||||
serializer_class = s.LibraryItemSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -51,7 +51,7 @@ class LibraryAdminView(generics.ListAPIView):
|
|||
@extend_schema_view()
|
||||
class LibraryTemplatesView(generics.ListAPIView):
|
||||
''' Endpoint: Get list of templates. '''
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
permission_classes = (permissions.Anyone,)
|
||||
serializer_class = s.LibraryItemSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -79,14 +79,14 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
return serializer.save()
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action in ['update', 'destroy', 'partial_update']:
|
||||
permission_list = [utils.ObjectOwnerOrAdmin]
|
||||
if self.action in ['destroy']:
|
||||
permission_list = [permissions.ItemOwner]
|
||||
elif self.action in ['update', 'partial_update']:
|
||||
permission_list = [permissions.ItemEditor]
|
||||
elif self.action in ['create', 'clone', 'subscribe', 'unsubscribe']:
|
||||
permission_list = [permissions.IsAuthenticated]
|
||||
elif self.action in ['claim']:
|
||||
permission_list = [utils.IsClaimable]
|
||||
permission_list = [permissions.GlobalUser]
|
||||
else:
|
||||
permission_list = [permissions.AllowAny]
|
||||
permission_list = [permissions.Anyone]
|
||||
return [permission() for permission in permission_list]
|
||||
|
||||
def _get_item(self) -> m.LibraryItem:
|
||||
|
@ -134,32 +134,6 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
)
|
||||
return Response(status=c.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@extend_schema(
|
||||
summary='claim item',
|
||||
tags=['Library'],
|
||||
request=None,
|
||||
responses={
|
||||
c.HTTP_200_OK: s.LibraryItemSerializer,
|
||||
c.HTTP_403_FORBIDDEN: None,
|
||||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@transaction.atomic
|
||||
@action(detail=True, methods=['post'])
|
||||
def claim(self, request: Request, pk=None):
|
||||
''' Endpoint: Claim ownership of LibraryItem. '''
|
||||
item = self._get_item()
|
||||
if item.owner == self.request.user:
|
||||
return Response(status=c.HTTP_304_NOT_MODIFIED)
|
||||
else:
|
||||
item.owner = cast(m.User, self.request.user)
|
||||
item.save()
|
||||
m.Subscription.subscribe(user=item.owner, item=item)
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.LibraryItemSerializer(item).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='subscribe to item',
|
||||
tags=['Library'],
|
||||
|
|
|
@ -6,7 +6,7 @@ import pyconcept
|
|||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
from rest_framework import status as c
|
||||
from rest_framework import views, viewsets
|
||||
from rest_framework.decorators import action, api_view
|
||||
|
@ -15,6 +15,7 @@ from rest_framework.response import Response
|
|||
|
||||
from .. import messages as msg
|
||||
from .. import models as m
|
||||
from .. import permissions
|
||||
from .. import serializers as s
|
||||
from .. import utils
|
||||
|
||||
|
@ -33,9 +34,9 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
''' Determine permission class. '''
|
||||
if self.action in ['load_trs', 'cst_create', 'cst_delete_multiple',
|
||||
'reset_aliases', 'cst_rename', 'cst_substitute']:
|
||||
permission_list = [utils.ObjectOwnerOrAdmin]
|
||||
permission_list = [permissions.ItemOwner]
|
||||
else:
|
||||
permission_list = [permissions.AllowAny]
|
||||
permission_list = [permissions.Anyone]
|
||||
return [permission() for permission in permission_list]
|
||||
|
||||
@extend_schema(
|
||||
|
@ -402,7 +403,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
class TrsImportView(views.APIView):
|
||||
''' Endpoint: Upload RS form in Exteor format. '''
|
||||
serializer_class = s.FileSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
permission_classes = [permissions.GlobalUser]
|
||||
|
||||
@extend_schema(
|
||||
summary='import TRS file into RSForm',
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import cast
|
|||
|
||||
from django.http import HttpResponse
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
from rest_framework import status as c
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action, api_view, permission_classes
|
||||
|
@ -11,25 +11,22 @@ from rest_framework.request import Request
|
|||
from rest_framework.response import Response
|
||||
|
||||
from .. import models as m
|
||||
from .. import permissions
|
||||
from .. import serializers as s
|
||||
from .. import utils
|
||||
|
||||
|
||||
@extend_schema(tags=['Version'])
|
||||
@extend_schema_view()
|
||||
class VersionViewset(viewsets.GenericViewSet, generics.RetrieveUpdateDestroyAPIView):
|
||||
class VersionViewset(
|
||||
viewsets.GenericViewSet,
|
||||
generics.RetrieveUpdateDestroyAPIView,
|
||||
permissions.EditorMixin
|
||||
):
|
||||
''' Endpoint: Get / Update Constituenta. '''
|
||||
queryset = m.Version.objects.all()
|
||||
serializer_class = s.VersionSerializer
|
||||
|
||||
def get_permissions(self):
|
||||
result = super().get_permissions()
|
||||
if self.request.method.upper() == 'GET':
|
||||
result.append(permissions.AllowAny())
|
||||
else:
|
||||
result.append(utils.ItemOwnerOrAdmin())
|
||||
return result
|
||||
|
||||
@extend_schema(
|
||||
summary='restore version data into current item',
|
||||
request=None,
|
||||
|
@ -62,7 +59,7 @@ class VersionViewset(viewsets.GenericViewSet, generics.RetrieveUpdateDestroyAPIV
|
|||
}
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([permissions.IsAuthenticated])
|
||||
@permission_classes([permissions.GlobalUser])
|
||||
def create_version(request: Request, pk_item: int):
|
||||
''' Endpoint: Create new version for RSForm copying current content. '''
|
||||
try:
|
||||
|
|
|
@ -246,13 +246,6 @@ export function deleteLibraryItem(target: string, request: FrontAction) {
|
|||
});
|
||||
}
|
||||
|
||||
export function postClaimLibraryItem(target: string, request: FrontPull<ILibraryItem>) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/library/${target}/claim`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function postSubscribe(target: string, request: FrontAction) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/library/${target}/subscribe`,
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
patchSubstituteConstituents,
|
||||
patchUploadTRS,
|
||||
patchVersion,
|
||||
postClaimLibraryItem,
|
||||
postCreateVersion,
|
||||
postNewConstituenta,
|
||||
postSubscribe
|
||||
|
@ -63,7 +62,6 @@ interface IRSFormContext {
|
|||
isSubscribed: boolean;
|
||||
|
||||
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void;
|
||||
claim: (callback?: DataCallback<ILibraryItem>) => void;
|
||||
subscribe: (callback?: () => void) => void;
|
||||
unsubscribe: (callback?: () => void) => void;
|
||||
download: (callback: DataCallback<Blob>) => void;
|
||||
|
@ -180,29 +178,6 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps)
|
|||
[schemaID, setError, setSchema, schema, library]
|
||||
);
|
||||
|
||||
const claim = useCallback(
|
||||
(callback?: DataCallback<ILibraryItem>) => {
|
||||
if (!schema || !user) {
|
||||
return;
|
||||
}
|
||||
setError(undefined);
|
||||
postClaimLibraryItem(schemaID, {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onSuccess: newData => {
|
||||
setSchema(Object.assign(schema, newData));
|
||||
library.localUpdateItem(newData);
|
||||
if (!user.subscriptions.includes(newData.id)) {
|
||||
user.subscriptions.push(newData.id);
|
||||
}
|
||||
if (callback) callback(newData);
|
||||
}
|
||||
});
|
||||
},
|
||||
[schemaID, setError, schema, user, setSchema, library]
|
||||
);
|
||||
|
||||
const subscribe = useCallback(
|
||||
(callback?: () => void) => {
|
||||
if (!schema || !user) {
|
||||
|
@ -543,7 +518,6 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps)
|
|||
update,
|
||||
download,
|
||||
upload,
|
||||
claim,
|
||||
restoreOrder,
|
||||
resetAliases,
|
||||
produceStructure,
|
||||
|
|
|
@ -2,15 +2,7 @@
|
|||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import {
|
||||
IconDestroy,
|
||||
IconDownload,
|
||||
IconFollow,
|
||||
IconFollowOff,
|
||||
IconOwner,
|
||||
IconSave,
|
||||
IconShare
|
||||
} from '@/components/Icons';
|
||||
import { IconDestroy, IconDownload, IconFollow, IconFollowOff, IconSave, IconShare } from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
|
@ -28,7 +20,7 @@ interface RSFormToolbarProps {
|
|||
onDestroy: () => void;
|
||||
}
|
||||
|
||||
function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, onDestroy }: RSFormToolbarProps) {
|
||||
function RSFormToolbar({ modified, anonymous, subscribed, onSubmit, onDestroy }: RSFormToolbarProps) {
|
||||
const controller = useRSEdit();
|
||||
const canSave = useMemo(() => modified && !controller.isProcessing, [modified, controller.isProcessing]);
|
||||
return (
|
||||
|
@ -65,14 +57,6 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
|
|||
onClick={controller.toggleSubscribe}
|
||||
/>
|
||||
) : null}
|
||||
{!anonymous && claimable ? (
|
||||
<MiniButton
|
||||
title='Стать владельцем'
|
||||
icon={<IconOwner size='1.25rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={controller.claim}
|
||||
/>
|
||||
) : null}
|
||||
{controller.isMutable ? (
|
||||
<MiniButton
|
||||
title='Удалить схему'
|
||||
|
|
|
@ -80,7 +80,6 @@ interface IRSEditContext {
|
|||
promptTemplate: () => void;
|
||||
promptClone: () => void;
|
||||
promptUpload: () => void;
|
||||
claim: () => void;
|
||||
share: () => void;
|
||||
toggleSubscribe: () => void;
|
||||
download: () => void;
|
||||
|
@ -488,13 +487,6 @@ export const RSEditState = ({
|
|||
});
|
||||
}, [model, isModified]);
|
||||
|
||||
const claim = useCallback(() => {
|
||||
if (!window.confirm('Вы уверены, что хотите стать владельцем данной схемы?')) {
|
||||
return;
|
||||
}
|
||||
model.claim(() => toast.success('Вы стали владельцем схемы'));
|
||||
}, [model]);
|
||||
|
||||
const share = useCallback(() => {
|
||||
const currentRef = window.location.href;
|
||||
const url = currentRef.includes('?') ? currentRef + '&share' : currentRef + '?share';
|
||||
|
@ -547,7 +539,6 @@ export const RSEditState = ({
|
|||
promptClone,
|
||||
promptUpload: () => setShowUpload(true),
|
||||
download,
|
||||
claim,
|
||||
share,
|
||||
toggleSubscribe,
|
||||
|
||||
|
|
|
@ -52,11 +52,6 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
|||
const editMenu = useDropdown();
|
||||
const accessMenu = useDropdown();
|
||||
|
||||
function handleClaimOwner() {
|
||||
editMenu.hide();
|
||||
controller.claim();
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
schemaMenu.hide();
|
||||
onDestroy();
|
||||
|
@ -140,14 +135,6 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
|||
onClick={schemaMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||
{user ? (
|
||||
<DropdownButton
|
||||
text={model.isOwned ? 'Вы — владелец' : 'Стать владельцем'}
|
||||
icon={<IconOwner size='1rem' className='icon-green' />}
|
||||
disabled={!model.isClaimable && !model.isOwned}
|
||||
onClick={!model.isOwned && model.isClaimable ? handleClaimOwner : undefined}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Поделиться'
|
||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||
|
|
Loading…
Reference in New Issue
Block a user