Hide empty reviews, apart from admins and your own (#5227)

This commit is contained in:
Andrew Williamson 2017-04-25 15:44:23 +01:00 коммит произвёл GitHub
Родитель 87eff8b801
Коммит b098079c45
5 изменённых файлов: 104 добавлений и 6 удалений

Просмотреть файл

@ -44,6 +44,10 @@ user has already posted a review for the current version of an add-on.
can change that with the ``filter=with_deleted`` query parameter, which can change that with the ``filter=with_deleted`` query parameter, which
requires the Addons:Edit permission. requires the Addons:Edit permission.
Only non-empty reviews (reviews with both a rating and text body) are returned,
unless a) the reviews were created by the user making the API request or
b) the user has the Addons:Edit permission.
------ ------
Detail Detail
------ ------

Просмотреть файл

@ -2,6 +2,7 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from decimal import Decimal from decimal import Decimal
import json import json
import random
import re import re
from django import test from django import test
@ -17,7 +18,7 @@ from waffle.testutils import override_switch
from olympia import amo from olympia import amo
from olympia.amo.tests import APITestClient, ESTestCase, TestCase from olympia.amo.tests import APITestClient, ESTestCase, TestCase
from olympia.amo.helpers import numberfmt, urlparams from olympia.amo.helpers import numberfmt, urlparams
from olympia.amo.tests import addon_factory, version_factory from olympia.amo.tests import addon_factory, user_factory, version_factory
from olympia.amo.urlresolvers import reverse from olympia.amo.urlresolvers import reverse
from olympia.addons.utils import generate_addon_guid from olympia.addons.utils import generate_addon_guid
from olympia.abuse.models import AbuseReport from olympia.abuse.models import AbuseReport
@ -28,6 +29,7 @@ from olympia.bandwagon.models import Collection
from olympia.constants.categories import CATEGORIES from olympia.constants.categories import CATEGORIES
from olympia.files.models import WebextPermission, WebextPermissionDescription from olympia.files.models import WebextPermission, WebextPermissionDescription
from olympia.paypal.tests.test import other_error from olympia.paypal.tests.test import other_error
from olympia.reviews.models import Review
from olympia.stats.models import Contribution from olympia.stats.models import Contribution
from olympia.users.helpers import users_list from olympia.users.helpers import users_list
from olympia.users.models import UserProfile from olympia.users.models import UserProfile
@ -1075,6 +1077,29 @@ class TestDetailPage(TestCase):
assert pq(content)('.manage-button a').eq(2).attr('href') == ( assert pq(content)('.manage-button a').eq(2).attr('href') == (
reverse('zadmin.addon_manage', args=[self.addon.slug])) reverse('zadmin.addon_manage', args=[self.addon.slug]))
def test_reviews(self):
def create_review(body='review text'):
return Review.objects.create(
addon=self.addon, user=user_factory(),
rating=random.randrange(0, 6),
body=body)
url = reverse('addons.detail', args=['a3615'])
create_review()
response = self.client.get(url, follow=True)
assert len(response.context['reviews']) == 1
# Add a new review but with no body - shouldn't be shown on detail page
create_review(body=None)
response = self.client.get(url, follow=True)
assert len(response.context['reviews']) == 1
# Test one last time in case caching
create_review()
response = self.client.get(url, follow=True)
assert len(response.context['reviews']) == 2
def get_pq(self): def get_pq(self):
return pq(self.client.get(self.url).content) return pq(self.client.get(self.url).content)

Просмотреть файл

@ -133,7 +133,7 @@ def extension_detail(request, addon):
'grouped_ratings': GroupedRating.get(addon.id), 'grouped_ratings': GroupedRating.get(addon.id),
'review_form': ReviewForm(), 'review_form': ReviewForm(),
'reviews': Review.without_replies.all().filter( 'reviews': Review.without_replies.all().filter(
addon=addon, is_latest=True), addon=addon, is_latest=True).exclude(body=None),
'get_replies': Review.get_replies, 'get_replies': Review.get_replies,
'collections': collections.order_by('-subscribers')[:3], 'collections': collections.order_by('-subscribers')[:3],
'abuse_form': AbuseForm(request=request), 'abuse_form': AbuseForm(request=request),

Просмотреть файл

