Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deletion feature for comments #660

Open
wants to merge 12 commits into
base: dev
Choose a base branch
from
13 changes: 12 additions & 1 deletion sde_collections/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework import serializers

from .models.candidate_url import CandidateURL
from .models.collection import Collection
from .models.collection import Collection, Comments
from .models.collection_choice_fields import DocumentTypes
from .models.pattern import (
DocumentTypePattern,
Expand Down Expand Up @@ -197,3 +197,14 @@ def validate_match_pattern(self, value):
except DocumentTypePattern.DoesNotExist:
pass
return value


class CommentsSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()

def get_username(self, obj):
return obj.user.username
Comment on lines +205 to +206
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this help with?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the username who's commenting on a source. The username of the user is one of the parameters that is going to be posted with the comment.


class Meta:
model = Comments
fields = ["id", "collection", "username", "text", "created_at"]
1 change: 1 addition & 0 deletions sde_collections/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
router.register(r"title-patterns", views.TitlePatternViewSet)
router.register(r"document-type-patterns", views.DocumentTypePatternViewSet)
router.register(r"environmental-justice", EnvironmentalJusticeRowViewSet)
router.register(r"comments", views.CommentsViewSet)

app_name = "sde_collections"

Expand Down
29 changes: 22 additions & 7 deletions sde_collections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django.views.generic.detail import DetailView
from django.views.generic.edit import DeleteView
from django.views.generic.list import ListView
from rest_framework import generics, status, viewsets
from rest_framework import generics, permissions, status, viewsets
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
Expand All @@ -38,6 +38,7 @@
CandidateURLSerializer,
CollectionReadSerializer,
CollectionSerializer,
CommentsSerializer,
DocumentTypePatternSerializer,
ExcludePatternSerializer,
IncludePatternSerializer,
Expand Down Expand Up @@ -140,13 +141,9 @@ def get_context_data(self, **kwargs):
if "comments_form" not in context:
context["comments_form"] = CommentsForm()

context["required_urls"] = RequiredUrls.objects.filter(
collection=self.get_object()
)
context["required_urls"] = RequiredUrls.objects.filter(collection=self.get_object())
context["segment"] = "collection-detail"
context["comments"] = Comments.objects.filter(
collection=self.get_object()
).order_by("-created_at")
context["comments"] = Comments.objects.filter(collection=self.get_object()).order_by("-created_at")
return context


Expand Down Expand Up @@ -434,3 +431,21 @@ def get_context_data(self, **kwargs):
context["differences"] = self.data

return context


class CommentsViewSet(viewsets.ModelViewSet):
queryset = Comments.objects.all()
serializer_class = CommentsSerializer
permission_classes = [permissions.IsAuthenticated]

def get_permissions(self):
if self.action == "destroy":
return [permissions.IsAuthenticated()]
return super().get_permissions()
Comment on lines +473 to +476
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only for "destroy"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we wouldn't want anyone else other than the user who posts a comment or the admin to delete a comment. Not really necessary to grab permissions while posting because anyone the user who posts a comment is going to validated through login.


def destroy(self, request, *args, **kwargs):
comment = self.get_object()
if request.user == comment.user:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd want the admin to have privileges to delete a comment, but this works too because the user alone owns the responsibility of the comments posted.

return super().destroy(request, *args, **kwargs)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
20 changes: 20 additions & 0 deletions sde_indexing_helper/static/css/collection_detail.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,23 @@
.comment p {
margin-top: 5px;
}
.delete-button {
background-color: #ff4d4d;
color: white;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
font-family: 'Arial', sans-serif;
transition: background-color 0.3s, transform 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.delete-button:hover {
background-color: #ff3333;
transform: scale(1.05);
}

.delete-button:active {
transform: scale(0.95);
}
15 changes: 15 additions & 0 deletions sde_indexing_helper/static/js/collection_detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ document.getElementById('cancel-github-link-button').addEventListener('click', f
document.getElementById('github-link-form').style.display = 'none';
document.getElementById('id_github_issue_link').value = originalValue;
});

function deleteComment(element) {
var commentId = element.getAttribute('data-comment-id');
$.ajax({
url: `/api/comments/${commentId}/`,
type: 'DELETE',
headers: { 'X-CSRFToken': csrftoken },
success: function(result) {
$(element).closest('.comment').remove();
},
error: function(xhr, status, error) {
console.error('Comment deletion failed:', error);
}
});
}
Comment on lines +20 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ <h1>{{ collection.name }}</h1>
<strong>{{ comment.user.username }}</strong>
<span>{{ comment.created_at|date:"M. d, Y, P" }}</span>
<p>{{ comment.text }}</p>
{% if user.is_authenticated and user == comment.user or user.is_staff %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Business logic repeated here.

<button data-comment-id="{{ comment.id }}" onclick="deleteComment(this)" class="delete-button">Delete</button>
{% endif %}
</div>
{% empty %}
<p>No comments yet</p>
Expand Down Expand Up @@ -150,5 +153,8 @@ <h1>{{ collection.name }}</h1>
</table>
{% endblock content %}
{% block javascripts %}
<script type="text/javascript">
var csrftoken = '{{ csrf_token }}';
</script>
Comment on lines +156 to +158
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works.

<script src="{% static 'js/collection_detail.js' %}"></script>
{% endblock javascripts %}