Skip to content

Commit 34b3ef6

Browse files
committed
allow replies to a note (bug 888707)
1 parent 71007f1 commit 34b3ef6

File tree

5 files changed

+87
-11
lines changed

5 files changed

+87
-11
lines changed

apps/comm/models.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,10 @@
66

77
import amo.models
88
from mkt.constants import comm as const
9-
from translations.fields import TranslatedField, save_signal
9+
from translations.fields import save_signal
1010

1111

12-
class CommunicationThread(amo.models.ModelBase):
13-
addon = models.ForeignKey('addons.Addon', related_name='threads')
14-
version = models.ForeignKey('versions.Version', related_name='threads',
15-
null=True)
16-
12+
class CommunicationPermissionModel(amo.models.ModelBase):
1713
# Read permissions imply write permissions as well.
1814
read_permission_public = models.BooleanField()
1915
read_permission_developer = models.BooleanField()
@@ -22,6 +18,15 @@ class CommunicationThread(amo.models.ModelBase):
2218
read_permission_mozilla_contact = models.BooleanField()
2319
read_permission_staff = models.BooleanField()
2420

21+
class Meta:
22+
abstract = True
23+
24+
25+
class CommunicationThread(CommunicationPermissionModel):
26+
addon = models.ForeignKey('addons.Addon', related_name='threads')
27+
version = models.ForeignKey('versions.Version', related_name='threads',
28+
null=True)
29+
2530
class Meta:
2631
db_table = 'comm_threads'
2732

@@ -37,11 +42,13 @@ class Meta:
3742
unique_together = ('user', 'thread',)
3843

3944

40-
class CommunicationNote(amo.models.ModelBase):
45+
class CommunicationNote(CommunicationPermissionModel):
4146
thread = models.ForeignKey(CommunicationThread, related_name='notes')
4247
author = models.ForeignKey('users.UserProfile', related_name='comm_notes')
4348
note_type = models.IntegerField()
4449
body = models.TextField(null=True)
50+
reply_to = models.ForeignKey('self', related_name='replies', null=True,
51+
blank=True)
4552

