def test_solution_notification(self, get_current): """Assert that hitting the watch toggle toggles and that proper mails are sent to anonymous and registered watchers.""" # TODO: Too monolithic. Split this test into several. get_current.return_value.domain = 'testserver' question = self._toggle_watch_question('solution', turn_on=True) QuestionSolvedEvent.notify('*****@*****.**', question) answer = question.answers.all()[0] # Post a reply self.client.login(username='******', password='******') post(self.client, 'questions.solve', args=[question.id, answer.id]) # Order of emails is not important. attrs_eq(mail.outbox[0], to=['user47963@nowhere'], subject='Solution found to Firefox Help question') starts_with(mail.outbox[0].body, SOLUTION_EMAIL % answer.id) attrs_eq(mail.outbox[1], to=['*****@*****.**'], subject='Solution found to Firefox Help question') starts_with(mail.outbox[1].body, SOLUTION_EMAIL_TO_ANONYMOUS % answer.id) self._toggle_watch_question('solution', turn_on=False)
def test_solution_notification(self, get_current): """Assert that hitting the watch toggle toggles and that proper mails are sent to anonymous and registered watchers.""" # TODO: Too monolithic. Split this test into several. get_current.return_value.domain = 'testserver' question = self._toggle_watch_question('solution', turn_on=True) QuestionSolvedEvent.notify('*****@*****.**', question) answer = question.answers.all()[0] # Post a reply self.client.login(username='******', password='******') post(self.client, 'questions.solution', args=[question.id, answer.id]) # Order of emails is not important. attrs_eq(mail.outbox[0], to=['user47963@nowhere'], subject='Solution found to Firefox Help question') starts_with(mail.outbox[0].body, SOLUTION_EMAIL % answer.id) attrs_eq(mail.outbox[1], to=['*****@*****.**'], subject='Solution found to Firefox Help question') starts_with(mail.outbox[1].body, SOLUTION_EMAIL_TO_ANONYMOUS % answer.id) self._toggle_watch_question('solution', turn_on=False)
def test_watch_solution_and_replies(self): """User subscribes to solution and replies: page doesn't break""" self.client.login(username="******", password="******") user = User.objects.get(username="******") QuestionReplyEvent.notify(user, self.question) QuestionSolvedEvent.notify(user, self.question) response = get(self.client, "questions.answers", args=[self.question.id]) eq_(200, response.status_code)
def test_watch_solution_and_replies(self): """User subscribes to solution and replies: page doesn't break""" self.client.login(username='******', password='******') user = User.objects.get(username='******') QuestionReplyEvent.notify(user, self.question) QuestionSolvedEvent.notify(user, self.question) response = get(self.client, 'questions.answers', args=[self.question.id]) eq_(200, response.status_code)
def unsubscribe_watch(request, watch_id, secret): """Stop watching a question, for anonymous users.""" watch = get_object_or_404(Watch, pk=watch_id) question = watch.content_object success = False if watch.secret == secret and isinstance(question, Question): user_or_email = watch.user or watch.email QuestionReplyEvent.stop_notifying(user_or_email, question) QuestionSolvedEvent.stop_notifying(user_or_email, question) success = True return jingo.render(request, "questions/unsubscribe_watch.html", {"question": question, "success": success})
def unsubscribe_watch(request, watch_id, secret): """Stop watching a question, for anonymous users.""" watch = get_object_or_404(Watch, pk=watch_id) question = watch.content_object success = False if watch.secret == secret and isinstance(question, Question): user_or_email = watch.user or watch.email QuestionReplyEvent.stop_notifying(user_or_email, question) QuestionSolvedEvent.stop_notifying(user_or_email, question) success = True return jingo.render(request, 'questions/unsubscribe_watch.html', {'question': question, 'success': success})
def _answers_data(request, question_id, form=None, watch_form=None, answer_preview=None): """Return a map of the minimal info necessary to draw an answers page.""" question = get_object_or_404(Question, pk=question_id) answers_ = question.answers.all() if not request.MOBILE: answers_ = paginate(request, answers_, per_page=constants.ANSWERS_PER_PAGE) feed_urls = ((reverse('questions.answers.feed', kwargs={'question_id': question_id}), AnswersFeed().title(question)),) frequencies = dict(FREQUENCY_CHOICES) is_watching_question = ( request.user.is_authenticated() and ( QuestionReplyEvent.is_notifying(request.user, question) or QuestionSolvedEvent.is_notifying(request.user, question))) return {'question': question, 'answers': answers_, 'form': form or AnswerForm(), 'answer_preview': answer_preview, 'watch_form': watch_form or _init_watch_form(request, 'reply'), 'feeds': feed_urls, 'frequencies': frequencies, 'is_watching_question': is_watching_question, 'can_tag': request.user.has_perm('questions.tag_question'), 'can_create_tags': request.user.has_perm('taggit.add_tag')}
def _answers_data(request, question_id, form=None, watch_form=None, answer_preview=None): """Return a map of the minimal info necessary to draw an answers page.""" question = get_object_or_404(Question, pk=question_id) answers_ = paginate(request, question.answers.all(), per_page=constants.ANSWERS_PER_PAGE) vocab = [t.name for t in Tag.objects.all()] # TODO: Fetch only name. feed_urls = ( (reverse("questions.answers.feed", kwargs={"question_id": question_id}), AnswersFeed().title(question)), ) frequencies = dict(FREQUENCY_CHOICES) is_watching_question = request.user.is_authenticated() and ( QuestionReplyEvent.is_notifying(request.user, question) or QuestionSolvedEvent.is_notifying(request.user, question) ) return { "question": question, "answers": answers_, "form": form or AnswerForm(), "answer_preview": answer_preview, "watch_form": watch_form or _init_watch_form(request, "reply"), "feeds": feed_urls, "tag_vocab": json.dumps(vocab), "frequencies": frequencies, "is_watching_question": is_watching_question, "can_tag": request.user.has_perm("questions.tag_question"), "can_create_tags": request.user.has_perm("taggit.add_tag"), }
def _watch_question(self): """Helper to watch a question.""" question = Question.objects.all()[0] self.client.login(username='******', password='******') user = User.objects.get(username='******') # Make sure it isn't watching yet. assert not QuestionSolvedEvent.is_notifying(user, question), ( '%s should not be notifying.' % QuestionSolvedEvent.__name__) post(self.client, 'questions.watch', {}, args=[question.id]) assert QuestionSolvedEvent.is_notifying(user, question), ( '%s should be notifying.' % QuestionSolvedEvent.__name__) return question
def _answers_data(request, question_id, form=None, watch_form=None, answer_preview=None): """Return a map of the minimal info necessary to draw an answers page.""" question = get_object_or_404(Question, pk=question_id) answers_ = paginate(request, question.answers.all(), per_page=constants.ANSWERS_PER_PAGE) vocab = [t.name for t in Tag.objects.all()] # TODO: Fetch only name. feed_urls = ((reverse('questions.answers.feed', kwargs={'question_id': question_id}), AnswersFeed().title(question)),) frequencies = dict(FREQUENCY_CHOICES) is_watching_question = ( request.user.is_authenticated() and ( QuestionReplyEvent.is_notifying(request.user, question) or QuestionSolvedEvent.is_notifying(request.user, question))) return {'question': question, 'answers': answers_, 'form': form or AnswerForm(), 'answer_preview': answer_preview, 'watch_form': watch_form or _init_watch_form(request, 'reply'), 'feeds': feed_urls, 'tag_vocab': json.dumps(vocab), 'frequencies': frequencies, 'is_watching_question': is_watching_question, 'can_tag': request.user.has_perm('questions.tag_question'), 'can_create_tags': request.user.has_perm('taggit.add_tag')}
def watch_question(request, question_id): """Start watching a question for replies or solution.""" question = get_object_or_404(Question, pk=question_id) form = WatchQuestionForm(request.user, request.POST) # Process the form msg = None if form.is_valid(): user_or_email = (request.user if request.user.is_authenticated() else form.cleaned_data['email']) try: if form.cleaned_data['event_type'] == 'reply': QuestionReplyEvent.notify(user_or_email, question) else: QuestionSolvedEvent.notify(user_or_email, question) except ActivationRequestFailed: msg = _('Could not send a message to that email address.') # Respond to ajax request if request.is_ajax(): if form.is_valid(): msg = msg or (_('You will be notified of updates by email.') if request.user.is_authenticated() else _( 'You should receive an email shortly ' 'to confirm your subscription.')) return HttpResponse(json.dumps({'message': msg})) if request.POST.get('from_vote'): tmpl = 'questions/includes/question_vote_thanks.html' else: tmpl = 'questions/includes/email_subscribe.html' html = jingo.render_to_string(request, tmpl, { 'question': question, 'watch_form': form }) return HttpResponse(json.dumps({'html': html})) # Respond to normal request if form.is_valid() and not msg: return HttpResponseRedirect(question.get_absolute_url()) return answers(request, question.id, watch_form=form, message=msg)
def watch_question(request, question_id): """Start watching a question for replies or solution.""" question = get_object_or_404(Question, pk=question_id) form = WatchQuestionForm(request.user, request.POST) # Process the form msg = None if form.is_valid(): user_or_email = (request.user if request.user.is_authenticated() else form.cleaned_data['email']) try: if form.cleaned_data['event_type'] == 'reply': QuestionReplyEvent.notify(user_or_email, question) else: QuestionSolvedEvent.notify(user_or_email, question) except ActivationRequestFailed: msg = _('Could not send message to that email address.') # Respond to ajax request if request.is_ajax(): if form.is_valid(): if not msg: msg = (_('You will be notified of updates by email.') if request.user.is_authenticated() else _('You should receive an email shortly ' 'to confirm your subscription.')) return HttpResponse(json.dumps({'message': msg})) if request.POST.get('from_vote'): tmpl = 'questions/includes/question_vote_thanks.html' else: tmpl = 'questions/includes/email_subscribe.html' html = jingo.render_to_string(request, tmpl, {'question': question, 'watch_form': form}) return HttpResponse(json.dumps({'html': html})) # Respond to normal request # TODO: show failure here if email fails to send. if form.is_valid(): return HttpResponseRedirect(question.get_absolute_url()) return answers(request, question.id, watch_form=form)
def watch_question(request, question_id): """Start watching a question for replies or solution.""" question = get_object_or_404(Question, pk=question_id) form = WatchQuestionForm(request.user, request.POST) # Process the form msg = None if form.is_valid(): user_or_email = request.user if request.user.is_authenticated() else form.cleaned_data["email"] try: if form.cleaned_data["event_type"] == "reply": QuestionReplyEvent.notify(user_or_email, question) else: QuestionSolvedEvent.notify(user_or_email, question) statsd.incr("questions.watches.new") except ActivationRequestFailed: msg = _("Could not send a message to that email address.") # Respond to ajax request if request.is_ajax(): if form.is_valid(): msg = msg or ( _("You will be notified of updates by email.") if request.user.is_authenticated() else _("You should receive an email shortly " "to confirm your subscription.") ) return HttpResponse(json.dumps({"message": msg})) if request.POST.get("from_vote"): tmpl = "questions/includes/question_vote_thanks.html" else: tmpl = "questions/includes/email_subscribe.html" html = jingo.render_to_string(request, tmpl, {"question": question, "watch_form": form}) return HttpResponse(json.dumps({"html": html})) if msg: messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(question.get_absolute_url())
def watch_question(request, question_id): """Start watching a question for replies or solution.""" question = get_object_or_404(Question, pk=question_id) form = WatchQuestionForm(request.user, request.POST) # Process the form msg = None if form.is_valid(): user_or_email = (request.user if request.user.is_authenticated() else form.cleaned_data['email']) try: QuestionSolvedEvent.notify(user_or_email, question) statsd.incr('questions.watches.new') except ActivationRequestFailed: msg = _('Could not send a message to that email address.') # Respond to ajax request if request.is_ajax(): if form.is_valid(): msg = msg or (_('You will be notified of updates by email.') if request.user.is_authenticated() else _('You should receive an email shortly ' 'to confirm your subscription.')) return HttpResponse(json.dumps({'message': msg})) html = jingo.render_to_string( request, 'questions/includes/question_vote_thanks.html', {'question': question, 'watch_form': form}) return HttpResponse(json.dumps({'html': html})) if msg: messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(question.get_absolute_url())
def test_solution_notification_deleted(self, get_current): """Calling QuestionSolvedEvent.fire() should not query the questions_question table. This test attempts to simulate the replication lag presumed to cause bug 585029. """ get_current.return_value.domain = 'testserver' answer = Answer.objects.get(pk=1) question = Question.objects.get(pk=1) question.solution = answer question.save() a_user = User.objects.get(username='******') QuestionSolvedEvent.notify(a_user, question) event = QuestionSolvedEvent(answer) # Delete the question, pretend it hasn't been replicated yet Question.objects.get(pk=question.pk).delete() event.fire(exclude=question.creator) eq_(1, len(mail.outbox))
def solution(request, question_id, answer_id): """Accept an answer as the solution to the question.""" question = get_object_or_404(Question, pk=question_id) answer = get_object_or_404(Answer, pk=answer_id) if question.is_locked: raise PermissionDenied if question.creator != request.user: return HttpResponseForbidden() question.solution = answer question.save() QuestionSolvedEvent(answer).fire(exclude=question.creator) return HttpResponseRedirect(answer.get_absolute_url())
def test_watch_solution(self, get_current): """Watch a question for solution.""" self.client.logout() get_current.return_value.domain = "testserver" post(self.client, "questions.watch", {"email": "*****@*****.**", "event_type": "solution"}, args=[self.question.id]) assert QuestionSolvedEvent.is_notifying("*****@*****.**", self.question), "Watch was not created" attrs_eq(mail.outbox[0], to=["*****@*****.**"], subject="Please confirm your email address") assert "questions/confirm/" in mail.outbox[0].body assert "Solution found" in mail.outbox[0].body # Now activate the watch. w = Watch.objects.get() get(self.client, "questions.activate_watch", args=[w.id, w.secret]) assert Watch.objects.get().is_active
def solve(request, question_id, answer_id): """Accept an answer as the solution to the question.""" question = get_object_or_404(Question, pk=question_id) # It is possible this was clicked from the email. if not request.user.is_authenticated(): watch_secret = request.GET.get('watch', None) try: watch = Watch.objects.get(secret=watch_secret, event_type='question reply', user=question.creator) # Create a new secret. distinguishable_letters = \ 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ' new_secret = ''.join(random.choice(distinguishable_letters) for x in xrange(10)) watch.update(secret=new_secret) request.user = question.creator except Watch.DoesNotExist: # This user is neither authenticated nor using the correct secret return HttpResponseForbidden() answer = get_object_or_404(Answer, pk=answer_id) if question.is_locked: raise PermissionDenied if (question.creator != request.user and not request.user.has_perm('questions.change_solution')): return HttpResponseForbidden() question.solution = answer question.save() statsd.incr('questions.solution') QuestionSolvedEvent(answer).fire(exclude=question.creator) SolutionAction(user=answer.creator, day=answer.created).save() messages.add_message(request, messages.SUCCESS, _('Thank you for choosing a solution!')) return HttpResponseRedirect(question.get_absolute_url())
def test_watch_solution(self, get_current): """Watch a question for solution.""" self.client.logout() get_current.return_value.domain = 'testserver' post(self.client, 'questions.watch', {'email': '*****@*****.**', 'event_type': 'solution'}, args=[self.question.id]) assert QuestionSolvedEvent.is_notifying('*****@*****.**', self.question), ( 'Watch was not created') attrs_eq(mail.outbox[0], to=['*****@*****.**'], subject='Please confirm your email address') assert 'questions/confirm/' in mail.outbox[0].body assert 'Solution found' in mail.outbox[0].body # Now activate the watch. w = Watch.objects.get() get(self.client, 'questions.activate_watch', args=[w.id, w.secret]) assert Watch.objects.get().is_active
def unwatch_question(request, question_id): """Stop watching a question.""" question = get_object_or_404(Question, pk=question_id) QuestionReplyEvent.stop_notifying(request.user, question) QuestionSolvedEvent.stop_notifying(request.user, question) return HttpResponseRedirect(question.get_absolute_url())