From 107b1ea7dc98ea35e38c9555bbe2457c688fa000 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 19 Jun 2018 23:27:21 +0200 Subject: [PATCH] See #248: can now generate and list invitations in the front-end --- api/funkwhale_api/manage/filters.py | 9 +- api/funkwhale_api/manage/serializers.py | 37 ++++ api/funkwhale_api/manage/urls.py | 1 + api/funkwhale_api/manage/views.py | 24 +++ api/tests/manage/test_views.py | 21 ++ front/src/components/Sidebar.vue | 2 +- .../manage/users/InvitationForm.vue | 82 ++++++++ .../manage/users/InvitationsTable.vue | 180 ++++++++++++++++++ .../components/manage/users/UsersTable.vue | 2 +- front/src/router/index.js | 14 +- front/src/views/admin/users/Base.vue | 5 +- .../src/views/admin/users/InvitationsList.vue | 26 +++ 12 files changed, 395 insertions(+), 8 deletions(-) create mode 100644 front/src/components/manage/users/InvitationForm.vue create mode 100644 front/src/components/manage/users/InvitationsTable.vue create mode 100644 front/src/views/admin/users/InvitationsList.vue diff --git a/api/funkwhale_api/manage/filters.py b/api/funkwhale_api/manage/filters.py index e4cda18c5..16ee5c162 100644 --- a/api/funkwhale_api/manage/filters.py +++ b/api/funkwhale_api/manage/filters.py @@ -1,4 +1,3 @@ - from django_filters import rest_framework as filters from funkwhale_api.common import fields @@ -37,3 +36,11 @@ class ManageUserFilterSet(filters.FilterSet): "permission_settings", "permission_federation", ] + + +class ManageInvitationFilterSet(filters.FilterSet): + q = fields.SearchFilter(search_fields=["owner__username", "code", "owner__email"]) + + class Meta: + model = users_models.Invitation + fields = ["q"] diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py index 6e57db81f..e8f1e328e 100644 --- a/api/funkwhale_api/manage/serializers.py +++ b/api/funkwhale_api/manage/serializers.py @@ -78,6 +78,23 @@ class PermissionsSerializer(serializers.Serializer): return {"permissions": o} +class ManageUserSimpleSerializer(serializers.ModelSerializer): + class Meta: + model = users_models.User + fields = ( + "id", + "username", + "email", + "name", + "is_active", + "is_staff", + "is_superuser", + "date_joined", + "last_activity", + "privacy_level", + ) + + class ManageUserSerializer(serializers.ModelSerializer): permissions = PermissionsSerializer(source="*") @@ -115,3 +132,23 @@ class ManageUserSerializer(serializers.ModelSerializer): update_fields=["permission_{}".format(p) for p in permissions.keys()] ) return instance + + +class ManageInvitationSerializer(serializers.ModelSerializer): + users = ManageUserSimpleSerializer(many=True, required=False) + owner = ManageUserSimpleSerializer(required=False) + code = serializers.CharField(required=False, allow_null=True) + + class Meta: + model = users_models.Invitation + fields = ("id", "owner", "code", "expiration_date", "creation_date", "users") + read_only_fields = ["id", "expiration_date", "owner", "creation_date", "users"] + + def validate_code(self, value): + if not value: + return value + if users_models.Invitation.objects.filter(code=value.lower()).exists(): + raise serializers.ValidationError( + "An invitation with this code already exists" + ) + return value diff --git a/api/funkwhale_api/manage/urls.py b/api/funkwhale_api/manage/urls.py index f208fb857..3d4e15db9 100644 --- a/api/funkwhale_api/manage/urls.py +++ b/api/funkwhale_api/manage/urls.py @@ -7,6 +7,7 @@ library_router = routers.SimpleRouter() library_router.register(r"track-files", views.ManageTrackFileViewSet, "track-files") users_router = routers.SimpleRouter() users_router.register(r"users", views.ManageUserViewSet, "users") +users_router.register(r"invitations", views.ManageInvitationViewSet, "invitations") urlpatterns = [ url(r"^library/", include((library_router.urls, "instance"), namespace="library")), diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py index f9b78ef87..803f8db7c 100644 --- a/api/funkwhale_api/manage/views.py +++ b/api/funkwhale_api/manage/views.py @@ -62,3 +62,27 @@ class ManageUserViewSet( context = super().get_serializer_context() context["default_permissions"] = preferences.get("users__default_permissions") return context + + +class ManageInvitationViewSet( + mixins.CreateModelMixin, + mixins.ListModelMixin, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + viewsets.GenericViewSet, +): + queryset = ( + users_models.Invitation.objects.all() + .order_by("-id") + .prefetch_related("users") + .select_related("owner") + ) + serializer_class = serializers.ManageInvitationSerializer + filter_class = filters.ManageInvitationFilterSet + permission_classes = (HasUserPermission,) + required_permissions = ["settings"] + ordering_fields = ["creation_date", "expiration_date"] + + def perform_create(self, serializer): + serializer.save(owner=self.request.user) diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py index a72bcf5af..d54fca5dd 100644 --- a/api/tests/manage/test_views.py +++ b/api/tests/manage/test_views.py @@ -9,6 +9,7 @@ from funkwhale_api.manage import serializers, views [ (views.ManageTrackFileViewSet, ["library"], "and"), (views.ManageUserViewSet, ["settings"], "and"), + (views.ManageInvitationViewSet, ["settings"], "and"), ], ) def test_permissions(assert_user_permission, view, permissions, operator): @@ -42,3 +43,23 @@ def test_user_view(factories, superuser_api_client, mocker): assert response.data["count"] == len(users) assert response.data["results"] == expected + + +def test_invitation_view(factories, superuser_api_client, mocker): + invitations = factories["users.Invitation"].create_batch(size=5) + qs = invitations[0].__class__.objects.order_by("-id") + url = reverse("api:v1:manage:users:invitations-list") + + response = superuser_api_client.get(url, {"sort": "-id"}) + expected = serializers.ManageInvitationSerializer(qs, many=True).data + + assert response.data["count"] == len(invitations) + assert response.data["results"] == expected + + +def test_invitation_view_create(factories, superuser_api_client, mocker): + url = reverse("api:v1:manage:users:invitations-list") + response = superuser_api_client.post(url) + + assert response.status_code == 201 + assert superuser_api_client.user.invitations.latest("id") is not None diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue index 03ea4ee07..87c374a33 100644 --- a/front/src/components/Sidebar.vue +++ b/front/src/components/Sidebar.vue @@ -99,7 +99,7 @@ + :to="{name: 'manage.users.users.list'}"> {{ $t('Users') }} diff --git a/front/src/components/manage/users/InvitationForm.vue b/front/src/components/manage/users/InvitationForm.vue new file mode 100644 index 000000000..ffd5a7d12 --- /dev/null +++ b/front/src/components/manage/users/InvitationForm.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/front/src/components/manage/users/InvitationsTable.vue b/front/src/components/manage/users/InvitationsTable.vue new file mode 100644 index 000000000..e9b46cc2c --- /dev/null +++ b/front/src/components/manage/users/InvitationsTable.vue @@ -0,0 +1,180 @@ + + + diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue index 5658583c3..855fbe2b5 100644 --- a/front/src/components/manage/users/UsersTable.vue +++ b/front/src/components/manage/users/UsersTable.vue @@ -45,7 +45,7 @@