diff --git a/readthedocs/comments/__init__.py b/readthedocs/comments/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/readthedocs/comments/admin.py b/readthedocs/comments/admin.py deleted file mode 100644 index debfb776022..00000000000 --- a/readthedocs/comments/admin.py +++ /dev/null @@ -1,25 +0,0 @@ -"""ModelAdmin configurations for comments app.""" - -from __future__ import absolute_import -from django.contrib import admin -from .models import DocumentNode, DocumentComment, NodeSnapshot - - -class SnapshotAdmin(admin.TabularInline): - model = NodeSnapshot - - -class DocumentNodeAdmin(admin.ModelAdmin): - search_fields = ('id', 'document') - list_filter = ('project__name',) - raw_id_fields = ('project', 'version') - list_display = ('latest_hash', 'latest_commit') - inlines = (SnapshotAdmin,) - - -class DocumentCommentAdmin(admin.ModelAdmin): - search_fields = ('text',) - raw_id_fields = ('node',) - -admin.site.register(DocumentNode, DocumentNodeAdmin) -admin.site.register(DocumentComment, DocumentCommentAdmin) diff --git a/readthedocs/comments/backend.py b/readthedocs/comments/backend.py deleted file mode 100644 index f2d26deb64f..00000000000 --- a/readthedocs/comments/backend.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Storage backends for the comments app.""" - -from __future__ import absolute_import -import json - -from django.core import serializers -from sphinx.websupport.storage import StorageBackend - -from .models import DocumentNode - - -class DjangoStorage(StorageBackend): - - """A Sphinx StorageBackend using Django.""" - - def get_metadata(self, docname, moderator=None): - ret_dict = {} - for node in DocumentNode.objects.filter(page=docname): - ret_dict[node.latest_hash()] = node.comments.count() - return ret_dict - - def get_data(self, node_id, username, moderator=None): - try: - node = DocumentNode.objects.get(snapshots__hash=node_id) - except DocumentNode.DoesNotExist: - return None - ret_comments = [] - for comment in node.comments.all(): - json_data = json.loads(serializers.serialize("json", [comment]))[0] - fields = json_data['fields'] - fields['pk'] = json_data['pk'] - ret_comments.append( - fields - ) - - return {'source': '', - 'comments': ret_comments} diff --git a/readthedocs/comments/migrations/0001_initial.py b/readthedocs/comments/migrations/0001_initial.py deleted file mode 100644 index 5a2d8c0a8f1..00000000000 --- a/readthedocs/comments/migrations/0001_initial.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - ('builds', '0001_initial'), - ('projects', '0002_add_importedfile_model'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='DocumentComment', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')), - ('rating', models.IntegerField(default=0, verbose_name='Rating')), - ('text', models.TextField(verbose_name='Text')), - ], - ), - migrations.CreateModel( - name='DocumentNode', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('page', models.CharField(max_length=255, verbose_name='Path')), - ('raw_source', models.TextField(verbose_name='Raw Source')), - ('project', models.ForeignKey(related_name='nodes', verbose_name='Project', to='projects.Project', null=True)), - ('version', models.ForeignKey(related_name='nodes', verbose_name='Version', to='builds.Version', null=True)), - ], - ), - migrations.CreateModel( - name='ModerationAction', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('decision', models.IntegerField(choices=[(0, b'No Decision'), (1, b'Publish'), (2, b'Hide')])), - ('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')), - ('comment', models.ForeignKey(related_name='moderation_actions', to='comments.DocumentComment')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'get_latest_by': 'date', - }, - ), - migrations.CreateModel( - name='ModerationActionManager', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ], - ), - migrations.CreateModel( - name='NodeSnapshot', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(auto_now_add=True, verbose_name=b'Publication date')), - ('hash', models.CharField(max_length=255, verbose_name='Hash')), - ('commit', models.CharField(max_length=255)), - ('node', models.ForeignKey(related_name='snapshots', to='comments.DocumentNode')), - ], - options={ - 'get_latest_by': 'date', - }, - ), - migrations.AddField( - model_name='documentcomment', - name='node', - field=models.ForeignKey(related_name='comments', to='comments.DocumentNode'), - ), - migrations.AddField( - model_name='documentcomment', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), - ), - migrations.AlterUniqueTogether( - name='nodesnapshot', - unique_together=set([('hash', 'node', 'commit')]), - ), - ] diff --git a/readthedocs/comments/migrations/__init__.py b/readthedocs/comments/migrations/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/readthedocs/comments/models.py b/readthedocs/comments/models.py deleted file mode 100644 index cc15734e159..00000000000 --- a/readthedocs/comments/models.py +++ /dev/null @@ -1,244 +0,0 @@ -"""Models for the comments app.""" - -from __future__ import absolute_import -from builtins import str -from builtins import object -from django.contrib.auth.models import User -from django.db import models -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers - -from readthedocs.restapi.serializers import VersionSerializer - - -class DocumentNodeManager(models.Manager): - - def create(self, *args, **kwargs): - - try: - node_hash = kwargs.pop('hash') - commit = kwargs.pop('commit') - except KeyError: - raise TypeError("You must provide a hash and commit for the initial NodeSnapshot.") - - node = super(DocumentNodeManager, self).create(*args, **kwargs) - NodeSnapshot.objects.create(commit=commit, hash=node_hash, node=node) - - return node - - def from_hash(self, version_slug, page, node_hash, project_slug=None): - """Return a node matching a given hash.""" - snapshots = NodeSnapshot.objects.filter(hash=node_hash, - node__version__slug=version_slug, - node__page=page) - - if project_slug: - snapshots = snapshots.filter(node__project__slug=project_slug) - - if not snapshots.exists(): - raise DocumentNode.DoesNotExist( - "No node exists on %s with a current hash of %s" % ( - page, node_hash)) - - if snapshots.count() == 1: - # If we have found only one snapshot, we know we have the correct node. - node = snapshots[0].node - else: - # IF we have found more than one snapshot... - number_of_nodes = len(set(snapshots.values_list('node'))) - if number_of_nodes == 1: - # ...and they're all from the same node, then we know we have the proper node. - node = snapshots[0].node - else: - # On the other hand, if they're from different nodes, then we must - # have different nodes with the same hash (and thus the same content). - raise NotImplementedError( - ''' - There is more than one node with this content on this page. - In the future, ReadTheDocs will implement an indexing feature - to allow unique identification of nodes on the same page with the same content. - ''') - return node - - -@python_2_unicode_compatible -class DocumentNode(models.Model): - - """Document node.""" - - objects = DocumentNodeManager() - - project = models.ForeignKey('projects.Project', verbose_name=_('Project'), - related_name='nodes', null=True) - version = models.ForeignKey('builds.Version', verbose_name=_('Version'), - related_name='nodes', null=True) - page = models.CharField(_('Path'), max_length=255) - - raw_source = models.TextField(_('Raw Source')) - - def __str__(self): - return "node %s on %s for %s" % (self.id, self.page, self.project) - - def latest_hash(self): - return self.snapshots.latest().hash - - def latest_commit(self): - return self.snapshots.latest().commit - - def visible_comments(self): - if not self.project.comment_moderation: - return self.comments.all() - # non-optimal SQL warning. - decisions = ModerationAction.objects.filter( - comment__node=self, - decision=1, - date__gt=self.snapshots.latest().date - ) - valid_comments = self.comments.filter(moderation_actions__in=decisions).distinct() - return valid_comments - - def update_hash(self, new_hash, commit): - latest_snapshot = self.snapshots.latest() - if latest_snapshot.hash == new_hash and latest_snapshot.commit == commit: - return latest_snapshot - return self.snapshots.create(hash=new_hash, commit=commit) - - -class DocumentNodeSerializer(serializers.ModelSerializer): - version = VersionSerializer() - - current_hash = serializers.CharField(source='latest_hash') - last_commit = serializers.CharField(source='latest_commit') - snapshots_count = serializers.CharField(source='snapshots.count') - - class Meta(object): - model = DocumentNode - exclude = ('') - - -@python_2_unicode_compatible -class NodeSnapshot(models.Model): - date = models.DateTimeField('Publication date', auto_now_add=True) - hash = models.CharField(_('Hash'), max_length=255) - node = models.ForeignKey(DocumentNode, related_name="snapshots") - commit = models.CharField(max_length=255) - - class Meta(object): - get_latest_by = 'date' - # Snapshots are *almost* unique_together just for node and hash, - # but for the possibility that a node's hash might change and then change back - # in a later commit. - unique_together = ("hash", "node", "commit") - - def __str__(self): - return self.hash - - -# class DocumentCommentManager(models.Manager): -# -# def visible(self, inquiring_user=None, node=None): -# if node: -# -# decisions = ModerationAction.objects.filter( -# comment__node=node, -# decision=1, -# date__gt=self.snapshots.latest().date -# ) -# valid_comments = node.comments.filter(moderation_actions__in=decisions).distinct() -# -# if not self.project.comment_moderation: -# return self.comments.all() -# else: -# non-optimal SQL warning. -# -# return valid_comments - - -@python_2_unicode_compatible -class DocumentComment(models.Model): - - """Comment on a ``DocumentNode`` by a user.""" - - date = models.DateTimeField(_('Date'), auto_now_add=True) - rating = models.IntegerField(_('Rating'), default=0) - text = models.TextField(_('Text')) - user = models.ForeignKey(User) - node = models.ForeignKey(DocumentNode, related_name='comments') - - def __str__(self): - return "%s - %s" % (self.text, self.node) - - def get_absolute_url(self): - return "/%s" % self.node.latest_hash() - - def moderate(self, user, decision): - return self.moderation_actions.create(user=user, decision=decision) - - def has_been_approved_since_most_recent_node_change(self): - try: - latest_moderation_action = self.moderation_actions.latest() - except ModerationAction.DoesNotExist: - # If we have no moderation actions, obviously we're not approved. - return False - - most_recent_node_change = self.node.snapshots.latest().date - - if latest_moderation_action.date > most_recent_node_change: - # If we do have an approval action which is newer than the most recent change, - # we'll return True or False commensurate with its "approved" attribute. - return latest_moderation_action.approved() - return False - - def is_orphaned(self): - raise NotImplementedError('TODO') - - -class DocumentCommentSerializer(serializers.ModelSerializer): - node = DocumentNodeSerializer() - - class Meta(object): - model = DocumentComment - fields = ('date', 'user', 'text', 'node') - - def perform_create(self): - pass - - -@python_2_unicode_compatible -class ModerationActionManager(models.Model): - - def __str__(self): - return str(self.id) - - def current_approvals(self): - # pylint: disable=unused-variable - most_recent_change = self.comment.node.snapshots.latest().date # noqa - - -@python_2_unicode_compatible -class ModerationAction(models.Model): - user = models.ForeignKey(User) - comment = models.ForeignKey(DocumentComment, related_name="moderation_actions") - decision = models.IntegerField(choices=( - (0, 'No Decision'), - (1, 'Publish'), - (2, 'Hide'), - )) - date = models.DateTimeField(_('Date'), auto_now_add=True) - - def __str__(self): - return "%s - %s" % (self.user_id, self.get_decision_display()) - - class Meta(object): - get_latest_by = 'date' - - def approved(self): - return self.decision == 1 - - -class ModerationActionSerializer(serializers.ModelSerializer): - - class Meta(object): - model = ModerationAction - exclude = () diff --git a/readthedocs/comments/session.py b/readthedocs/comments/session.py deleted file mode 100644 index 16076de4e92..00000000000 --- a/readthedocs/comments/session.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Authentication backends for DRF.""" - -from __future__ import absolute_import -from rest_framework.authentication import SessionAuthentication - - -class UnsafeSessionAuthentication(SessionAuthentication): - - def authenticate(self, request): - http_request = request._request # pylint: disable=protected-access - user = getattr(http_request, 'user', None) - - if not user or not user.is_active: - return None - - return (user, None) diff --git a/readthedocs/comments/static/_static/ajax-loader.gif b/readthedocs/comments/static/_static/ajax-loader.gif deleted file mode 100644 index 61faf8cab23..00000000000 Binary files a/readthedocs/comments/static/_static/ajax-loader.gif and /dev/null differ diff --git a/readthedocs/comments/static/_static/comment-bright.png b/readthedocs/comments/static/_static/comment-bright.png deleted file mode 100644 index 551517b8c83..00000000000 Binary files a/readthedocs/comments/static/_static/comment-bright.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/comment-close.png b/readthedocs/comments/static/_static/comment-close.png deleted file mode 100644 index 09b54be46da..00000000000 Binary files a/readthedocs/comments/static/_static/comment-close.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/comment.png b/readthedocs/comments/static/_static/comment.png deleted file mode 100644 index 92feb52b882..00000000000 Binary files a/readthedocs/comments/static/_static/comment.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/down-pressed.png b/readthedocs/comments/static/_static/down-pressed.png deleted file mode 100644 index 6f7ad782782..00000000000 Binary files a/readthedocs/comments/static/_static/down-pressed.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/down.png b/readthedocs/comments/static/_static/down.png deleted file mode 100644 index 3003a88770d..00000000000 Binary files a/readthedocs/comments/static/_static/down.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/file.png b/readthedocs/comments/static/_static/file.png deleted file mode 100644 index d18082e397e..00000000000 Binary files a/readthedocs/comments/static/_static/file.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/minus.png b/readthedocs/comments/static/_static/minus.png deleted file mode 100644 index da1c5620d10..00000000000 Binary files a/readthedocs/comments/static/_static/minus.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/plus.png b/readthedocs/comments/static/_static/plus.png deleted file mode 100644 index b3cb37425ea..00000000000 Binary files a/readthedocs/comments/static/_static/plus.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/up-pressed.png b/readthedocs/comments/static/_static/up-pressed.png deleted file mode 100644 index 8bd587afee2..00000000000 Binary files a/readthedocs/comments/static/_static/up-pressed.png and /dev/null differ diff --git a/readthedocs/comments/static/_static/up.png b/readthedocs/comments/static/_static/up.png deleted file mode 100644 index b94625680b4..00000000000 Binary files a/readthedocs/comments/static/_static/up.png and /dev/null differ diff --git a/readthedocs/comments/templates/base.html b/readthedocs/comments/templates/base.html deleted file mode 100644 index 6ebaf40297d..00000000000 --- a/readthedocs/comments/templates/base.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - {% block title %}{% endblock %} - {% block css %} - {{ sg.css|safe }} - {% endblock %} - - {% block js %} - {{ sg.script|safe }} - {% endblock %} - - - {% block relbar1 %} - - {% endblock %} - -
-
-
-
- {% block body %}{% endblock %} -
-
-
- {% block sidebar %}{% endblock %} -
- - {% block relbar2 %}{% endblock %} - -{%- block footer %} - -{%- endblock %} - - - diff --git a/readthedocs/comments/templates/doc.html b/readthedocs/comments/templates/doc.html deleted file mode 100644 index fe33dad7f73..00000000000 --- a/readthedocs/comments/templates/doc.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "docbase.html" %} - -{% block title %} - {{ document.title|striptags }} -{% endblock %} - -{% block css %} - {{ document.css|safe }} -{% endblock %} - -{% block js %} - {{ document.script|safe }} -{% endblock %} - -{% block relbar1 %} - {{ document.relbar|safe }} -{% endblock %} - -{% block body %} - {{ document.body|safe }} -{% endblock %} - -{% block content %} - {{ document.body|safe }} -{% endblock %} - -{% block sidebar %} - {{ document.sidebar|safe }} -{% endblock %} - -{% block relbar2 %} - {{ document.relbar|safe }} -{% endblock %} diff --git a/readthedocs/comments/templates/docbase.html b/readthedocs/comments/templates/docbase.html deleted file mode 100644 index 70832116bfe..00000000000 --- a/readthedocs/comments/templates/docbase.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - {% block title %}{% endblock %} - {% block css %} - {{ sg.css|safe }} - {% endblock %} - - {% block js %} - {{ sg.script|safe }} - {% endblock %} - - - {% block relbar1 %} - - {% endblock %} - -
-
-
-
- {% block body %}{% endblock %} -
-
-
- {% block sidebar %}{% endblock %} -
- - {% block relbar2 %}{% endblock %} - -{% block footer %} - -{% endblock %} - - - diff --git a/readthedocs/comments/tests.py b/readthedocs/comments/tests.py deleted file mode 100644 index e585808adc4..00000000000 --- a/readthedocs/comments/tests.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from __future__ import absolute_import -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/readthedocs/comments/urls.py b/readthedocs/comments/urls.py deleted file mode 100644 index 7567f37c51f..00000000000 --- a/readthedocs/comments/urls.py +++ /dev/null @@ -1,17 +0,0 @@ -"""URL configuration for comments app.""" - -from __future__ import absolute_import -from django.conf.urls import url - -from readthedocs.comments import views - -urlpatterns = [ - url(r'build', views.build, name='build'), - url(r'_has_node', views.has_node, name='has_node'), - url(r'_add_node', views.add_node, name='add_node'), - url(r'_update_node', views.update_node, name='update_node'), - url(r'_attach_comment', views.attach_comment, name='attach_comment'), - url(r'_get_metadata', views.get_metadata, name='get_metadata'), - url(r'_get_options', views.get_options, name='get_options'), - url(r'(?P.*)', views.serve_file, name='serve_file'), -] diff --git a/readthedocs/comments/views.py b/readthedocs/comments/views.py deleted file mode 100644 index 6c4a807fe34..00000000000 --- a/readthedocs/comments/views.py +++ /dev/null @@ -1,216 +0,0 @@ -# -*- coding: utf-8 -*- -"""Views for comments app.""" - -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - -from django.contrib.auth.decorators import login_required -from django.shortcuts import render -from django.utils.decorators import method_decorator -from rest_framework import permissions, status -from rest_framework.decorators import ( - api_view, authentication_classes, detail_route, permission_classes, - renderer_classes) -from rest_framework.exceptions import ParseError -from rest_framework.renderers import JSONRenderer -from rest_framework.response import Response -from rest_framework.viewsets import ModelViewSet -from sphinx.websupport import WebSupport - -from readthedocs.comments.models import ( - DocumentComment, DocumentCommentSerializer, DocumentNode, - DocumentNodeSerializer, ModerationActionSerializer, NodeSnapshot) -from readthedocs.projects.models import Project -from readthedocs.restapi.permissions import CommentModeratorOrReadOnly - -from .backend import DjangoStorage -from .session import UnsafeSessionAuthentication - -storage = DjangoStorage() - -support = WebSupport( - srcdir='/Users/eric/projects/readthedocs.org/docs', - builddir='/Users/eric/projects/readthedocs.org/docs/_build/websupport', - datadir='/Users/eric/projects/readthedocs.org/docs/_build/websupport/data', - storage=storage, - docroot='websupport', -) - -######## -# called by javascript -######## - - -@api_view(['GET']) -@permission_classes([permissions.IsAuthenticatedOrReadOnly]) -@renderer_classes((JSONRenderer,)) -def get_options(request): # pylint: disable=unused-argument - base_opts = support.base_comment_opts - base_opts['addCommentURL'] = '/api/v2/comments/' - base_opts['getCommentsURL'] = '/api/v2/comments/' - return Response(base_opts) - - -@api_view(['GET']) -@permission_classes([permissions.IsAuthenticatedOrReadOnly]) -@renderer_classes((JSONRenderer,)) -def get_metadata(request): - """ - Check for get_metadata. - - GET: page - """ - document = request.GET.get('page', '') - return Response(storage.get_metadata(docname=document)) - - -@api_view(['GET', 'POST']) -@permission_classes([permissions.AllowAny]) -@authentication_classes([UnsafeSessionAuthentication]) -@renderer_classes((JSONRenderer,)) -def attach_comment(request): - comment_id = request.POST.get('comment', '') - comment = DocumentComment.objects.get(pk=comment_id) - - node_id = request.POST.get('node', '') - snapshot = NodeSnapshot.objects.get(hash=node_id) - comment.node = snapshot.node - - serialized_comment = DocumentCommentSerializer(comment) - return Response(serialized_comment.data) - - -####### -# Normal Views -####### - - -def build(request): # pylint: disable=unused-argument - support.build() - - -def serve_file(request, file): # pylint: disable=redefined-builtin - document = support.get_document(file) - - return render(request, 'doc.html', {'document': document}) - - -###### -# Called by Builder -###### - - -@api_view(['GET']) -@permission_classes([permissions.IsAuthenticatedOrReadOnly]) -def has_node(request): - """ - Checks to see if a node exists. - - GET: node_id - The node's ID to check - """ - node_id = request.GET.get('node_id', '') - exists = storage.has_node(node_id) - return Response({'exists': exists}) - - -@api_view(['GET', 'POST']) -@permission_classes([permissions.AllowAny]) -@authentication_classes([UnsafeSessionAuthentication]) -@renderer_classes((JSONRenderer,)) -def add_node(request): - post_data = request.data - project = Project.objects.get(slug=post_data['project']) - page = post_data.get('document', '') - node_hash = post_data.get('id', '') - version = post_data.get('version', '') - commit = post_data.get('commit', '') - project.add_node(node_hash, page, version=version, commit=commit) - return Response() - - -@api_view(['GET', 'POST']) -@permission_classes([permissions.AllowAny]) -@authentication_classes([UnsafeSessionAuthentication]) -@renderer_classes((JSONRenderer,)) -def update_node(request): - post_data = request.data - try: - old_hash = post_data['old_hash'] - new_hash = post_data['new_hash'] - commit = post_data['commit'] - project = post_data['project'] - version = post_data['version'] - page = post_data['page'] - - node = DocumentNode.objects.from_hash( - node_hash=old_hash, project_slug=project, version_slug=version, - page=page) - - node.update_hash(new_hash, commit) - return Response(DocumentNodeSerializer(node).data) - except KeyError: - return Response( - 'You must include new_hash and commit in POST payload to this view.', - status.HTTP_400_BAD_REQUEST) - - -class CommentViewSet(ModelViewSet): - - """Viewset for Comment model.""" - - serializer_class = DocumentCommentSerializer - permission_classes = [ - CommentModeratorOrReadOnly, - permissions.IsAuthenticatedOrReadOnly, - ] - - def get_queryset(self): - qp = self.request.query_params - if qp.get('node'): - try: - node = DocumentNode.objects.from_hash( - version_slug=qp['version'], - page=qp['document_page'], - node_hash=qp['node'], - project_slug=qp['project'], - ) - queryset = DocumentComment.objects.filter(node=node) - - except KeyError: - raise ParseError( - 'To get comments by node, you must also provide page, ' - 'version, and project.') - except DocumentNode.DoesNotExist: - queryset = DocumentComment.objects.none() - elif qp.get('project'): - queryset = DocumentComment.objects.filter( - node__project__slug=qp['project']) - - else: - queryset = DocumentComment.objects.all() - return queryset - - @method_decorator(login_required) - def create(self, request, *args, **kwargs): - project = Project.objects.get(slug=request.data['project']) - comment = project.add_comment( - version_slug=request.data['version'], - page=request.data['document_page'], - content_hash=request.data['node'], - commit=request.data['commit'], - user=request.user, - text=request.data['text'], - ) - - serializer = self.get_serializer(comment) - headers = self.get_success_headers(serializer.data) - return Response( - serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - @detail_route(methods=['put']) - def moderate(self, request, pk): # pylint: disable=unused-argument - comment = self.get_object() - decision = request.data['decision'] - moderation_action = comment.moderate(request.user, decision) - - return Response(ModerationActionSerializer(moderation_action).data)