mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'feat-description-collaboration' of https://github.com/makeplane/plane into feat-description-collaboration
This commit is contained in:
commit
ddf7ac856a
@ -22,6 +22,7 @@ from plane.app.views import (
|
|||||||
IssueDescriptionViewSet,
|
IssueDescriptionViewSet,
|
||||||
BulkIssueOperationsEndpoint,
|
BulkIssueOperationsEndpoint,
|
||||||
BulkArchiveIssuesEndpoint,
|
BulkArchiveIssuesEndpoint,
|
||||||
|
ArchivedIssueDescriptionViewSet,
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -272,6 +273,16 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
name="project-issue-archive-unarchive",
|
name="project-issue-archive-unarchive",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-issues/<uuid:pk>/description/",
|
||||||
|
ArchivedIssueDescriptionViewSet.as_view(
|
||||||
|
{
|
||||||
|
"get": "retrieve",
|
||||||
|
"patch": "partial_update",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
name="archived-issue-description",
|
||||||
|
),
|
||||||
## End Issue Archives
|
## End Issue Archives
|
||||||
## Issue Relation
|
## Issue Relation
|
||||||
path(
|
path(
|
||||||
@ -321,4 +332,14 @@ urlpatterns = [
|
|||||||
BulkIssueOperationsEndpoint.as_view(),
|
BulkIssueOperationsEndpoint.as_view(),
|
||||||
name="bulk-operations-issues",
|
name="bulk-operations-issues",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/projects/<uuid:project_id>/issue-drafts/<uuid:pk>/description/",
|
||||||
|
IssueDraftViewSet.as_view(
|
||||||
|
{
|
||||||
|
"get": "retrieve",
|
||||||
|
"patch": "partial_update",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
name="draft-issue-description",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -115,6 +115,7 @@ from .issue.activity import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .issue.archive import IssueArchiveViewSet, BulkArchiveIssuesEndpoint
|
from .issue.archive import IssueArchiveViewSet, BulkArchiveIssuesEndpoint
|
||||||
|
from .issue.archive import IssueArchiveViewSet, ArchivedIssueDescriptionViewSet
|
||||||
|
|
||||||
from .issue.attachment import (
|
from .issue.attachment import (
|
||||||
IssueAttachmentEndpoint,
|
IssueAttachmentEndpoint,
|
||||||
@ -125,7 +126,7 @@ from .issue.comment import (
|
|||||||
CommentReactionViewSet,
|
CommentReactionViewSet,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .issue.draft import IssueDraftViewSet
|
from .issue.draft import IssueDraftViewSet, DraftIssueDescriptionViewSet
|
||||||
|
|
||||||
from .issue.label import (
|
from .issue.label import (
|
||||||
LabelViewSet,
|
LabelViewSet,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
@ -12,6 +13,7 @@ from django.db.models import (
|
|||||||
Exists,
|
Exists,
|
||||||
)
|
)
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.http import StreamingHttpResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.gzip import gzip_page
|
from django.views.decorators.gzip import gzip_page
|
||||||
|
|
||||||
@ -319,56 +321,46 @@ class IssueArchiveViewSet(BaseViewSet):
|
|||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
class BulkArchiveIssuesEndpoint(BaseAPIView):
|
class ArchivedIssueDescriptionViewSet(BaseViewSet):
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
ProjectEntityPermission,
|
ProjectEntityPermission,
|
||||||
]
|
]
|
||||||
|
|
||||||
def post(self, request, slug, project_id):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
issue_ids = request.data.get("issue_ids", [])
|
issue = Issue.objects.get(
|
||||||
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
if not len(issue_ids):
|
|
||||||
return Response(
|
|
||||||
{"error": "Issue IDs are required"},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
|
||||||
)
|
|
||||||
|
|
||||||
issues = Issue.objects.filter(
|
|
||||||
workspace__slug=slug, project_id=project_id, pk__in=issue_ids
|
|
||||||
).select_related("state")
|
|
||||||
bulk_archive_issues = []
|
|
||||||
for issue in issues:
|
|
||||||
if issue.state.group not in ["completed", "cancelled"]:
|
|
||||||
return Response(
|
|
||||||
{
|
|
||||||
"error_code": 4091,
|
|
||||||
"error_message": "INVALID_ARCHIVE_STATE_GROUP"
|
|
||||||
},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
|
||||||
)
|
|
||||||
issue_activity.delay(
|
|
||||||
type="issue.activity.updated",
|
|
||||||
requested_data=json.dumps(
|
|
||||||
{
|
|
||||||
"archived_at": str(timezone.now().date()),
|
|
||||||
"automation": False,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
actor_id=str(request.user.id),
|
|
||||||
issue_id=str(issue.id),
|
|
||||||
project_id=str(project_id),
|
|
||||||
current_instance=json.dumps(
|
|
||||||
IssueSerializer(issue).data, cls=DjangoJSONEncoder
|
|
||||||
),
|
|
||||||
epoch=int(timezone.now().timestamp()),
|
|
||||||
notification=True,
|
|
||||||
origin=request.META.get("HTTP_ORIGIN"),
|
|
||||||
)
|
|
||||||
issue.archived_at = timezone.now().date()
|
|
||||||
bulk_archive_issues.append(issue)
|
|
||||||
Issue.objects.bulk_update(bulk_archive_issues, ["archived_at"])
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
{"archived_at": str(timezone.now().date())},
|
|
||||||
status=status.HTTP_200_OK,
|
|
||||||
)
|
)
|
||||||
|
binary_data = issue.description_binary
|
||||||
|
|
||||||
|
def stream_data():
|
||||||
|
if binary_data:
|
||||||
|
yield binary_data
|
||||||
|
else:
|
||||||
|
yield b""
|
||||||
|
|
||||||
|
response = StreamingHttpResponse(
|
||||||
|
stream_data(), content_type="application/octet-stream"
|
||||||
|
)
|
||||||
|
response["Content-Disposition"] = (
|
||||||
|
'attachment; filename="issue_description.bin"'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
|
issue = Issue.objects.get(
|
||||||
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
|
)
|
||||||
|
|
||||||
|
base64_data = request.data.get("description_binary")
|
||||||
|
|
||||||
|
if base64_data:
|
||||||
|
# Decode the base64 data to bytes
|
||||||
|
new_binary_data = base64.b64decode(base64_data)
|
||||||
|
|
||||||
|
# Store the updated binary data
|
||||||
|
issue.description_binary = new_binary_data
|
||||||
|
issue.description_html = request.data.get("description_html")
|
||||||
|
issue.save()
|
||||||
|
return Response({"message": "Updated successfully"})
|
||||||
|
else:
|
||||||
|
return Response({"error": "No binary data provided"})
|
||||||
|
@ -598,7 +598,7 @@ class IssueDescriptionViewSet(BaseViewSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def retrieve(self, request, slug, project_id, pk):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
issue = Issue.objects.get(
|
issue = Issue.issue_objects.get(
|
||||||
pk=pk, workspace__slug=slug, project_id=project_id
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
)
|
)
|
||||||
binary_data = issue.description_binary
|
binary_data = issue.description_binary
|
||||||
@ -618,7 +618,7 @@ class IssueDescriptionViewSet(BaseViewSet):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
issue = Issue.objects.get(
|
issue = Issue.issue_objects.get(
|
||||||
pk=pk, workspace__slug=slug, project_id=project_id
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
|
from django.http import StreamingHttpResponse
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
@ -393,3 +395,48 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
origin=request.META.get("HTTP_ORIGIN"),
|
origin=request.META.get("HTTP_ORIGIN"),
|
||||||
)
|
)
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class DraftIssueDescriptionViewSet(BaseViewSet):
|
||||||
|
permission_classes = [
|
||||||
|
ProjectEntityPermission,
|
||||||
|
]
|
||||||
|
|
||||||
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
|
issue = Issue.objects.get(
|
||||||
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
|
)
|
||||||
|
binary_data = issue.description_binary
|
||||||
|
|
||||||
|
def stream_data():
|
||||||
|
if binary_data:
|
||||||
|
yield binary_data
|
||||||
|
else:
|
||||||
|
yield b""
|
||||||
|
|
||||||
|
response = StreamingHttpResponse(
|
||||||
|
stream_data(), content_type="application/octet-stream"
|
||||||
|
)
|
||||||
|
response["Content-Disposition"] = (
|
||||||
|
'attachment; filename="issue_description.bin"'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
|
issue = Issue.objects.get(
|
||||||
|
pk=pk, workspace__slug=slug, project_id=project_id
|
||||||
|
)
|
||||||
|
|
||||||
|
base64_data = request.data.get("description_binary")
|
||||||
|
|
||||||
|
if base64_data:
|
||||||
|
# Decode the base64 data to bytes
|
||||||
|
new_binary_data = base64.b64decode(base64_data)
|
||||||
|
|
||||||
|
# Store the updated binary data
|
||||||
|
issue.description_binary = new_binary_data
|
||||||
|
issue.description_html = request.data.get("description_html")
|
||||||
|
issue.save()
|
||||||
|
return Response({"message": "Updated successfully"})
|
||||||
|
else:
|
||||||
|
return Response({"error": "No binary data provided"})
|
||||||
|
Loading…
Reference in New Issue
Block a user