def test_reviewer_reply(self, send_mail_mock): # One from the reviewer. self._create(amo.LOG.REJECT_VERSION, self.reviewer) # One from the developer. self._create(amo.LOG.DEVELOPER_REPLY_VERSION, self.developer) action = amo.LOG.REVIEWER_REPLY_VERSION comments = u'Thîs ïs a revïewer replyîng' log_and_notify(action, comments, self.reviewer, self.version) logs = ActivityLog.objects.filter(action=action.id) assert len(logs) == 1 assert logs[0].details['comments'] == u'Thîs ïs a revïewer replyîng' assert send_mail_mock.call_count == 2 # Both authors. sender = '%s <notifications@%s>' % ( self.reviewer.name, settings.INBOUND_EMAIL_DOMAIN) assert sender == send_mail_mock.call_args_list[0][1]['from_email'] recipients = self._recipients(send_mail_mock) assert len(recipients) == 2 assert self.developer.email in recipients assert self.developer2.email in recipients # The reviewer who sent it doesn't get their email back. assert self.reviewer.email not in recipients self._check_email(send_mail_mock.call_args_list[0], absolutify(self.addon.get_dev_url('versions')), 'Reviewer Reply', 'you are an author of this add-on.') self._check_email(send_mail_mock.call_args_list[1], absolutify(self.addon.get_dev_url('versions')), 'Reviewer Reply', 'you are an author of this add-on.')
def test_review_url_listed(self, send_mail_mock): # One from the reviewer. self._create(amo.LOG.REJECT_VERSION, self.reviewer) # One from the developer. So the developer is on the 'thread' self._create(amo.LOG.DEVELOPER_REPLY_VERSION, self.developer) action = amo.LOG.DEVELOPER_REPLY_VERSION comments = u'Thïs is á reply' log_and_notify(action, comments, self.developer, self.version) logs = ActivityLog.objects.filter(action=action.id) assert len(logs) == 2 # We added one above. assert logs[0].details['comments'] == u'Thïs is á reply' assert send_mail_mock.call_count == 2 # One author, one reviewer. recipients = self._recipients(send_mail_mock) assert len(recipients) == 2 assert self.reviewer.email in recipients assert self.developer2.email in recipients # The developer who sent it doesn't get their email back. assert self.developer.email not in recipients self._check_email(send_mail_mock.call_args_list[0], absolutify(self.addon.get_dev_url('versions')), 'Developer Reply', 'you are an author of this add-on.') review_url = absolutify( reverse('editors.review', add_prefix=False, kwargs={'channel': 'listed', 'addon_id': self.addon.pk})) self._check_email(send_mail_mock.call_args_list[1], review_url, 'Developer Reply', 'you reviewed this add-on.')
def test_developer_reply(self, send_mail_mock): # One from the reviewer. self._create(amo.LOG.REJECT_VERSION, self.reviewer) # One from the developer. So the developer is on the 'thread' self._create(amo.LOG.DEVELOPER_REPLY_VERSION, self.developer) action = amo.LOG.DEVELOPER_REPLY_VERSION comments = u'Thïs is á reply' log_and_notify(action, comments, self.developer, self.version) logs = ActivityLog.objects.filter(action=action.id) assert len(logs) == 2 # We added one above. assert logs[0].details['comments'] == u'Thïs is á reply' assert send_mail_mock.call_count == 2 # One author, one reviewer. recipients = self._recipients(send_mail_mock) assert len(recipients) == 2 assert self.reviewer.email in recipients assert self.developer2.email in recipients # The developer who sent it doesn't get their email back. assert self.developer.email not in recipients self._check_email(send_mail_mock.call_args_list[0], absolutify(self.addon.get_dev_url('versions'))) review_url = absolutify( reverse('editors.review', args=[self.addon.pk], add_prefix=False)) self._check_email(send_mail_mock.call_args_list[1], review_url)
def _test_version(self, version, data): assert data['id'] == version.pk assert data['compatibility'] assert len(data['compatibility']) == len(version.compatible_apps) for app, compat in version.compatible_apps.items(): assert data['compatibility'][app.short] == { 'min': compat.min.version, 'max': compat.max.version } assert data['files'] assert len(data['files']) == 1 result_file = data['files'][0] file_ = version.files.latest('pk') assert result_file['id'] == file_.pk assert result_file['created'] == ( file_.created.replace(microsecond=0).isoformat() + 'Z') assert result_file['hash'] == file_.hash assert result_file['platform'] == ( amo.PLATFORM_CHOICES_API[file_.platform]) assert result_file['size'] == file_.size assert result_file['status'] == amo.STATUS_CHOICES_API[file_.status] assert result_file['url'] == file_.get_url_path(src='') assert result_file['permissions'] == file_.webext_permissions_list assert data['edit_url'] == absolutify( self.addon.get_dev_url('versions.edit', args=[version.pk], prefix_only=True)) assert data['reviewed'] == version.reviewed assert data['version'] == version.version assert data['url'] == absolutify(version.get_url_path())
def send_notification_email(self): if self.reply_to: # It's a reply. reply_url = helpers.url("addons.reviews.detail", self.addon.slug, self.reply_to.pk, add_prefix=False) data = { "name": self.addon.name, "reply_title": self.title, "reply": self.body, "reply_url": helpers.absolutify(reply_url), } recipients = [self.reply_to.user.email] subject = u"Mozilla Add-on Developer Reply: %s" % self.addon.name template = "reviews/emails/reply_review.ltxt" perm_setting = "reply" else: # It's a new review. reply_url = helpers.url("addons.reviews.reply", self.addon.slug, self.pk, add_prefix=False) data = { "name": self.addon.name, "rating": "%s out of 5 stars" % self.rating, "review": self.body, "reply_url": helpers.absolutify(reply_url), } recipients = [author.email for author in self.addon.authors.all()] subject = u"Mozilla Add-on User Review: %s" % self.addon.name template = "reviews/emails/add_review.ltxt" perm_setting = "new_review" send_mail_jinja(subject, template, Context(data), recipient_list=recipients, perm_setting=perm_setting)
def send_notification_email(self): if self.reply_to: # It's a reply. reply_url = helpers.url('addons.reviews.detail', self.addon.slug, self.reply_to.pk, add_prefix=False) data = { 'name': self.addon.name, 'reply_title': self.title, 'reply': self.body, 'reply_url': helpers.absolutify(reply_url) } recipients = [self.reply_to.user.email] subject = u'Mozilla Add-on Developer Reply: %s' % self.addon.name template = 'reviews/emails/reply_review.ltxt' perm_setting = 'reply' else: # It's a new review. reply_url = helpers.url('addons.reviews.reply', self.addon.slug, self.pk, add_prefix=False) data = { 'name': self.addon.name, 'rating': '%s out of 5 stars' % self.rating, 'review': self.body, 'reply_url': helpers.absolutify(reply_url) } recipients = [author.email for author in self.addon.authors.all()] subject = u'Mozilla Add-on User Review: %s' % self.addon.name template = 'reviews/emails/add_review.ltxt' perm_setting = 'new_review' send_mail_jinja( subject, template, Context(data), recipient_list=recipients, perm_setting=perm_setting)
def _test_version(self, version, data): assert data['id'] == version.pk assert data['compatibility'] assert len(data['compatibility']) == len(version.compatible_apps) for app, compat in version.compatible_apps.items(): assert data['compatibility'][app.short] == { 'min': compat.min.version, 'max': compat.max.version } assert data['files'] assert len(data['files']) == 1 result_file = data['files'][0] file_ = version.files.latest('pk') assert result_file['id'] == file_.pk assert result_file['created'] == file_.created.isoformat() assert result_file['hash'] == file_.hash assert result_file['platform'] == ( amo.PLATFORM_CHOICES_API[file_.platform]) assert result_file['size'] == file_.size assert result_file['status'] == amo.STATUS_CHOICES_API[file_.status] assert result_file['url'] == file_.get_url_path(src='') assert data['edit_url'] == absolutify( self.addon.get_dev_url( 'versions.edit', args=[version.pk], prefix_only=True)) assert data['reviewed'] == version.reviewed assert data['version'] == version.version assert data['url'] == absolutify(version.get_url_path())
def get_context_data(self): addon_url = self.addon.get_url_path(add_prefix=False) dev_ver_url = self.addon.get_dev_url('versions') # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): addon = Addon.unfiltered.get(pk=self.addon.pk) else: addon = self.addon return { 'name': addon.name, 'number': self.version.version if self.version else '', 'reviewer': self.user.display_name, 'addon_url': absolutify(addon_url), 'dev_versions_url': absolutify(dev_ver_url), 'review_url': absolutify( reverse('editors.review', args=[self.addon.pk], add_prefix=False)), 'comments': self.data.get('comments'), 'SITE_URL': settings.SITE_URL }
def send_notification(self, version): user_log.info('Sending addon update notice to %s for %s' % (self.user.email, self.addon.pk)) context = Context({ 'name': self.addon.name, 'url': absolutify( reverse('addons.detail', args=[self.addon.pk], add_prefix=False)), 'number': version.version, 'review': absolutify( reverse('editors.review', args=[self.addon.pk], add_prefix=False)), 'SITE_URL': settings.SITE_URL, }) # Not being localised because we don't know the editors locale. subject = 'Mozilla Add-ons: %s Updated' % self.addon.name template = loader.get_template('editors/emails/notify_update.ltxt') send_mail(subject, template.render(Context(context)), recipient_list=[self.user.email], from_email=settings.EDITORS_EMAIL, use_deny_list=False)
def test_reviewer_reply(self, send_mail_mock): # One from the reviewer. self._create(amo.LOG.REJECT_VERSION, self.reviewer) # One from the developer. self._create(amo.LOG.DEVELOPER_REPLY_VERSION, self.developer) action = amo.LOG.REVIEWER_REPLY_VERSION comments = u'Thîs ïs a revïewer replyîng' log_and_notify(action, comments, self.reviewer, self.version) logs = ActivityLog.objects.filter(action=action.id) assert len(logs) == 1 assert logs[0].details['comments'] == u'Thîs ïs a revïewer replyîng' assert send_mail_mock.call_count == 2 # Both authors. recipients = self._recipients(send_mail_mock) assert len(recipients) == 2 assert self.developer.email in recipients assert self.developer2.email in recipients # The reviewer who sent it doesn't get their email back. assert self.reviewer.email not in recipients self._check_email(send_mail_mock.call_args_list[0], absolutify(self.addon.get_dev_url('versions'))) self._check_email(send_mail_mock.call_args_list[1], absolutify(self.addon.get_dev_url('versions')))
def test_review_url_correct_channel(self): # Listed email self.helper.set_data(self.get_data()) self.helper.handler.notify_email('super_review', 'Sample subject %s, %s') assert len(mail.outbox) == 1 listed_review_url = absolutify( reverse('editors.review', args=[self.addon.pk], add_prefix=False)) assert listed_review_url in mail.outbox[0].body mail.outbox = [] # Unlisted email self.version.update(channel=amo.RELEASE_CHANNEL_UNLISTED) self.helper.set_data(self.get_data()) self.helper.handler.notify_email('super_review', 'Sample subject %s, %s') assert len(mail.outbox) == 1 unlisted_review_url = absolutify( reverse('editors.review', kwargs={ 'addon_id': self.addon.pk, 'channel': 'unlisted' }, add_prefix=False)) assert unlisted_review_url in mail.outbox[0].body
def get_context_data(self): addon_url = self.addon.get_url_path(add_prefix=False) dev_ver_url = self.addon.get_dev_url('versions') # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): addon = Addon.unfiltered.get(pk=self.addon.pk) else: addon = self.addon review_url_kw = {'addon_id': self.addon.pk} if (self.version and self.version.channel == amo.RELEASE_CHANNEL_UNLISTED): review_url_kw['channel'] = 'unlisted' return {'name': addon.name, 'number': self.version.version if self.version else '', 'reviewer': self.user.display_name, 'addon_url': absolutify(addon_url), 'dev_versions_url': absolutify(dev_ver_url), 'review_url': absolutify(reverse('editors.review', kwargs=review_url_kw, add_prefix=False)), 'comments': self.data.get('comments'), 'SITE_URL': settings.SITE_URL, 'legacy_addon': ( not self.version.all_files[0].is_webextension if self.version else False)}
def log_and_notify(action, comments, note_creator, version): log_kwargs = { 'user': note_creator, 'created': datetime.datetime.now(), } if comments: log_kwargs['details'] = { 'comments': comments, 'version': version.version} else: # Just use the name of the action if no comments provided. Alas we # can't know the locale of recipient, and our templates are English # only so prevent language jumble by forcing into en-US. with no_translation(): comments = '%s' % action.short note = amo.log(action, version.addon, version, **log_kwargs) # Collect reviewers involved with this version. review_perm = ('Review' if version.channel == amo.RELEASE_CHANNEL_LISTED else 'ReviewUnlisted') log_users = { alog.user for alog in ActivityLog.objects.for_version(version) if acl.action_allowed_user(alog.user, 'Addons', review_perm)} # Collect add-on authors (excl. the person who sent the email.) addon_authors = set(version.addon.authors.all()) - {note_creator} # Collect staff that want a copy of the email staff_cc = set( UserProfile.objects.filter(groups__name=ACTIVITY_MAIL_GROUP)) # If task_user doesn't exist that's no big issue (i.e. in tests) try: task_user = {get_task_user()} except UserProfile.DoesNotExist: task_user = set() # Collect reviewers on the thread (excl. the email sender and task user for # automated messages). reviewers = ((log_users | staff_cc) - addon_authors - task_user - {note_creator}) author_context_dict = { 'name': version.addon.name, 'number': version.version, 'author': note_creator.name, 'comments': comments, 'url': absolutify(version.addon.get_dev_url('versions')), 'SITE_URL': settings.SITE_URL, } reviewer_context_dict = author_context_dict.copy() reviewer_context_dict['url'] = absolutify( reverse('editors.review', args=[version.addon.pk], add_prefix=False)) # Not being localised because we don't know the recipients locale. subject = 'Mozilla Add-ons: %s Updated' % version.addon.name template = loader.get_template('activity/emails/developer.txt') send_activity_mail( subject, template.render(Context(author_context_dict)), version, addon_authors, settings.EDITORS_EMAIL) send_activity_mail( subject, template.render(Context(reviewer_context_dict)), version, reviewers, settings.EDITORS_EMAIL) return note
def test_dev_versions_url_in_context(self): self.helper.set_data(self.get_data()) context_data = self.helper.handler.get_context_data() assert context_data['dev_versions_url'] == absolutify( self.addon.get_dev_url('versions')) self.version.update(channel=amo.RELEASE_CHANNEL_UNLISTED) context_data = self.helper.handler.get_context_data() assert context_data['dev_versions_url'] == absolutify( reverse('devhub.addons.versions', args=[self.addon.id]))
def get_context_data(self): addon_url = self.addon.get_url_path(add_prefix=False) dev_ver_url = self.addon.get_dev_url('versions') return {'name': self.addon.name, 'number': self.version.version if self.version else '', 'reviewer': self.user.display_name, 'addon_url': absolutify(addon_url), 'dev_versions_url': absolutify(dev_ver_url), 'review_url': absolutify(reverse('editors.review', args=[self.addon.pk], add_prefix=False)), 'comments': self.data.get('comments'), 'SITE_URL': settings.SITE_URL}
def get_paykey(data): """ Gets a paykey from Paypal. Need to pass in the following in data: pattern: the reverse pattern to resolve email: who the money is going to (required) amount: the amount of money (required) ip: ip address of end user (required) uuid: contribution_uuid (required) memo: any nice message (optional) qs: anything you want to append to the complete or cancel (optional) currency: valid paypal currency, defaults to USD (optional) API Docs from Paypal are at : https://developer.paypal.com/docs/classic/api/adaptive-payments/ ("Pay"). """ if data['pattern']: complete = reverse(data['pattern'], args=[data['slug'], 'complete']) cancel = reverse(data['pattern'], args=[data['slug'], 'cancel']) else: # If there's no pattern given, just fake some urls. complete = cancel = settings.SITE_URL + '/paypal/dummy/' qs = {'uuid': data['uuid']} if 'qs' in data: qs.update(data['qs']) uuid_qs = urllib.urlencode(qs) paypal_data = { 'actionType': 'PAY', 'currencyCode': data.get('currency', 'USD'), 'cancelUrl': absolutify('%s?%s' % (cancel, uuid_qs)), 'returnUrl': absolutify('%s?%s' % (complete, uuid_qs)), 'trackingId': data['uuid'], 'ipnNotificationUrl': absolutify(reverse('amo.paypal')), 'receiverList.receiver(0).email': data['email'], 'receiverList.receiver(0).amount': data['amount'], 'receiverList.receiver(0).invoiceID': 'mozilla-%s' % data['uuid'], 'receiverList.receiver(0).paymentType': 'DIGITALGOODS', 'requestEnvelope.errorLanguage': 'US' } if data.get('memo'): paypal_data['memo'] = data['memo'] with statsd.timer('paypal.paykey.retrieval'): response = _call(settings.PAYPAL_PAY_URL + 'Pay', paypal_data, ip=data['ip']) return response['payKey'], response['paymentExecStatus']
def get_context(addon, version, job, results, fileob=None): result_links = (absolutify(reverse('devhub.bulk_compat_result', args=[addon.slug, r.pk])) for r in results) addon_name = addon.name if fileob and fileob.platform != amo.PLATFORM_ALL.id: addon_name = u'%s (%s)' % (addon_name, fileob.get_platform_display()) return Context({ 'ADDON_NAME': addon_name, 'ADDON_VERSION': version.version, 'APPLICATION': str(job.application), 'COMPAT_LINK': absolutify(reverse('devhub.versions.edit', args=[addon.pk, version.pk])), 'RESULT_LINKS': ' '.join(result_links), 'VERSION': job.target_version.version})
def _check_disco_addon(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert result['addon']['icon_url'] == absolutify( addon.get_icon_url(64)) assert (result['addon']['current_version']['files'][0]['id'] == addon.current_version.all_files[0].pk) assert '<a href="{0}">{1}</a>'.format( absolutify(addon.get_url_path()), unicode(addon.name)) in result['heading'] assert '<span>' in result['heading'] assert '</span>' in result['heading'] assert result['description']
def _check_disco_addon(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert result['addon']['icon_url'] == absolutify( addon.get_icon_url(64)) assert (result['addon']['current_version']['files'][0]['id'] == addon.current_version.all_files[0].pk) assert u'<a href="{0}">{1}</a>'.format( absolutify(addon.get_url_path()), unicode(addon.name)) in result['heading'] assert '<span>' in result['heading'] assert '</span>' in result['heading'] assert result['description']
def test_basic(self): addon = addon_factory() self.review = Review.objects.create( addon=addon, user=self.user, rating=4, version=addon.current_version, body=u'This is my rëview. Like ît?', title=u'My Review Titlé') result = self.serialize() assert result['id'] == self.review.pk assert result['addon'] == {'id': addon.pk} assert result['body'] == unicode(self.review.body) assert result['created'] == ( self.review.created.replace(microsecond=0).isoformat() + 'Z') assert result['title'] == unicode(self.review.title) assert result['previous_count'] == int(self.review.previous_count) assert result['is_latest'] == self.review.is_latest assert result['rating'] == int(self.review.rating) assert result['reply'] is None assert result['user'] == { 'id': self.user.pk, 'name': unicode(self.user.name), 'url': absolutify(self.user.get_url_path()), } assert result['version'] == { 'id': self.review.version.id, 'version': self.review.version.version } self.review.update(version=None) result = self.serialize() assert result['version'] is None
def _check_feed(self, browse_url, rss_url, sort='featured'): """ Check RSS feed URLs and that the results on the listing pages match those for their respective RSS feeds. """ # Check URLs. r = self.client.get(browse_url, follow=True) doc = pq(r.content) rss_url += '?sort=%s' % sort assert doc('link[type="application/rss+xml"]').attr('href') == rss_url assert doc('#subscribe').attr('href') == rss_url # Ensure that the RSS items match those on the browse listing pages. r = self.client.get(rss_url) rss_doc = pq(r.content) pg_items = doc('.items .item') rss_items = rss_doc('item') # We have to set `parser=xml` because of # https://github.com/gawel/pyquery/issues/93 items_urls = zip( sorted((absolutify(pq(x).find('h3 a').attr('href')), pq(x)) for x in pg_items), sorted((pq(x).find('link').text(), pq(x, parser='xml')) for x in rss_items)) for (pg_url, pg_item), (rss_url, rss_item) in items_urls: abs_url = pg_url.split('?')[0] assert rss_url.endswith(abs_url), 'Unexpected URL: %s' % abs_url if sort in ('added', 'updated'): # Check timestamps. pg_ts = pg_item.find('.updated').text().strip('Added Updated') rss_ts = rss_item.find('pubDate').text() # Look at YMD, since we don't have h:m on listing pages. assert parse_dt(pg_ts).isocalendar() == ( parse_dt(rss_ts).isocalendar())
def test_creation_triggers_email_and_logging(self): addon = Addon.objects.get(pk=4) addon_author = user_factory() addon.addonuser_set.create(user=addon_author) review_user = user_factory() review = Review.objects.create(user=review_user, addon=addon, body=u'Rêviiiiiiew', user_responsible=review_user) activity_log = ActivityLog.objects.latest('pk') assert activity_log.user == review_user assert activity_log.arguments == [addon, review] assert activity_log.action == amo.LOG.ADD_REVIEW.id assert len(mail.outbox) == 1 email = mail.outbox[0] reply_url = helpers.absolutify( helpers.url('addons.reviews.reply', addon.slug, review.pk, add_prefix=False)) assert email.subject == 'Mozilla Add-on User Review: my addon name' assert 'A user has left a review for your add-on,' in email.body assert 'my addon name' in email.body assert reply_url in email.body assert email.to == [addon_author.email] assert email.from_email == 'Mozilla Add-ons <*****@*****.**>'
def test_with_reply(self): addon = addon_factory() reply_user = user_factory() self.review = Review.objects.create( addon=addon, user=self.user, version=addon.current_version, body=u'This is my rëview. Like ît ?', title=u'My Review Titlé') reply = Review.objects.create( addon=addon, user=reply_user, version=addon.current_version, body=u'Thîs is a reply.', title=u'My rèply', reply_to=self.review) result = self.serialize() assert result['reply'] assert 'rating' not in result['reply'] assert 'reply' not in result['reply'] assert result['reply']['id'] == reply.pk assert result['reply']['body'] == unicode(reply.body) assert result['reply']['created'] == ( reply.created.replace(microsecond=0).isoformat() + 'Z') assert result['reply']['title'] == unicode(reply.title) assert result['reply']['user'] == { 'id': reply_user.pk, 'name': unicode(reply_user.name), 'url': absolutify(reply_user.get_url_path()), }
def link(self, category): """Link for the feed as a whole""" if self.category: base = url('browse.search-tools.rss', self.category.slug) else: base = url('browse.search-tools.rss') return absolutify(base + '?sort=' + self.sort)
def test_staff_cc_group_get_mail(self, send_mail_mock): self.grant_permission(self.reviewer, 'None:None', ACTIVITY_MAIL_GROUP) action = amo.LOG.DEVELOPER_REPLY_VERSION comments = u'Thïs is á reply' log_and_notify(action, comments, self.developer, self.version) logs = ActivityLog.objects.filter(action=action.id) assert len(logs) == 1 recipients = self._recipients(send_mail_mock) sender = '%s <notifications@%s>' % ( self.developer.name, settings.INBOUND_EMAIL_DOMAIN) assert sender == send_mail_mock.call_args_list[0][1]['from_email'] assert len(recipients) == 2 # self.reviewers wasn't on the thread, but gets an email anyway. assert self.reviewer.email in recipients assert self.developer2.email in recipients review_url = absolutify( reverse('editors.review', kwargs={'addon_id': self.version.addon.pk, 'channel': 'listed'}, add_prefix=False)) self._check_email(send_mail_mock.call_args_list[1], review_url, 'Developer Reply', 'you are member of the activity email cc group.')
def tally_job_results(job_id, **kw): sql = """select sum(1), sum(case when completed IS NOT NULL then 1 else 0 end) from validation_result where validation_job_id=%s""" with connection.cursor() as cursor: cursor.execute(sql, [job_id]) total, completed = cursor.fetchone() if completed == total: # The job has finished. job = ValidationJob.objects.get(pk=job_id) job.update(completed=datetime.now()) if job.finish_email: send_mail( u'Behold! Validation results for %s %s->%s' % (amo.APP_IDS[job.application].pretty, job.curr_max_version.version, job.target_version.version), textwrap.dedent(""" Aww yeah %s """ % absolutify(reverse('zadmin.validation'))), from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[job.finish_email])
def test_with_deleted_reply_but_view_allowing_it_to_be_shown(self): addon = addon_factory() reply_user = user_factory() self.review = Review.objects.create( addon=addon, user=self.user, version=addon.current_version, body=u'This is my rëview. Like ît ?', title=u'My Review Titlé') reply = Review.objects.create(addon=addon, user=reply_user, version=addon.current_version, body=u'Thîs is a reply.', title=u'My rèply', reply_to=self.review) view = Mock(spec=[], should_access_deleted_reviews=True) view.should_access_deleted_reviews = True result = self.serialize(view=view) assert result['reply'] assert 'rating' not in result['reply'] assert 'reply' not in result['reply'] assert result['reply']['id'] == reply.pk assert result['reply']['body'] == unicode(reply.body) assert result['reply']['created'] == reply.created.isoformat() assert result['reply']['title'] == unicode(reply.title) assert result['reply']['user'] == { 'name': unicode(reply_user.name), 'url': absolutify(reply_user.get_url_path()), }
def test_creation_triggers_email_and_logging(self): addon = Addon.objects.get(pk=4) addon_author = user_factory() addon.addonuser_set.create(user=addon_author) review_user = user_factory() review = Review.objects.create( user=review_user, addon=addon, body=u'Rêviiiiiiew', user_responsible=review_user) activity_log = ActivityLog.objects.latest('pk') assert activity_log.user == review_user assert activity_log.arguments == [addon, review] assert activity_log.action == amo.LOG.ADD_REVIEW.id assert len(mail.outbox) == 1 email = mail.outbox[0] reply_url = helpers.absolutify( helpers.url('addons.reviews.reply', addon.slug, review.pk, add_prefix=False)) assert email.subject == 'Mozilla Add-on User Review: my addon name' assert 'A user has left a review for your add-on,' in email.body assert 'my addon name' in email.body assert reply_url in email.body assert email.to == [addon_author.email] assert email.from_email == 'Mozilla Add-ons <*****@*****.**>'
def test_basic(self): addon = addon_factory() self.review = Review.objects.create( addon=addon, user=self.user, rating=4, version=addon.current_version, body=u'This is my rëview. Like ît?', title=u'My Review Titlé') result = self.serialize() assert result['id'] == self.review.pk assert result['body'] == unicode(self.review.body) assert result['created'] == self.review.created.isoformat() assert result['title'] == unicode(self.review.title) assert result['previous_count'] == int(self.review.previous_count) assert result['is_latest'] == self.review.is_latest assert result['rating'] == int(self.review.rating) assert result['reply'] is None assert result['user'] == { 'name': unicode(self.user.name), 'url': absolutify(self.user.get_url_path()), } assert result['version'] == self.review.version.version self.review.update(version=None) result = self.serialize() assert result['version'] is None
def reply(request, addon, review_id): is_admin = acl.action_allowed(request, 'Addons', 'Edit') is_author = acl.check_addon_ownership(request, addon, dev=True) if not (is_admin or is_author): raise PermissionDenied review = get_object_or_404(Review.objects, pk=review_id, addon=addon) form = forms.ReviewReplyForm(request.POST or None) if request.method == 'POST' and form.is_valid(): d = dict(reply_to=review, addon=addon, defaults=dict(user=request.user)) reply, new = Review.objects.get_or_create(**d) for key, val in _review_details(request, addon, form).items(): setattr(reply, key, val) reply.save() action = 'New' if new else 'Edited' log.debug('%s reply to %s: %s' % (action, review_id, reply.id)) if new: reply_url = helpers.url('addons.reviews.detail', addon.slug, review.id, add_prefix=False) data = {'name': addon.name, 'reply_title': reply.title, 'reply': reply.body, 'reply_url': helpers.absolutify(reply_url)} emails = [review.user.email] sub = u'Mozilla Add-on Developer Reply: %s' % addon.name send_mail('reviews/emails/reply_review.ltxt', sub, emails, Context(data), 'reply') return redirect(helpers.url('addons.reviews.detail', addon.slug, review_id)) ctx = dict(review=review, form=form, addon=addon) return render(request, 'reviews/reply.html', ctx)
def add(request, addon, template=None): if addon.has_author(request.user): raise PermissionDenied form = forms.ReviewForm(request.POST or None) if (request.method == 'POST' and form.is_valid() and not request.POST.get('detailed')): details = _review_details(request, addon, form) review = Review.objects.create(**details) if 'flag' in form.cleaned_data and form.cleaned_data['flag']: rf = ReviewFlag(review=review, user_id=request.user.id, flag=ReviewFlag.OTHER, note='URLs') rf.save() amo.log(amo.LOG.ADD_REVIEW, addon, review) log.debug('New review: %s' % review.id) reply_url = helpers.url('addons.reviews.reply', addon.slug, review.id, add_prefix=False) data = {'name': addon.name, 'rating': '%s out of 5 stars' % details['rating'], 'review': details['body'], 'reply_url': helpers.absolutify(reply_url)} emails = [a.email for a in addon.authors.all()] send_mail('reviews/emails/add_review.ltxt', u'Mozilla Add-on User Review: %s' % addon.name, emails, Context(data), 'new_review') return redirect(helpers.url('addons.reviews.list', addon.slug)) return render(request, template, dict(addon=addon, form=form))
def get_url(self, instance): return absolutify( reverse('signing.version', args=[ instance.addon.guid, instance.version, instance.uuid.hex ]))
def test_with_reply(self): addon = addon_factory() reply_user = user_factory() self.review = Review.objects.create( addon=addon, user=self.user, version=addon.current_version, body=u'This is my rëview. Like ît ?', title=u'My Review Titlé') reply = Review.objects.create(addon=addon, user=reply_user, version=addon.current_version, body=u'Thîs is a reply.', title=u'My rèply', reply_to=self.review) result = self.serialize() assert result['reply'] assert 'rating' not in result['reply'] assert 'reply' not in result['reply'] assert result['reply']['id'] == reply.pk assert result['reply']['body'] == unicode(reply.body) assert result['reply']['created'] == reply.created.isoformat() assert result['reply']['title'] == unicode(reply.title) assert result['reply']['user'] == { 'name': unicode(reply_user.name), 'url': absolutify(reply_user.get_url_path()), }
def get_paykey(data): """ Gets a paykey from Paypal. Need to pass in the following in data: pattern: the reverse pattern to resolve email: who the money is going to (required) amount: the amount of money (required) ip: ip address of end user (required) uuid: contribution_uuid (required) memo: any nice message (optional) qs: anything you want to append to the complete or cancel (optional) currency: valid paypal currency, defaults to USD (optional) """ if data['pattern']: complete = reverse(data['pattern'], args=[data['slug'], 'complete']) cancel = reverse(data['pattern'], args=[data['slug'], 'cancel']) else: # If there's no pattern given, just fake some urls. complete = cancel = settings.SITE_URL + '/paypal/dummy/' qs = {'uuid': data['uuid']} if 'qs' in data: qs.update(data['qs']) uuid_qs = urllib.urlencode(qs) paypal_data = { 'actionType': 'PAY', 'currencyCode': data.get('currency', 'USD'), 'cancelUrl': absolutify('%s?%s' % (cancel, uuid_qs)), 'returnUrl': absolutify('%s?%s' % (complete, uuid_qs)), 'trackingId': data['uuid'], 'ipnNotificationUrl': absolutify(reverse('amo.paypal')), 'receiverList.receiver(0).email': data['email'], 'receiverList.receiver(0).amount': data['amount'], 'receiverList.receiver(0).invoiceID': 'mozilla-%s' % data['uuid'], 'receiverList.receiver(0).primary': 'TRUE', 'receiverList.receiver(0).paymentType': 'DIGITALGOODS', 'requestEnvelope.errorLanguage': 'US' } if data.get('memo'): paypal_data['memo'] = data['memo'] with statsd.timer('paypal.paykey.retrieval'): response = _call(settings.PAYPAL_PAY_URL + 'Pay', paypal_data, ip=data['ip']) return response['payKey'], response['paymentExecStatus']
def _authenticate(self, token): url = absolutify('/api/whatever') prefix = api_settings.JWT_AUTH_HEADER_PREFIX request = self.factory.post( url, HTTP_HOST='testserver', HTTP_AUTHORIZATION='{0} {1}'.format(prefix, token)) return self.auth.authenticate(request)
def _authenticate(self, token): url = absolutify('/api/v3/whatever/') prefix = WebTokenAuthentication.auth_header_prefix request = self.factory.post( url, HTTP_HOST='testserver', HTTP_AUTHORIZATION='{0} {1}'.format(prefix, token)) return self.auth.authenticate(request)
def test_basic(self): self.addon = addon_factory( description=u'My Addôn description', file_kw={ 'hash': 'fakehash', 'size': 42, }, guid='{%s}' % uuid.uuid4(), homepage=u'https://www.example.org/', name=u'My Addôn', public_stats=True, slug='my-addon', summary=u'My Addôn summary', support_email=u'*****@*****.**', support_url=u'https://support.example.org/support/my-addon/', tags=['some_tag', 'some_other_tag'], ) result = self.serialize() version = self.addon.current_version file_ = version.files.latest('pk') assert result['id'] == self.addon.pk assert result['current_version'] assert result['current_version']['id'] == version.pk assert result['current_version']['files'] assert len(result['current_version']['files']) == 1 result_file = result['current_version']['files'][0] assert result_file['id'] == file_.pk assert result_file['created'] == file_.created.isoformat() assert result_file['hash'] == file_.hash assert result_file['platform'] == file_.get_platform_display() assert result_file['size'] == file_.size assert result_file['status'] == file_.get_status_display() assert result_file['url'] == file_.get_url_path(src='') assert result['current_version']['reviewed'] == version.reviewed assert result['current_version']['version'] == version.version assert result['default_locale'] == self.addon.default_locale assert result['description'] == {'en-US': self.addon.description} assert result['guid'] == self.addon.guid assert result['homepage'] == {'en-US': self.addon.homepage} assert result['is_listed'] == self.addon.is_listed assert result['name'] == {'en-US': self.addon.name} assert result['last_updated'] == self.addon.last_updated.isoformat() assert result['public_stats'] == self.addon.public_stats assert result['slug'] == self.addon.slug assert result['status'] == self.addon.get_status_display() assert result['summary'] == {'en-US': self.addon.summary} assert result['support_email'] == {'en-US': self.addon.support_email} assert result['support_url'] == {'en-US': self.addon.support_url} assert set(result['tags']) == set(['some_tag', 'some_other_tag']) assert result['type'] == self.addon.get_type_display() assert result['url'] == absolutify(self.addon.get_url_path()) return result
def test_basic(self): self.addon = addon_factory( description=u'My Addôn description', file_kw={ 'hash': 'fakehash', 'size': 42, }, guid='{%s}' % uuid.uuid4(), homepage=u'https://www.example.org/', name=u'My Addôn', public_stats=True, slug='my-addon', summary=u'My Addôn summary', support_email=u'*****@*****.**', support_url=u'https://support.example.org/support/my-addon/', tags=['some_tag', 'some_other_tag'], ) result = self.serialize() version = self.addon.current_version file_ = version.files.latest('pk') assert result['id'] == self.addon.pk assert result['current_version'] assert result['current_version']['id'] == version.pk assert result['current_version']['files'] assert len(result['current_version']['files']) == 1 result_file = result['current_version']['files'][0] assert result_file['id'] == file_.pk assert result_file['created'] == file_.created.isoformat() assert result_file['hash'] == file_.hash assert result_file['platform'] == file_.get_platform_display() assert result_file['size'] == file_.size assert result_file['status'] == file_.get_status_display() assert result_file['url'] == file_.get_url_path(src='') assert result['current_version']['reviewed'] == version.reviewed assert result['current_version']['version'] == version.version assert result['default_locale'] == self.addon.default_locale assert result['description'] == {'en-US': self.addon.description} assert result['guid'] == self.addon.guid assert result['homepage'] == {'en-US': self.addon.homepage} assert result['name'] == {'en-US': self.addon.name} assert result['last_updated'] == self.addon.last_updated.isoformat() assert result['public_stats'] == self.addon.public_stats assert result['slug'] == self.addon.slug assert result['status'] == self.addon.get_status_display() assert result['summary'] == {'en-US': self.addon.summary} assert result['support_email'] == {'en-US': self.addon.support_email} assert result['support_url'] == {'en-US': self.addon.support_url} assert set(result['tags']) == set(['some_tag', 'some_other_tag']) assert result['type'] == self.addon.get_type_display() assert result['url'] == absolutify(self.addon.get_url_path()) return result
def log_and_notify(action, comments, note_creator, version): log_kwargs = { 'user': note_creator, 'created': datetime.datetime.now(), 'details': { 'comments': comments, 'version': version.version } } note = amo.log(action, version.addon, version, **log_kwargs) # Collect reviewers involved with this version. review_perm = ('Review' if version.channel == amo.RELEASE_CHANNEL_LISTED else 'ReviewUnlisted') log_users = { alog.user for alog in ActivityLog.objects.for_version(version) if acl.action_allowed_user(alog.user, 'Addons', review_perm) } # Collect add-on authors (excl. the person who sent the email.) addon_authors = set(version.addon.authors.all()) - {note_creator} # Collect staff that want a copy of the email staff_cc = set( UserProfile.objects.filter(groups__name=ACTIVITY_MAIL_GROUP)) # If task_user doesn't exist that's no big issue (i.e. in tests) try: task_user = {get_task_user()} except UserProfile.DoesNotExist: task_user = set() # Collect reviewers on the thread (excl. the email sender and task user for # automated messages). reviewers = ((log_users | staff_cc) - addon_authors - task_user - {note_creator}) author_context_dict = { 'name': version.addon.name, 'number': version.version, 'author': note_creator.name, 'comments': comments, 'url': version.addon.get_dev_url('versions'), 'SITE_URL': settings.SITE_URL, } reviewer_context_dict = author_context_dict.copy() reviewer_context_dict['url'] = absolutify( reverse('editors.review', args=[version.addon.pk], add_prefix=False)) # Not being localised because we don't know the recipients locale. subject = 'Mozilla Add-ons: %s Updated' % version.addon.name template = loader.get_template('activity/emails/developer.txt') send_activity_mail(subject, template.render(Context(author_context_dict)), version, addon_authors, settings.EDITORS_EMAIL) send_activity_mail(subject, template.render(Context(reviewer_context_dict)), version, reviewers, settings.EDITORS_EMAIL) return note
def _check_disco_theme(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert u'{1} <span>by <a href="{0}">{2}</a></span>'.format( absolutify(addon.get_url_path()), unicode(addon.name), u', '.join( author.name for author in addon.listed_authors)) == result['heading'] assert not result['description'] assert result['addon']['theme_data'] == addon.persona.theme_data
def send_notification(self, version): user_log.info('Sending addon update notice to %s for %s' % (self.user.email, self.addon.pk)) context = Context({ 'name': self.addon.name, 'url': absolutify(reverse('addons.detail', args=[self.addon.pk], add_prefix=False)), 'number': version.version, 'review': absolutify(reverse('editors.review', args=[self.addon.pk], add_prefix=False)), 'SITE_URL': settings.SITE_URL, }) # Not being localised because we don't know the editors locale. subject = 'Mozilla Add-ons: %s Updated' % self.addon.name template = loader.get_template('editors/emails/notify_update.ltxt') send_mail(subject, template.render(Context(context)), recipient_list=[self.user.email], from_email=settings.EDITORS_EMAIL, use_blacklist=False)
def test_basic(self): result = self.serialize() assert result['id'] == self.entry.pk assert result['date'] == self.now.isoformat() assert result['action'] == 'rejected' assert result['action_label'] == 'Rejected' assert result['comments'] == u'Oh nôes!' assert result['user'] == { 'name': self.user.name, 'url': absolutify(self.user.get_url_path())}
def _check_disco_theme(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert '<a href="{0}">{1}</a>'.format( absolutify(addon.get_url_path()), unicode(addon.name)) == result['heading'] assert '<span>' not in result['heading'] assert '</span>' not in result['heading'] assert not result['description'] assert result['addon']['theme_data'] == addon.persona.theme_data
def _check_disco_theme(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert u'<a href="{0}">{1}</a>'.format( absolutify(addon.get_url_path()), unicode(addon.name)) == result['heading'] assert '<span>' not in result['heading'] assert '</span>' not in result['heading'] assert not result['description'] assert result['addon']['theme_data'] == addon.persona.theme_data
def test_basic(self): result = self.serialize() assert result['id'] == self.entry.pk assert result['date'] == self.now.isoformat() + 'Z' assert result['action'] == 'rejected' assert result['action_label'] == 'Rejected' assert result['comments'] == u'Oh nôes!' assert result['user'] == { 'id': self.user.pk, 'name': self.user.name, 'url': absolutify(self.user.get_url_path())}
def _check_disco_theme(self, result, item): addon = self.addons[item.addon_id] assert result['addon']['id'] == item.addon_id == addon.pk assert result['addon']['name'] == unicode(addon.name) assert result['addon']['slug'] == addon.slug assert u'{1} <span>by <a href="{0}">{2}</a></span>'.format( absolutify(addon.get_url_path()), unicode(addon.name), u', '.join(author.name for author in addon.listed_authors) ) == result['heading'] assert not result['description'] assert result['addon']['theme_data'] == addon.persona.theme_data
def send_notification(self, version): user_log.info("Sending addon update notice to %s for %s" % (self.user.email, self.addon.pk)) context = Context( { "name": self.addon.name, "url": absolutify(reverse("addons.detail", args=[self.addon.pk], add_prefix=False)), "number": version.version, "review": absolutify(reverse("editors.review", args=[self.addon.pk], add_prefix=False)), "SITE_URL": settings.SITE_URL, } ) # Not being localised because we don't know the editors locale. subject = "Mozilla Add-ons: %s Updated" % self.addon.name template = loader.get_template("editors/emails/notify_update.ltxt") send_mail( subject, template.render(Context(context)), recipient_list=[self.user.email], from_email=settings.EDITORS_EMAIL, use_blacklist=False, )
def test_icon_url_without_modified_date(self): self.addon = addon_factory(icon_type='image/png') self.addon.update(created=datetime(year=1970, day=1, month=1)) obj = self.search() delattr(obj, 'modified') with self.assertNumQueries(0): serializer = ESAddonSerializer(context={'request': self.request}) result = serializer.to_representation(obj) assert result['id'] == self.addon.pk # icon_url should differ, since the serialized result could not use # the modification date. assert result['icon_url'] != absolutify(self.addon.get_icon_url(64)) # If we pretend the original add-on modification date is its creation # date, then icon_url should be the same, since that's what we do when # we don't have a modification date in the serializer. self.addon.modified = self.addon.created assert result['icon_url'] == absolutify(self.addon.get_icon_url(64))
def get_context_data(self): addon_url = self.addon.get_url_path(add_prefix=False) dev_ver_url = self.addon.get_dev_url('versions') # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): addon = Addon.unfiltered.get(pk=self.addon.pk) else: addon = self.addon return {'name': addon.name, 'number': self.version.version if self.version else '', 'reviewer': self.user.display_name, 'addon_url': absolutify(addon_url), 'dev_versions_url': absolutify(dev_ver_url), 'review_url': absolutify(reverse('editors.review', args=[self.addon.pk], add_prefix=False)), 'comments': self.data.get('comments'), 'SITE_URL': settings.SITE_URL}
def test_basic(self): now = self.days_ago(0) entry = self.log(u'Oh nôes!', amo.LOG.REJECT_VERSION, now) result = self.serialize(entry) assert result['id'] == entry.pk assert result['date'] == now.isoformat() assert result['action'] == 'rejected' assert result['action_label'] == 'Rejected' assert result['comments'] == u'Oh nôes!' assert result['user'] == { 'name': self.user.name, 'url': absolutify(self.user.get_url_path())}
def to_representation(self, instance): data = super(DiscoverySerializer, self).to_representation(instance) # Note: target and rel attrs are added in addons-frontend. addon_link = u'<a href="{0}">{1}</a>'.format( absolutify(instance.addon.get_url_path()), unicode(instance.addon.name)) if data['heading'] is None: data['heading'] = addon_link else: data['heading'] = data['heading'].replace( '{start_sub_heading}', '<span>').replace( '{end_sub_heading}', '</span>').replace( '{addon_name}', addon_link) return data
def test_latest_unlisted_version_with_rights(self): self.serializer_class = self.serializer_class_with_unlisted_data self.addon = addon_factory() version_factory( addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED, version='1.1') assert self.addon.latest_unlisted_version result = self.serialize() # In this serializer latest_unlisted_version is present. assert result['latest_unlisted_version'] self._test_version( self.addon.latest_unlisted_version, result['latest_unlisted_version']) assert result['latest_unlisted_version']['url'] == absolutify('')
def test_error_no_code_with_safe_path(self): request = self.make_request() assert len(get_messages(request)) == 0 response = self.render_error( request, views.ERROR_NO_CODE, next_path='/over/here') assert response.status_code == 302 messages = get_messages(request) assert len(messages) == 1 assert 'could not be parsed' in next(iter(messages)).message assert_url_equal(response['location'], absolutify('/over/here')) response = self.render_error( request, views.ERROR_NO_CODE, next_path=None) assert response.status_code == 302 messages = get_messages(request) assert len(messages) == 1 assert 'could not be parsed' in next(iter(messages)).message assert_url_equal(response['location'], self.login_url())
def test_reply_triggers_email_but_no_logging(self): review = Review.objects.get(id=1) user = user_factory() Review.objects.create( reply_to=review, user=user, addon=review.addon, body=u'Rêply', user_responsible=user) assert not ActivityLog.objects.exists() assert len(mail.outbox) == 1 email = mail.outbox[0] reply_url = helpers.absolutify( helpers.url('addons.reviews.detail', review.addon.slug, review.pk, add_prefix=False)) assert email.subject == 'Mozilla Add-on Developer Reply: my addon name' assert 'A developer has replied to your review' in email.body assert 'add-on my addon name' in email.body assert reply_url in email.body assert email.to == ['*****@*****.**'] assert email.from_email == 'Mozilla Add-ons <*****@*****.**>'