4653
class Meta:
4754
db_table = 'comm_thread_notes'
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_public` bool NOT NULL;
2+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_developer` bool NOT NULL;
3+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_reviewer` bool NOT NULL;
4+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_senior_reviewer` bool NOT NULL;
5+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_staff` bool NOT NULL;
6+
ALTER TABLE `comm_thread_notes` ADD COLUMN `read_permission_mozilla_contact` bool NOT NULL;
7+
ALTER TABLE `comm_thread_notes` ADD COLUMN `reply_to_id` int(11) unsigned;
8+
ALTER TABLE `comm_thread_notes` ADD CONSTRAINT `reply_to_id_refs_id_df5d5709` FOREIGN KEY (`reply_to_id`) REFERENCES `comm_thread_notes` (`id`);
9+
10+
CREATE INDEX `comm_thread_notes_dev_perm` ON `comm_thread_notes` (`read_permission_developer`);
11+
CREATE INDEX `comm_threads_dev_perm` ON `comm_threads` (`read_permission_developer`);

mkt/comm/api.py

+33-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin,
1111
ListModelMixin, RetrieveModelMixin)
1212
from rest_framework.permissions import BasePermission
13-
from rest_framework.relations import RelatedField
13+
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
1414
from rest_framework.serializers import ModelSerializer, SerializerMethodField
1515

1616
from addons.models import Addon
@@ -36,11 +36,12 @@ class Meta:
3636
class NoteSerializer(ModelSerializer):
3737
body = CharField()
3838
author_meta = AuthorSerializer(source='author', read_only=True)
39+
reply_to = PrimaryKeyRelatedField(required=False)
3940

4041
class Meta:
4142
model = CommunicationNote
4243
fields = ('id', 'author', 'author_meta', 'note_type', 'body',
43-
'created', 'thread')
44+
'created', 'thread', 'reply_to')
4445

4546

4647
class AddonSerializer(ModelSerializer):
@@ -195,6 +196,13 @@ def get_serializer(self, instance=None, data=None,
195196
return super(NoteViewSet, self).get_serializer(data=data_dict,
196197
files=files, instance=instance, many=many, partial=partial)
197198

199+
def pre_save(self, obj):
200+
"""Inherit permissions from the thread."""
201+
for key in ('developer', 'reviewer', 'senior_reviewer',
202+
'mozilla_contact', 'staff'):
203+
perm = 'read_permission_%s' % key
204+
setattr(obj, perm, getattr(self.comm_thread, perm))
205+
198206

199207
class EmailCreationPermission(object):
200208
def has_permission(self, request, view):
@@ -213,3 +221,26 @@ def post_email(request):
213221

214222
consume_email.apply_async((email_body,))
215223
return Response(status=201)
224+
225+
226+
class ReplyViewSet(NoteViewSet):
227+
cors_allowed_methods = ['get', 'post']
228+
229+
def initialize_request(self, request, *args, **kwargs):
230+
self.parent_note = get_object_or_404(CommunicationNote,
231+
id=kwargs['note_id'])
232+
233+
return super(ReplyViewSet, self).initialize_request(request, *args,
234+
**kwargs)
235+
236+
def get_queryset(self):
237+
return self.parent_note.replies.all()
238+
239+
def pre_save(self, obj):
240+
"""Inherit permissions from the parent note."""
241+
for key in ('developer', 'reviewer', 'senior_reviewer',
242+
'mozilla_contact', 'staff'):
243+
perm = 'read_permission_%s' % key
244+
setattr(obj, perm, getattr(self.parent_note, perm))
245+
246+
obj.reply_to = self.parent_note

mkt/comm/tests/test_api.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def setUp(self):
154154
super(TestNote, self).setUp()
155155
addon = Webapp.objects.get(pk=337141)
156156
self.thread = CommunicationThread.objects.create(addon=addon,
157-
read_permission_developer=True)
157+
read_permission_developer=True, version=addon.current_version)
158158
self.thread_url = reverse('comm-thread-detail',
159159
kwargs={'pk': self.thread.id})
160160
self.list_url = reverse('comm-note-list',
@@ -170,6 +170,7 @@ def test_response(self):
170170
'pk': note.id}))
171171
eq_(res.status_code, 200)
172172
eq_(res.json['body'], 'something')
173+
eq_(res.json['reply_to'], None)
173174

174175
def test_creation(self):
175176
res = self.client.post(self.list_url, data=json.dumps(
@@ -188,6 +189,29 @@ def test_cors_allowed(self):
188189
res = self.client.get(self.list_url)
189190
self.assertCORS(res, 'get', 'post', 'delete')
190191

192+
def test_reply_list(self):
193+
note = CommunicationNote.objects.create(author=self.profile,
194+
thread=self.thread, note_type=0, body='something')
195+
note.replies.create(body='somethingelse', note_type=0,
196+
thread=self.thread, author=self.profile)
197+
res = self.client.get(reverse('comm-note-replies-list',
198+
kwargs={'thread_id': self.thread.id,
199+
'note_id': note.id}))
200+
eq_(res.status_code, 200)
201+
eq_(len(res.json['objects']), 1)
202+
eq_(res.json['objects'][0]['reply_to'], note.id)
203+
204+
def test_reply_create(self):
205+
note = CommunicationNote.objects.create(author=self.profile,
206+
thread=self.thread, note_type=0, body='something')
207+
res = self.client.post(reverse('comm-note-replies-list',
208+
kwargs={'thread_id': self.thread.id,
209+
'note_id': note.id}),
210+
data=json.dumps({'note_type': '0',
211+
'body': 'something'}))
212+
eq_(res.status_code, 201)
213+
eq_(note.replies.count(), 1)
214+
191215

192216
class TestEmailApi(RestOAuth):
193217

mkt/comm/urls.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
from rest_framework.routers import DefaultRouter
44

5-
from mkt.comm.api import NoteViewSet, ThreadViewSet, post_email
5+
from mkt.comm.api import NoteViewSet, post_email, ReplyViewSet, ThreadViewSet
66

77

88
api_thread = DefaultRouter()
99
api_thread.register(r'thread', ThreadViewSet, base_name='comm-thread')
1010
api_thread.register(r'thread/(?P<thread_id>\d+)/note', NoteViewSet,
1111
base_name='comm-note')
12+
api_thread.register(
13+
r'thread/(?P<thread_id>\d+)/note/(?P<note_id>\d+)/replies', ReplyViewSet,
14+
base_name='comm-note-replies')
1215

1316
api_patterns = patterns('',
1417
url(r'^comm/', include(api_thread.urls)),

0 commit comments

Comments
 (0)