mirror of
https://github.com/mediacms-io/mediacms.git
synced 2024-11-24 17:24:43 +01:00
adds drf-yasg and automated generation of Swagger Schemas (#165)
* adds drf-yasg and automated generation of Swagger Schemas * swagger url * swagger docs * adds swagger url on Readme * swagger API * Code of Conduct file * doc
This commit is contained in:
parent
110695ae2f
commit
5602422d29
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Issue report
|
||||||
|
about: Create a report to help us improve MediaCMS
|
||||||
|
title: ''
|
||||||
|
labels: 'issue: bug'
|
||||||
|
assignees: mgogoulos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the issue**
|
||||||
|
A clear and concise description of what the issue is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the issue:
|
||||||
|
1. Go to ...
|
||||||
|
2. Perform action ...
|
||||||
|
3. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Environment (please complete the following information):**
|
||||||
|
- OS: [e.g. Ubuntu Linux]
|
||||||
|
- Installation method: [Docker install, or single server install]
|
||||||
|
- Browser, if applicable
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea
|
||||||
|
title: ''
|
||||||
|
labels: 'issue: enhancement'
|
||||||
|
assignees: mgogoulos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the feature you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
13
CODE_OF_CONDUCT.md
Normal file
13
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Contributor Code of Conduct
|
||||||
|
|
||||||
|
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||||
|
|
||||||
|
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html
|
24
README.md
24
README.md
@ -158,28 +158,13 @@ This software uses the following list of awesome technologies:
|
|||||||
|
|
||||||
## Who is using it
|
## Who is using it
|
||||||
|
|
||||||
- **EngageMedia** non-profit media, technology and culture organization - https://video.engagemedia.org
|
- **Cinemata** non-profit media, technology and culture organization - https://cinemata.org
|
||||||
|
|
||||||
- **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org
|
- **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org
|
||||||
|
|
||||||
- **Heritales** International Heritage Film Festival - https://stage.heritales.org
|
- **Heritales** International Heritage Film Festival - https://stage.heritales.org
|
||||||
|
|
||||||
|
|
||||||
## Thanks To
|
|
||||||
|
|
||||||
- **Anna Helme**, for such a great partnership all these years!
|
|
||||||
|
|
||||||
- **Steve Anderson**, for trusting us and helping the Wordgames team make this real.
|
|
||||||
|
|
||||||
- **Andrew Lowenthal, King Catoy, Rezwan Islam** and the rest of the great team of [Engage Media](https://engagemedia.org).
|
|
||||||
|
|
||||||
- **Ioannis Korovesis, Ioannis Maistros, Diomidis Spinellis and Theodoros Karounos**, for their mentorship all these years, their contribution to science and the promotion of open source and free software technologies.
|
|
||||||
|
|
||||||
- **Antonis Ikonomou**, for hosting us on the excellent [Innovathens](https://www.innovathens.gr) space.
|
|
||||||
|
|
||||||
- **Werner Robitza**, for helping us with ffmpeg related stuff.
|
|
||||||
|
|
||||||
|
|
||||||
## How to contribute
|
## How to contribute
|
||||||
|
|
||||||
If you like the project, here's a few things you can do
|
If you like the project, here's a few things you can do
|
||||||
@ -190,6 +175,13 @@ If you like the project, here's a few things you can do
|
|||||||
- Open issues, participate on discussions, report bugs, suggest ideas
|
- Open issues, participate on discussions, report bugs, suggest ideas
|
||||||
- Star the project
|
- Star the project
|
||||||
- Add functionality, work on a PR, fix an issue!
|
- Add functionality, work on a PR, fix an issue!
|
||||||
|
|
||||||
|
## Developers info
|
||||||
|
|
||||||
|
- API documentation available under /swagger URL (example https://demo.mediacms.io/swagger/)
|
||||||
|
- We're working on proper documentation for users, managers and developers, until then checkout what's available on the docs/ folder of this repository
|
||||||
- Before you send a PR, make sure your code is properly formatted. For that, use `pre-commit install` to install a pre-commit hook and run `pre-commit run --all` and fix everything before you commit. This pre-commit will check for your code lint everytime you commit a code.
|
- Before you send a PR, make sure your code is properly formatted. For that, use `pre-commit install` to install a pre-commit hook and run `pre-commit run --all` and fix everything before you commit. This pre-commit will check for your code lint everytime you commit a code.
|
||||||
|
- Checkout the [Code of conduct page](CODE_OF_CONDUCT.md) if you want to contribute to this repository
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
info@mediacms.io
|
info@mediacms.io
|
||||||
|
@ -292,6 +292,7 @@ INSTALLED_APPS = [
|
|||||||
"uploader.apps.UploaderConfig",
|
"uploader.apps.UploaderConfig",
|
||||||
"djcelery_email",
|
"djcelery_email",
|
||||||
"ckeditor",
|
"ckeditor",
|
||||||
|
"drf_yasg",
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@ -423,6 +424,7 @@ CELERY_BEAT_SCHEDULE = {
|
|||||||
# TODO: beat, delete chunks from media root
|
# TODO: beat, delete chunks from media root
|
||||||
# chunks_dir after xx days...(also uploads_dir)
|
# chunks_dir after xx days...(also uploads_dir)
|
||||||
|
|
||||||
|
|
||||||
LOCAL_INSTALL = False
|
LOCAL_INSTALL = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
15
cms/urls.py
15
cms/urls.py
@ -1,7 +1,17 @@
|
|||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path
|
from django.urls import path, re_path
|
||||||
|
from drf_yasg import openapi
|
||||||
|
from drf_yasg.views import get_schema_view
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
|
||||||
|
schema_view = get_schema_view(
|
||||||
|
openapi.Info(title="MediaCMS API", default_version='v1', contact=openapi.Contact(url="https://mediacms.io"), x_logo={"url": "../../static/images/logo_dark.svg"}),
|
||||||
|
public=True,
|
||||||
|
permission_classes=(AllowAny,),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^__debug__/", include(debug_toolbar.urls)),
|
url(r"^__debug__/", include(debug_toolbar.urls)),
|
||||||
@ -10,4 +20,7 @@ urlpatterns = [
|
|||||||
url(r"^accounts/", include("allauth.urls")),
|
url(r"^accounts/", include("allauth.urls")),
|
||||||
url(r"^api-auth/", include("rest_framework.urls")),
|
url(r"^api-auth/", include("rest_framework.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
|
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||||
|
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||||
|
path('docs/api/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from drf_yasg import openapi as openapi
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.parsers import JSONParser
|
from rest_framework.parsers import JSONParser
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -23,6 +25,17 @@ class MediaList(APIView):
|
|||||||
permission_classes = (IsMediacmsEditor,)
|
permission_classes = (IsMediacmsEditor,)
|
||||||
parser_classes = (JSONParser,)
|
parser_classes = (JSONParser,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='sort_by', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Sort by any of: title, add_date, edit_date, views, likes, reported_times'),
|
||||||
|
openapi.Parameter(name='ordering', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Order by: asc, desc'),
|
||||||
|
openapi.Parameter(name='state', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Media state, options: private", "public", "unlisted'),
|
||||||
|
openapi.Parameter(name='encoding_status', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='Encoding status, options "pending", "running", "fail", "success"'),
|
||||||
|
],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Manage Media',
|
||||||
|
operation_description='Manage media for MediaCMS managers and reviewers',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
ordering = params.get("ordering", "").strip()
|
ordering = params.get("ordering", "").strip()
|
||||||
@ -94,6 +107,12 @@ class MediaList(APIView):
|
|||||||
serializer = MediaSerializer(page, many=True, context={"request": request})
|
serializer = MediaSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Delete Media',
|
||||||
|
operation_description='Delete media for MediaCMS managers and reviewers',
|
||||||
|
)
|
||||||
def delete(self, request, format=None):
|
def delete(self, request, format=None):
|
||||||
tokens = request.GET.get("tokens")
|
tokens = request.GET.get("tokens")
|
||||||
if tokens:
|
if tokens:
|
||||||
@ -112,6 +131,12 @@ class CommentList(APIView):
|
|||||||
permission_classes = (IsMediacmsEditor,)
|
permission_classes = (IsMediacmsEditor,)
|
||||||
parser_classes = (JSONParser,)
|
parser_classes = (JSONParser,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Manage Comments',
|
||||||
|
operation_description='Manage comments for MediaCMS managers and reviewers',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
ordering = params.get("ordering", "").strip()
|
ordering = params.get("ordering", "").strip()
|
||||||
@ -137,6 +162,12 @@ class CommentList(APIView):
|
|||||||
serializer = CommentSerializer(page, many=True, context={"request": request})
|
serializer = CommentSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Delete Comments',
|
||||||
|
operation_description='Delete comments for MediaCMS managers and reviewers',
|
||||||
|
)
|
||||||
def delete(self, request, format=None):
|
def delete(self, request, format=None):
|
||||||
comment_ids = request.GET.get("comment_ids")
|
comment_ids = request.GET.get("comment_ids")
|
||||||
if comment_ids:
|
if comment_ids:
|
||||||
@ -156,6 +187,12 @@ class UserList(APIView):
|
|||||||
permission_classes = (IsMediacmsEditor,)
|
permission_classes = (IsMediacmsEditor,)
|
||||||
parser_classes = (JSONParser,)
|
parser_classes = (JSONParser,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Manage Users',
|
||||||
|
operation_description='Manage users for MediaCMS managers and reviewers',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
ordering = params.get("ordering", "").strip()
|
ordering = params.get("ordering", "").strip()
|
||||||
@ -187,6 +224,12 @@ class UserList(APIView):
|
|||||||
serializer = UserSerializer(page, many=True, context={"request": request})
|
serializer = UserSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Manage'],
|
||||||
|
operation_summary='Delete Users',
|
||||||
|
operation_description='Delete users for MediaCMS managers',
|
||||||
|
)
|
||||||
def delete(self, request, format=None):
|
def delete(self, request, format=None):
|
||||||
if not is_mediacms_manager(request.user):
|
if not is_mediacms_manager(request.user):
|
||||||
return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
159
files/views.py
159
files/views.py
@ -10,6 +10,8 @@ from django.db.models import Q
|
|||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
|
from drf_yasg import openapi as openapi
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import permissions, status
|
from rest_framework import permissions, status
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.parsers import (
|
from rest_framework.parsers import (
|
||||||
@ -366,6 +368,12 @@ class MediaList(APIView):
|
|||||||
permission_classes = (IsAuthorizedToAdd,)
|
permission_classes = (IsAuthorizedToAdd,)
|
||||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
# Show media
|
# Show media
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
@ -405,6 +413,12 @@ class MediaList(APIView):
|
|||||||
serializer = MediaSerializer(page, many=True, context={"request": request})
|
serializer = MediaSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, format=None):
|
def post(self, request, format=None):
|
||||||
# Add new media
|
# Add new media
|
||||||
serializer = MediaSerializer(data=request.data, context={"request": request})
|
serializer = MediaSerializer(data=request.data, context={"request": request})
|
||||||
@ -446,6 +460,12 @@ class MediaDetail(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, friendly_token, format=None):
|
def get(self, request, friendly_token, format=None):
|
||||||
# Get media details
|
# Get media details
|
||||||
password = request.GET.get("password")
|
password = request.GET.get("password")
|
||||||
@ -471,6 +491,12 @@ class MediaDetail(APIView):
|
|||||||
ret["related_media"] = related_media
|
ret["related_media"] = related_media
|
||||||
return Response(ret)
|
return Response(ret)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, friendly_token, format=None):
|
def post(self, request, friendly_token, format=None):
|
||||||
"""superuser actions
|
"""superuser actions
|
||||||
Available only to MediaCMS editors and managers
|
Available only to MediaCMS editors and managers
|
||||||
@ -521,6 +547,12 @@ class MediaDetail(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def put(self, request, friendly_token, format=None):
|
def put(self, request, friendly_token, format=None):
|
||||||
# Update a media object
|
# Update a media object
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -534,6 +566,12 @@ class MediaDetail(APIView):
|
|||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def delete(self, request, friendly_token, format=None):
|
def delete(self, request, friendly_token, format=None):
|
||||||
# Delete a media object
|
# Delete a media object
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -565,6 +603,12 @@ class MediaActions(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, friendly_token, format=None):
|
def get(self, request, friendly_token, format=None):
|
||||||
# show date and reason for each time media was reported
|
# show date and reason for each time media was reported
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -580,6 +624,12 @@ class MediaActions(APIView):
|
|||||||
|
|
||||||
return Response(ret, status=status.HTTP_200_OK)
|
return Response(ret, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, friendly_token, format=None):
|
def post(self, request, friendly_token, format=None):
|
||||||
# perform like/dislike/report actions
|
# perform like/dislike/report actions
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -609,6 +659,12 @@ class MediaActions(APIView):
|
|||||||
else:
|
else:
|
||||||
return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def delete(self, request, friendly_token, format=None):
|
def delete(self, request, friendly_token, format=None):
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
if isinstance(media, Response):
|
if isinstance(media, Response):
|
||||||
@ -639,6 +695,12 @@ class MediaSearch(APIView):
|
|||||||
|
|
||||||
parser_classes = (JSONParser,)
|
parser_classes = (JSONParser,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Search'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
query = params.get("q", "").strip().lower()
|
query = params.get("q", "").strip().lower()
|
||||||
@ -736,6 +798,12 @@ class PlaylistList(APIView):
|
|||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
|
||||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||||
paginator = pagination_class()
|
paginator = pagination_class()
|
||||||
@ -750,6 +818,12 @@ class PlaylistList(APIView):
|
|||||||
serializer = PlaylistSerializer(page, many=True, context={"request": request})
|
serializer = PlaylistSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, format=None):
|
def post(self, request, format=None):
|
||||||
serializer = PlaylistSerializer(data=request.data, context={"request": request})
|
serializer = PlaylistSerializer(data=request.data, context={"request": request})
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
@ -777,6 +851,12 @@ class PlaylistDetail(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, friendly_token, format=None):
|
def get(self, request, friendly_token, format=None):
|
||||||
playlist = self.get_playlist(friendly_token)
|
playlist = self.get_playlist(friendly_token)
|
||||||
if isinstance(playlist, Response):
|
if isinstance(playlist, Response):
|
||||||
@ -793,6 +873,12 @@ class PlaylistDetail(APIView):
|
|||||||
|
|
||||||
return Response(ret)
|
return Response(ret)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, friendly_token, format=None):
|
def post(self, request, friendly_token, format=None):
|
||||||
playlist = self.get_playlist(friendly_token)
|
playlist = self.get_playlist(friendly_token)
|
||||||
if isinstance(playlist, Response):
|
if isinstance(playlist, Response):
|
||||||
@ -803,6 +889,12 @@ class PlaylistDetail(APIView):
|
|||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def put(self, request, friendly_token, format=None):
|
def put(self, request, friendly_token, format=None):
|
||||||
playlist = self.get_playlist(friendly_token)
|
playlist = self.get_playlist(friendly_token)
|
||||||
if isinstance(playlist, Response):
|
if isinstance(playlist, Response):
|
||||||
@ -857,6 +949,12 @@ class PlaylistDetail(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Playlists'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def delete(self, request, friendly_token, format=None):
|
def delete(self, request, friendly_token, format=None):
|
||||||
playlist = self.get_playlist(friendly_token)
|
playlist = self.get_playlist(friendly_token)
|
||||||
if isinstance(playlist, Response):
|
if isinstance(playlist, Response):
|
||||||
@ -874,6 +972,7 @@ class EncodingDetail(APIView):
|
|||||||
permission_classes = (permissions.IsAdminUser,)
|
permission_classes = (permissions.IsAdminUser,)
|
||||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(auto_schema=None)
|
||||||
def post(self, request, encoding_id):
|
def post(self, request, encoding_id):
|
||||||
ret = {}
|
ret = {}
|
||||||
force = request.data.get("force", False)
|
force = request.data.get("force", False)
|
||||||
@ -999,6 +1098,7 @@ class EncodingDetail(APIView):
|
|||||||
return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
return Response({"status": "success"}, status=status.HTTP_201_CREATED)
|
return Response({"status": "success"}, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
@swagger_auto_schema(auto_schema=None)
|
||||||
def put(self, request, encoding_id, format=None):
|
def put(self, request, encoding_id, format=None):
|
||||||
encoding_file = request.data["file"]
|
encoding_file = request.data["file"]
|
||||||
encoding = Encoding.objects.filter(id=encoding_id).first()
|
encoding = Encoding.objects.filter(id=encoding_id).first()
|
||||||
@ -1016,6 +1116,15 @@ class CommentList(APIView):
|
|||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
|
||||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='page', type=openapi.TYPE_INTEGER, in_=openapi.IN_QUERY, description='Page number'),
|
||||||
|
openapi.Parameter(name='author', type=openapi.TYPE_STRING, in_=openapi.IN_QUERY, description='username'),
|
||||||
|
],
|
||||||
|
tags=['Comments'],
|
||||||
|
operation_summary='Lists Comments',
|
||||||
|
operation_description='Paginated listing of all comments',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||||
paginator = pagination_class()
|
paginator = pagination_class()
|
||||||
@ -1060,6 +1169,12 @@ class CommentDetail(APIView):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, friendly_token):
|
def get(self, request, friendly_token):
|
||||||
# list comments for a media
|
# list comments for a media
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -1072,6 +1187,12 @@ class CommentDetail(APIView):
|
|||||||
serializer = CommentSerializer(page, many=True, context={"request": request})
|
serializer = CommentSerializer(page, many=True, context={"request": request})
|
||||||
return paginator.get_paginated_response(serializer.data)
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def delete(self, request, friendly_token, uid=None):
|
def delete(self, request, friendly_token, uid=None):
|
||||||
"""Delete a comment
|
"""Delete a comment
|
||||||
Administrators, MediaCMS editors and managers,
|
Administrators, MediaCMS editors and managers,
|
||||||
@ -1091,6 +1212,12 @@ class CommentDetail(APIView):
|
|||||||
return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Media'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def post(self, request, friendly_token):
|
def post(self, request, friendly_token):
|
||||||
"""Create a comment"""
|
"""Create a comment"""
|
||||||
media = self.get_object(friendly_token)
|
media = self.get_object(friendly_token)
|
||||||
@ -1115,6 +1242,14 @@ class CommentDetail(APIView):
|
|||||||
class UserActions(APIView):
|
class UserActions(APIView):
|
||||||
parser_classes = (JSONParser,)
|
parser_classes = (JSONParser,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='action', type=openapi.TYPE_STRING, in_=openapi.IN_PATH, description='action', required=True, enum=VALID_USER_ACTIONS),
|
||||||
|
],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='List user actions',
|
||||||
|
operation_description='Lists user actions',
|
||||||
|
)
|
||||||
def get(self, request, action):
|
def get(self, request, action):
|
||||||
media = []
|
media = []
|
||||||
if action in VALID_USER_ACTIONS:
|
if action in VALID_USER_ACTIONS:
|
||||||
@ -1140,6 +1275,12 @@ class UserActions(APIView):
|
|||||||
class CategoryList(APIView):
|
class CategoryList(APIView):
|
||||||
"""List categories"""
|
"""List categories"""
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Categories'],
|
||||||
|
operation_summary='Lists Categories',
|
||||||
|
operation_description='Lists all categories',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
categories = Category.objects.filter().order_by("title")
|
categories = Category.objects.filter().order_by("title")
|
||||||
serializer = CategorySerializer(categories, many=True, context={"request": request})
|
serializer = CategorySerializer(categories, many=True, context={"request": request})
|
||||||
@ -1150,6 +1291,14 @@ class CategoryList(APIView):
|
|||||||
class TagList(APIView):
|
class TagList(APIView):
|
||||||
"""List tags"""
|
"""List tags"""
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='page', type=openapi.TYPE_INTEGER, in_=openapi.IN_QUERY, description='Page number'),
|
||||||
|
],
|
||||||
|
tags=['Tags'],
|
||||||
|
operation_summary='Lists Tags',
|
||||||
|
operation_description='Paginated listing of all tags',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
tags = Tag.objects.filter().order_by("-media_count")
|
tags = Tag.objects.filter().order_by("-media_count")
|
||||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||||
@ -1162,6 +1311,12 @@ class TagList(APIView):
|
|||||||
class EncodeProfileList(APIView):
|
class EncodeProfileList(APIView):
|
||||||
"""List encode profiles"""
|
"""List encode profiles"""
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Encoding Profiles'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
profiles = EncodeProfile.objects.all()
|
profiles = EncodeProfile.objects.all()
|
||||||
serializer = EncodeProfileSerializer(profiles, many=True, context={"request": request})
|
serializer = EncodeProfileSerializer(profiles, many=True, context={"request": request})
|
||||||
@ -1171,6 +1326,8 @@ class EncodeProfileList(APIView):
|
|||||||
class TasksList(APIView):
|
class TasksList(APIView):
|
||||||
"""List tasks"""
|
"""List tasks"""
|
||||||
|
|
||||||
|
swagger_schema = None
|
||||||
|
|
||||||
permission_classes = (permissions.IsAdminUser,)
|
permission_classes = (permissions.IsAdminUser,)
|
||||||
|
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
@ -1181,6 +1338,8 @@ class TasksList(APIView):
|
|||||||
class TaskDetail(APIView):
|
class TaskDetail(APIView):
|
||||||
"""Cancel a task"""
|
"""Cancel a task"""
|
||||||
|
|
||||||
|
swagger_schema = None
|
||||||
|
|
||||||
permission_classes = (permissions.IsAdminUser,)
|
permission_classes = (permissions.IsAdminUser,)
|
||||||
|
|
||||||
def delete(self, request, uid, format=None):
|
def delete(self, request, uid, format=None):
|
||||||
|
@ -13,12 +13,17 @@ drf-yasg==1.20.0
|
|||||||
|
|
||||||
Pillow==8.1.1
|
Pillow==8.1.1
|
||||||
django-imagekit
|
django-imagekit
|
||||||
|
|
||||||
markdown
|
markdown
|
||||||
django-filter
|
django-filter
|
||||||
|
|
||||||
filetype
|
filetype
|
||||||
django-mptt
|
django-mptt
|
||||||
|
|
||||||
django-crispy-forms
|
django-crispy-forms
|
||||||
|
|
||||||
requests==2.25.0
|
requests==2.25.0
|
||||||
|
|
||||||
django-celery-email
|
django-celery-email
|
||||||
m3u8
|
m3u8
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ from django.contrib.auth.decorators import login_required
|
|||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from drf_yasg import openapi as openapi
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import permissions, status
|
from rest_framework import permissions, status
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
@ -131,6 +133,13 @@ def edit_channel(request, friendly_token):
|
|||||||
return render(request, "cms/channel_edit.html", {"form": form})
|
return render(request, "cms/channel_edit.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
methods=['post'],
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Contact user',
|
||||||
|
operation_description='Contact user through email, if user has set this option',
|
||||||
|
)
|
||||||
@api_view(["POST"])
|
@api_view(["POST"])
|
||||||
def contact_user(request, username):
|
def contact_user(request, username):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
@ -167,9 +176,18 @@ Sender email: %s\n
|
|||||||
|
|
||||||
|
|
||||||
class UserList(APIView):
|
class UserList(APIView):
|
||||||
|
|
||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='page', type=openapi.TYPE_INTEGER, in_=openapi.IN_QUERY, description='Page number'),
|
||||||
|
],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='List users',
|
||||||
|
operation_description='Paginated listing of users',
|
||||||
|
)
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||||
paginator = pagination_class()
|
paginator = pagination_class()
|
||||||
@ -202,6 +220,14 @@ class UserDetail(APIView):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return Response({"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='username', type=openapi.TYPE_STRING, in_=openapi.IN_PATH, description='username', required=True),
|
||||||
|
],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='List user details',
|
||||||
|
operation_description='Get user details',
|
||||||
|
)
|
||||||
def get(self, request, username, format=None):
|
def get(self, request, username, format=None):
|
||||||
# Get user details
|
# Get user details
|
||||||
user = self.get_user(username)
|
user = self.get_user(username)
|
||||||
@ -211,9 +237,24 @@ class UserDetail(APIView):
|
|||||||
serializer = UserDetailSerializer(user, context={"request": request})
|
serializer = UserDetailSerializer(user, context={"request": request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
def post(self, request, uid, format=None):
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name='username', type=openapi.TYPE_STRING, in_=openapi.IN_PATH, description='username', required=True),
|
||||||
|
],
|
||||||
|
request_body=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
'description': openapi.Schema(type=openapi.TYPE_STRING, description='description'),
|
||||||
|
'name': openapi.Schema(type=openapi.TYPE_STRING, description='name'),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Edit user details',
|
||||||
|
operation_description='Post user details - authenticated view',
|
||||||
|
)
|
||||||
|
def post(self, request, username, format=None):
|
||||||
# USER
|
# USER
|
||||||
user = self.get_user(uid)
|
user = self.get_user(username)
|
||||||
if isinstance(user, Response):
|
if isinstance(user, Response):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@ -228,6 +269,12 @@ class UserDetail(APIView):
|
|||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Xto_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def put(self, request, uid, format=None):
|
def put(self, request, uid, format=None):
|
||||||
# ADMIN
|
# ADMIN
|
||||||
user = self.get_user(uid)
|
user = self.get_user(uid)
|
||||||
@ -248,6 +295,12 @@ class UserDetail(APIView):
|
|||||||
serializer = UserDetailSerializer(user, context={"request": request})
|
serializer = UserDetailSerializer(user, context={"request": request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[],
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='to_be_written',
|
||||||
|
operation_description='to_be_written',
|
||||||
|
)
|
||||||
def delete(self, request, username, format=None):
|
def delete(self, request, username, format=None):
|
||||||
# Delete a user
|
# Delete a user
|
||||||
user = self.get_user(username)
|
user = self.get_user(username)
|
||||||
|
Loading…
Reference in New Issue
Block a user