From f95f24231fbf6b8f0cba70670ddf05b3a3c03f0f Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Mon, 5 Feb 2024 15:12:56 +0530 Subject: [PATCH] dev: comment assets --- apiserver/plane/app/urls/issue.py | 13 ++++ apiserver/plane/app/urls/page.py | 11 ++++ apiserver/plane/app/views/__init__.py | 2 + apiserver/plane/app/views/issue.py | 63 ++++++++++++++++++ apiserver/plane/app/views/page.py | 92 ++++++++++++++++++++++++--- 5 files changed, 172 insertions(+), 9 deletions(-) diff --git a/apiserver/plane/app/urls/issue.py b/apiserver/plane/app/urls/issue.py index a098dee70..f09b7270f 100644 --- a/apiserver/plane/app/urls/issue.py +++ b/apiserver/plane/app/urls/issue.py @@ -21,6 +21,7 @@ from plane.app.views import ( IssueArchiveViewSet, IssueRelationViewSet, IssueDraftViewSet, + CommentAssetEndpoint, ) @@ -313,4 +314,16 @@ urlpatterns = [ ), name="project-issue-draft", ), + # Comment Assets + path( + "workspaces//projects//comments//attachments/", + CommentAssetEndpoint.as_view(), + name="project-comment-attachments", + ), + path( + "workspaces//projects//comments//attachments///", + CommentAssetEndpoint.as_view(), + name="project-comment-attachments", + ), + ## End Comments ] diff --git a/apiserver/plane/app/urls/page.py b/apiserver/plane/app/urls/page.py index 58cec2cd4..b23c4d6da 100644 --- a/apiserver/plane/app/urls/page.py +++ b/apiserver/plane/app/urls/page.py @@ -6,6 +6,7 @@ from plane.app.views import ( PageFavoriteViewSet, PageLogEndpoint, SubPagesEndpoint, + PageAssetEndpoint, ) @@ -130,4 +131,14 @@ urlpatterns = [ SubPagesEndpoint.as_view(), name="sub-page", ), + path( + "workspaces//projects//pages//attachments/", + PageAssetEndpoint.as_view(), + name="page-assets", + ), + path( + "workspaces//projects//pages//attachments///", + PageAssetEndpoint.as_view(), + name="page-assets", + ), ] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 3061b8300..65a6bf205 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -89,6 +89,7 @@ from .issue import ( IssueReactionViewSet, IssueRelationViewSet, IssueDraftViewSet, + CommentAssetEndpoint, ) from .auth_extended import ( @@ -141,6 +142,7 @@ from .page import ( PageFavoriteViewSet, PageLogEndpoint, SubPagesEndpoint, + PageAssetEndpoint, ) from .search import GlobalSearchEndpoint, IssueSearchEndpoint diff --git a/apiserver/plane/app/views/issue.py b/apiserver/plane/app/views/issue.py index 9adf25abb..8f8ead4c3 100644 --- a/apiserver/plane/app/views/issue.py +++ b/apiserver/plane/app/views/issue.py @@ -1926,3 +1926,66 @@ class IssueDraftViewSet(BaseViewSet): origin=request.META.get("HTTP_ORIGIN"), ) return Response(status=status.HTTP_204_NO_CONTENT) + + +class CommentAssetEndpoint(BaseAPIView): + permission_classes = [ + ProjectLitePermission, + ] + parser_classes = ( + MultiPartParser, + FormParser, + JSONParser, + ) + + def post(self, request, slug, project_id, comment_id): + serializer = FileAssetSerializer(data=request.data) + workspace = Workspace.objects.get(slug=slug) + if serializer.is_valid(): + serializer.save( + workspace=workspace, + project_id=project_id, + entity_type="comment", + entity_identifier=comment_id, + ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete( + self, request, slug, project_id, comment_id, workspace_id, asset_key + ): + key = f"{workspace_id}/{asset_key}" + asset = FileAsset.objects.get( + asset=key, + entity_identifier=comment_id, + entity_type="comment", + workspace__slug=slug, + project_id=project_id, + ) + asset.is_deleted = True + asset.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def get( + self, + request, + slug, + project_id, + comment_id, + workspace_id=None, + asset_key=None, + ): + if workspace_id and asset_key: + key = f"{workspace_id}/{asset_key}" + url = generate_download_presigned_url(key) + return HttpResponseRedirect(url) + + # For listing + comment_assets = FileAsset.objects.filter( + entity_type="comment", + entity_identifier=comment_id, + workspace__slug=slug, + project_id=project_id, + ) + serializer = FileAssetSerializer(comment_assets, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/page.py b/apiserver/plane/app/views/page.py index 1d8ff1fbb..d5f1e6c47 100644 --- a/apiserver/plane/app/views/page.py +++ b/apiserver/plane/app/views/page.py @@ -4,23 +4,34 @@ from datetime import date, datetime, timedelta # Django imports from django.db import connection from django.db.models import Exists, OuterRef, Q -from django.utils import timezone +from django.http import HttpResponseRedirect from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page + # Third party imports from rest_framework import status from rest_framework.response import Response - -from plane.app.permissions import ProjectEntityPermission -from plane.app.serializers import (IssueLiteSerializer, PageFavoriteSerializer, - PageLogSerializer, PageSerializer, - SubPageSerializer) -from plane.db.models import (Issue, IssueActivity, IssueAssignee, Page, - PageFavorite, PageLog, ProjectMember) +from rest_framework.parsers import MultiPartParser, FormParser, JSONParser # Module imports +from plane.app.permissions import ProjectEntityPermission +from plane.app.serializers import ( + PageFavoriteSerializer, + PageLogSerializer, + PageSerializer, + SubPageSerializer, + FileAssetSerializer, +) +from plane.db.models import ( + Workspace, + Page, + PageFavorite, + PageLog, + ProjectMember, + FileAsset, +) from .base import BaseAPIView, BaseViewSet - +from plane.utils.presigned_url_generator import generate_download_presigned_url def unarchive_archive_page_and_descendants(page_id, archived_at): # Your SQL query @@ -348,3 +359,66 @@ class SubPagesEndpoint(BaseAPIView): return Response( SubPageSerializer(pages, many=True).data, status=status.HTTP_200_OK ) + + +class PageAssetEndpoint(BaseAPIView): + permission_classes = [ + ProjectEntityPermission, + ] + parser_classes = ( + MultiPartParser, + FormParser, + JSONParser, + ) + + def post(self, request, slug, project_id, page_id): + serializer = FileAssetSerializer(data=request.data) + workspace = Workspace.objects.get(slug=slug) + if serializer.is_valid(): + serializer.save( + workspace=workspace, + project_id=project_id, + entity_type="page", + entity_identifier=page_id, + ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete( + self, request, slug, project_id, page_id, workspace_id, asset_key + ): + key = f"{workspace_id}/{asset_key}" + asset = FileAsset.objects.get( + asset=key, + entity_identifier=page_id, + entity_type="page", + workspace__slug=slug, + project_id=project_id, + ) + asset.is_deleted = True + asset.save() + return Response(status=status.HTTP_204_NO_CONTENT) + + def get( + self, + request, + slug, + project_id, + page_id, + workspace_id=None, + asset_key=None, + ): + if workspace_id and asset_key: + key = f"{workspace_id}/{asset_key}" + url = generate_download_presigned_url(key) + return HttpResponseRedirect(url) + + # For listing + page_assets = FileAsset.objects.filter( + entity_type="page", + entity_identifier=page_id, + workspace__slug=slug, + project_id=project_id, + ) + serializer = FileAssetSerializer(page_assets, many=True) + return Response(serializer.data, status=status.HTTP_200_OK)