@ -105,6 +105,31 @@ class TestViews(ReviewTest):
assert r.rating is None assert r.rating is None
assert item.attr('data-rating') == '' assert item.attr('data-rating') == ''
def test_empty_reviews_in_list(self):
def create_review(body='review text', user=None):
return Review.objects.create(
addon=self.addon, user=user or user_factory(),
rating=3, body=body)
url = helpers.url('addons.reviews.list', self.addon.slug)
create_review()
create_review(body=None)
create_review(
body=None,
user=UserProfile.objects.get(email='trev@adblockplus.org'))
# Don't show the reviews with no body.
assert len(self.client.get(url).context['reviews']) == 2
self.login_dev()
# Except if it's your review
assert len(self.client.get(url).context['reviews']) == 3
# Or you're an admin
self.login_admin()
assert len(self.client.get(url).context['reviews']) == 4
def test_list_rss(self): def test_list_rss(self):
r = self.client.get(helpers.url('addons.reviews.list', r = self.client.get(helpers.url('addons.reviews.list',
self.addon.slug)) self.addon.slug))
@ -926,6 +951,36 @@ class TestReviewViewSetGet(TestCase):
def test_list_addon_slug(self): def test_list_addon_slug(self):
self.test_list_addon(addon_pk=self.addon.slug) self.test_list_addon(addon_pk=self.addon.slug)
def test_list_with_empty_reviews(self):
def create_review(body='review text', user=None):
return Review.objects.create(
addon=self.addon, user=user or user_factory(),
rating=3, body=body)
self.user = user_factory()
create_review()
create_review()
create_review(body=None)
create_review(body=None, user=self.user)
# Don't show the reviews with no body.
response = self.client.get(self.url, {'addon': self.addon.pk})
data = json.loads(response.content)
assert data['count'] == 2 == len(data['results'])
# Except if it's your review
self.client.login_api(self.user)
response = self.client.get(self.url, {'addon': self.addon.pk})
data = json.loads(response.content)
assert data['count'] == 3 == len(data['results'])
# Or you're an admin
self.grant_permission(self.user, 'Addons:Edit')
response = self.client.get(self.url, {'addon': self.addon.pk})
data = json.loads(response.content)
assert data['count'] == 4 == len(data['results'])
def test_list_user(self, **kwargs): def test_list_user(self, **kwargs):
self.user = user_factory() self.user = user_factory()
review1 = Review.objects.create( review1 = Review.objects.create(

Просмотреть файл

@ -1,6 +1,6 @@
from django import http from django import http
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models import Prefetch from django.db.models import Q, Prefetch
from django.db.transaction import non_atomic_requests from django.db.transaction import non_atomic_requests
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.utils.encoding import force_text from django.utils.encoding import force_text
@ -49,6 +49,7 @@ def review_list(request, addon, review_id=None, user_id=None, template=None):
'grouped_ratings': GroupedRating.get(addon.id)} 'grouped_ratings': GroupedRating.get(addon.id)}
ctx['form'] = forms.ReviewForm(None) ctx['form'] = forms.ReviewForm(None)
is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
if review_id is not None: if review_id is not None:
ctx['page'] = 'detail' ctx['page'] = 'detail'
@ -66,13 +67,18 @@ def review_list(request, addon, review_id=None, user_id=None, template=None):
else: else:
ctx['page'] = 'list' ctx['page'] = 'list'
qs = qs.filter(is_latest=True) qs = qs.filter(is_latest=True)
# Don't filter out empty reviews for admins.
if not is_admin:
# But otherwise, filter out everyone elses empty reviews.
user_filter = (Q(user=request.user.pk)
if request.user.is_authenticated() else Q())
qs = qs.filter(~Q(body=None) | user_filter)
ctx['reviews'] = reviews = paginate(request, qs) ctx['reviews'] = reviews = paginate(request, qs)
ctx['replies'] = Review.get_replies(reviews.object_list) ctx['replies'] = Review.get_replies(reviews.object_list)
if request.user.is_authenticated(): if request.user.is_authenticated():
ctx['review_perms'] = { ctx['review_perms'] = {
'is_admin': acl.action_allowed(request, 'is_admin': is_admin,
amo.permissions.ADDONS_EDIT),
'is_editor': acl.is_editor(request, addon), 'is_editor': acl.is_editor(request, addon),
'is_author': acl.check_addon_ownership(request, addon, viewer=True, 'is_author': acl.check_addon_ownership(request, addon, viewer=True,
dev=True, support=True), dev=True, support=True),
@ -359,6 +365,8 @@ class ReviewViewSet(AddonChildMixin, ModelViewSet):
def get_queryset(self): def get_queryset(self):
requested = self.request.GET.get('filter') requested = self.request.GET.get('filter')
has_addons_edit = acl.action_allowed(self.request,
amo.permissions.ADDONS_EDIT)
# Add this as a property of the view, because we need to pass down the # Add this as a property of the view, because we need to pass down the
# information to the serializer to show/hide delete replies. # information to the serializer to show/hide delete replies.
@ -366,7 +374,7 @@ class ReviewViewSet(AddonChildMixin, ModelViewSet):
self.should_access_deleted_reviews = ( self.should_access_deleted_reviews = (
(requested == 'with_deleted' or self.action != 'list') and (requested == 'with_deleted' or self.action != 'list') and
self.request.user.is_authenticated() and self.request.user.is_authenticated() and
acl.action_allowed(self.request, amo.permissions.ADDONS_EDIT)) has_addons_edit)
should_access_only_top_level_reviews = ( should_access_only_top_level_reviews = (
self.action == 'list' and self.get_addon_object()) self.action == 'list' and self.get_addon_object())
@ -387,6 +395,12 @@ class ReviewViewSet(AddonChildMixin, ModelViewSet):
# reviews instead. # reviews instead.
self.queryset = Review.without_replies.all() self.queryset = Review.without_replies.all()
# Filter out (other user's) empty reviews for non-admins.
if self.action == 'list' and not has_addons_edit:
user_filter = (Q(user=self.request.user.pk)
if self.request.user.is_authenticated() else Q())
self.queryset = self.queryset.filter(~Q(body=None) | user_filter)
qs = super(ReviewViewSet, self).get_queryset() qs = super(ReviewViewSet, self).get_queryset()
# The serializer needs reply, version (only the "version" field) and # The serializer needs reply, version (only the "version" field) and
# user. We don't need much for version and user, so we can make joins # user. We don't need much for version and user, so we can make joins