def test_lang_initial(self): """If no lang is set on the user, initial value is current locale.""" # Lang is already set: don't change it. res = self.client.get(self.url) form = res.context['form'] assert form.initial['lang'] == 'en-US' with self.activate('fr'): res = self.client.get(reverse('users.edit')) form = res.context['form'] assert form.initial['lang'] == 'en-US' # Lang isn't set yet: initial value is set to the current locale. user = UserProfile.objects.get(email='*****@*****.**') user.lang = None user.save() res = self.client.get(self.url) form = res.context['form'] assert form.initial['lang'] == 'en-US' with self.activate('fr'): res = self.client.get(reverse('users.edit')) form = res.context['form'] assert form.initial['lang'] == 'fr'
def test_search_tools_within_a_category(self): # Pretend Foxy is the only bookmarks related search add-on AddonCategory.objects.all().delete() foxy = Addon.objects.get(name__localized_string='FoxyProxy Standard') foxy.type = amo.ADDON_SEARCH foxy.save() bookmarks = Category.objects.get(slug='bookmarks') AddonCategory.objects.create( addon=foxy, category=bookmarks, feature=False) url = reverse('browse.search-tools.rss', args=('bookmarks',)) + '?sort=popular' response = self.client.get(url) assert response.status_code == 200 doc = pq(response.content) assert doc('rss channel title')[0].text == ( 'Bookmarks :: Search Tools :: Add-ons for Firefox') link = doc('rss channel link')[0].text rel_link = reverse('browse.search-tools.rss', args=('bookmarks',)) + '?sort=popular' assert link.endswith(rel_link), ('Unexpected link: %r' % link) assert doc('rss channel description')[0].text == ( "Search tools relating to Bookmarks") assert [e.text for e in doc('rss channel item title')] == ( ['FoxyProxy Standard 2.17'])
def test_ban(self): ban_url = reverse('admin:users_userprofile_ban', args=(self.user.pk, )) wrong_ban_url = reverse( 'admin:users_userprofile_ban', args=(self.user.pk + 42, )) user = user_factory() self.grant_permission(user, 'Admin:Tools') self.client.login(email=user.email) core.set_user(user) response = self.client.post(ban_url, follow=True) assert response.status_code == 403 self.grant_permission(user, 'Users:Edit') response = self.client.get(ban_url, follow=True) assert response.status_code == 405 # Wrong http method. response = self.client.post(wrong_ban_url, follow=True) assert response.status_code == 404 # Wrong pk. self.user.reload() assert not self.user.deleted response = self.client.post(ban_url, follow=True) assert response.status_code == 200 assert response.redirect_chain[-1][0].endswith(self.detail_url) assert response.redirect_chain[-1][1] == 302 self.user.reload() assert self.user.deleted assert self.user.email alog = ActivityLog.objects.latest('pk') assert alog.action == amo.LOG.ADMIN_USER_BANNED.id assert alog.arguments == [self.user]
def test_version_update_info(self): addon = Addon.objects.get(pk=3615) response = self.client.get( reverse('addons.versions.update_info', args=(addon.slug, self.version.version))) assert response.status_code == 200 assert response['Content-Type'] == 'application/xhtml+xml' # pyquery is annoying to use with XML and namespaces. Use the HTML # parser, but do check that xmlns attribute is present (required by # Firefox for the notes to be shown properly). doc = PyQuery(response.content, parser='html') assert doc('html').attr('xmlns') == 'http://www.w3.org/1999/xhtml' assert doc('p').html() == 'Fix for an important bug' # Test update info in another language. with self.activate(locale='fr'): response = self.client.get( reverse('addons.versions.update_info', args=(addon.slug, self.version.version))) assert response.status_code == 200 assert response['Content-Type'] == 'application/xhtml+xml' assert '<br/>' in response.content, ( 'Should be using XHTML self-closing tags!') doc = PyQuery(response.content, parser='html') assert doc('html').attr('xmlns') == 'http://www.w3.org/1999/xhtml' assert doc('p').html() == ( u"Quelque chose en français.<br/><br/>Quelque chose d'autre.")
def sidebar(app): """Populates the sidebar with (categories, types).""" from olympia.addons.models import Category if app is None: return [], [] # We muck with query to make order_by and extra_order_by play nice. q = Category.objects.filter(application=app.id, weight__gte=0, type=amo.ADDON_EXTENSION) categories = order_by_translation(q, 'name') categories.query.extra_order_by.insert(0, 'weight') Type = collections.namedtuple('Type', 'id name url') base = urlresolvers.reverse('home') types = [Type(99, _('Collections'), base + 'collections/')] shown_types = { amo.ADDON_PERSONA: urlresolvers.reverse('browse.personas'), amo.ADDON_DICT: urlresolvers.reverse('browse.language-tools'), amo.ADDON_SEARCH: urlresolvers.reverse('browse.search-tools'), amo.ADDON_THEME: urlresolvers.reverse('browse.themes'), } titles = dict(amo.ADDON_TYPES, **{amo.ADDON_DICT: _('Dictionaries & Language Packs')}) for type_, url in shown_types.items(): if type_ in app.types: types.append(Type(type_, titles[type_], url)) return categories, sorted(types, key=lambda x: x.name)
def test_delete_picture(self, delete_picture_mock): delete_picture_url = reverse( 'admin:users_userprofile_delete_picture', args=(self.user.pk, )) wrong_delete_picture_url = reverse( 'admin:users_userprofile_delete_picture', args=(self.user.pk + 42, )) user = user_factory() self.grant_permission(user, 'Admin:Tools') self.client.login(email=user.email) core.set_user(user) response = self.client.post(delete_picture_url, follow=True) assert response.status_code == 403 self.grant_permission(user, 'Users:Edit') response = self.client.get(delete_picture_url, follow=True) assert response.status_code == 405 # Wrong http method. response = self.client.post(wrong_delete_picture_url, follow=True) assert response.status_code == 404 # Wrong pk. assert delete_picture_mock.call_count == 0 response = self.client.post(delete_picture_url, follow=True) assert response.status_code == 200 assert response.redirect_chain[-1][0].endswith(self.detail_url) assert response.redirect_chain[-1][1] == 302 assert delete_picture_mock.call_count == 1 alog = ActivityLog.objects.latest('pk') assert alog.action == amo.LOG.ADMIN_USER_PICTURE_DELETED.id assert alog.arguments == [self.user]
def setUp(self): super(TestDetails, self).setUp() self.addon = self.get_addon() self.detail_url = reverse('discovery.addons.detail', args=[self.addon.slug]) self.eula_url = reverse('discovery.addons.eula', args=[self.addon.slug])
def get_absolute_url(url): if isinstance(url, tuple): url = reverse(url[0], args=url[1:]) else: url = reverse(url) return 'http://%s%s' % ('api', url)
def test_acl_collections_edit(self): # Test users in group with 'Collections:Edit' are allowed. user = UserProfile.objects.get(email='*****@*****.**') group = Group.objects.create(name='Staff', rules='Collections:Edit') GroupUser.objects.create(user=user, group=group) r = self.client.post(self.add_url, self.data, follow=True) self.login_regular() url_args = ['admin', self.slug] url = reverse('collections.edit', args=url_args) r = self.client.get(url) eq_(r.status_code, 200) url = reverse('collections.edit_addons', args=url_args) r = self.client.get(url) eq_(r.status_code, 405) # Passed acl check, but this view needs a POST. url = reverse('collections.edit_contributors', args=url_args) r = self.client.get(url) eq_(r.status_code, 405) # Passed acl check, but this view needs a POST. url = reverse('collections.edit_privacy', args=url_args) r = self.client.get(url) eq_(r.status_code, 405) # Passed acl check, but this view needs a POST. url = reverse('collections.delete', args=url_args) r = self.client.get(url) eq_(r.status_code, 200)
def setup_viewer(request, file_obj): addon = file_obj.version.addon data = { 'file': file_obj, 'version': file_obj.version, 'addon': addon, 'status': False, 'selected': {}, 'validate_url': '' } is_user_a_reviewer = acl.is_reviewer(request, addon) if (is_user_a_reviewer or acl.check_addon_ownership( request, addon, dev=True, ignore_disabled=True)): data['validate_url'] = reverse('devhub.json_file_validation', args=[addon.slug, file_obj.id]) data['automated_signing'] = file_obj.automated_signing if file_obj.has_been_validated: data['validation_data'] = file_obj.validation.processed_validation if is_user_a_reviewer: data['file_link'] = { 'text': ugettext('Back to review'), 'url': reverse('reviewers.review', args=[addon.slug]) } else: data['file_link'] = { 'text': ugettext('Back to add-on'), 'url': reverse('addons.detail', args=[addon.pk]) } return data
def get_context_data(self): addon_url = self.addon.get_url_path(add_prefix=False) # 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' dev_ver_url = reverse( 'devhub.addons.versions', args=[self.addon.id]) else: dev_ver_url = self.addon.get_dev_url('versions') 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('reviewers.review', kwargs=review_url_kw, add_prefix=False)), 'comments': self.data.get('comments'), 'SITE_URL': settings.SITE_URL}
def legacy_theme_redirects(request, category=None, category_name=None): url = None if category_name is not None: # This format is for the Complete Themes RSS feed. url = reverse('browse.themes.rss', args=[category_name]) else: if not category or category == 'all': url = reverse('browse.personas') else: try: # Theme? cat = Category.objects.filter(slug=category, type=amo.ADDON_PERSONA)[0] except IndexError: pass else: # Hey, it was a Theme. url = reverse('browse.personas', args=[cat.slug]) if url: if 'sort' in request.GET: url = amo.utils.urlparams(url, sort=request.GET['sort']) return redirect(url, permanent=not settings.DEBUG) else: raise Http404
def sidebar(app): """Populates the sidebar with (categories, types).""" from olympia.addons.models import Category if app is None: return [], [] # Fetch categories... qs = Category.objects.filter(application=app.id, weight__gte=0, type=amo.ADDON_EXTENSION) # Now sort them in python according to their name property (which looks up # the translated name using gettext + our constants) categories = sorted(qs, key=attrgetter('weight', 'name')) Type = collections.namedtuple('Type', 'id name url') base = urlresolvers.reverse('home') types = [Type(99, ugettext('Collections'), base + 'collections/')] shown_types = { amo.ADDON_PERSONA: urlresolvers.reverse('browse.personas'), amo.ADDON_DICT: urlresolvers.reverse('browse.language-tools'), amo.ADDON_SEARCH: urlresolvers.reverse('browse.search-tools'), amo.ADDON_THEME: urlresolvers.reverse('browse.themes'), } titles = dict( amo.ADDON_TYPES, **{amo.ADDON_DICT: ugettext('Dictionaries & Language Packs')}) for type_, url in shown_types.items(): if type_ in app.types: types.append(Type(type_, titles[type_], url)) return categories, sorted(types, key=lambda x: x.name)
def login(self, account): log.debug('creating fxa account') fxa_account, email_account = helpers.get_fxa_account() log.debug('calling login/start to generate fxa_state') response = self.client.get( reverse('accounts.login_start'), allow_redirects=False) params = dict(urlparse.parse_qsl(response.headers['Location'])) fxa_state = params['state'] log.debug('Get browser id session token') fxa_session = helpers.get_fxa_client().login( email=fxa_account.email, password=fxa_account.password) oauth_client = fxa_oauth.Client( client_id=FXA_CONFIG['client_id'], client_secret=FXA_CONFIG['client_secret'], server_url=FXA_CONFIG['oauth_host']) log.debug('convert browser id session token into oauth code') oauth_code = oauth_client.authorize_code(fxa_session, scope='profile') # Now authenticate the user, this will verify the user on the response = self.client.get( reverse('accounts.authenticate'), params={ 'state': fxa_state, 'code': oauth_code, } )
def test_delete_link(self): # Create an addon by user 1. self.create_collection() url = reverse('collections.edit_contributors', args=['admin', self.slug]) self.client.post( url, { 'contributor': '*****@*****.**', 'application': 1, 'type': 1 }, follow=True) url = reverse('collections.edit', args=['admin', self.slug]) r = self.client.get(url) assert r.status_code == 200 doc = pq(r.content) assert len(doc('a.delete')) == 2 self.login_regular() r = self.client.get(url) assert r.status_code == 200 doc = pq(r.content) assert len(doc('a.delete')) == 0
def test_nonapp_pages(self): self._check({ '/en-US/pages/developer_faq': reverse('pages.dev_faq'), '/en-US/pages/review_guide': reverse('pages.review_guide'), '/en-US/pages/developer_agreement': reverse( 'devhub.docs', args=['policies/agreement']), })
def test_forbidden_edit(self): self.create_collection() self.login_regular() url_args = ['admin', self.slug] url = reverse('collections.edit', args=url_args) r = self.client.get(url) assert r.status_code == 403 r = self.client.post(url) assert r.status_code == 403 url = reverse('collections.edit_addons', args=url_args) r = self.client.get(url) assert r.status_code == 403 r = self.client.post(url) assert r.status_code == 403 url = reverse('collections.edit_contributors', args=url_args) r = self.client.get(url) assert r.status_code == 403 r = self.client.post(url) assert r.status_code == 403 url = reverse('collections.edit_privacy', args=url_args) r = self.client.get(url) assert r.status_code == 403 r = self.client.post(url) assert r.status_code == 403 url = reverse('collections.delete', args=url_args) r = self.client.get(url) assert r.status_code == 403 r = self.client.post(url) assert r.status_code == 403
def test_user_review_history(self): self.theme_factory() reviewer = self.create_and_become_reviewer() res = self.client.get(reverse('editors.themes.history')) assert res.status_code == 200 doc = pq(res.content) assert doc('tbody tr').length == 0 theme = Persona.objects.all()[0] for x in range(3): ActivityLog.create( amo.LOG.THEME_REVIEW, theme.addon, user=reviewer, details={'action': rvw.ACTION_APPROVE, 'comment': '', 'reject_reason': ''}) res = self.client.get(reverse('editors.themes.history')) assert res.status_code == 200 doc = pq(res.content) assert doc('tbody tr').length == 3 res = self.client.get(reverse('editors.themes.logs')) assert res.status_code == 200 doc = pq(res.content) assert doc('tbody tr').length == 3 * 2 # Double for comment rows.
def test_featured_extensions_with_category_es_ES(self): addon = self.addon res = self.client.get(reverse('browse.extensions', args=['bookmarks'])) assert addon in res.context['filter'].all()['featured'] self.change_addoncategory(addon, 'es') res = self.client.get(reverse('browse.extensions', args=['bookmarks'])) assert addon not in res.context['filter'].all()['featured']
def test_upload_link_label_in_edit_nav(self): url = reverse('devhub.versions.edit', args=(self.addon.slug, self.version.pk)) r = self.client.get(url) link = pq(r.content)('.addon-status>.addon-upload>strong>a') assert link.text() == 'Upload New Version' assert link.attr('href') == '%s#version-upload' % ( reverse('devhub.addons.versions', args=[self.addon.slug]))
def setUp(self): super(TestVotes, self).setUp() self.client.login(username='******', password='******') args = ['fligtar', 'slug'] Collection.objects.create(slug='slug', author_id=9945) self.c_url = reverse('collections.detail', args=args) self.up = reverse('collections.vote', args=args + ['up']) self.down = reverse('collections.vote', args=args + ['down'])
def test_request_with_unkown_email(self): r = self.client.post( reverse('password_reset_form'), {'email': '*****@*****.**'} ) eq_(len(mail.outbox), 0) self.assert3xx(r, reverse('password_reset_done'))
def test_dashboard(self): """Make sure the dashboard is getting data.""" self.log_creates(10) r = self.client.get(reverse('devhub.addons')) doc = pq(r.content) assert len(doc('li.item')) == 4 assert doc('.subscribe-feed').attr('href')[:-32] == ( reverse('devhub.feed_all') + '?privaterss=')
def test_nonapp_pages(self): self._check({ '/en-US/pages/review_guide': reverse('pages.review_guide'), }) r = self.client.get('/en-US/firefox/pages/developer_agreement', follow=False) self.assert3xx(r, reverse( 'devhub.docs', args=['policies/agreement']), 301)
def test_login_protected(self): self.client.logout() r = self.client.get(reverse('devhub.bulk_compat_result', args=[self.addon.slug, self.result.id])) assert r.status_code == 302 r = self.client.post(reverse('devhub.json_bulk_compat_result', args=[self.addon.slug, self.result.id])) assert r.status_code == 302
def test_reverse(self): # Make sure it works outside the request. urlresolvers.clean_url_prefixes() # Modified in BaseTestCase. assert urlresolvers.reverse('home') == '/' # With a request, locale and app prefixes work. Client().get('/') assert urlresolvers.reverse('home') == '/en-US/firefox/'
def test_reverse(self): # Make sure it works outside the request. urlresolvers.clean_url_prefixes() # Modified in BaseTestCase. eq_(urlresolvers.reverse("home"), "/") # With a request, locale and app prefixes work. Client().get("/") eq_(urlresolvers.reverse("home"), "/en-US/firefox/")
def test_breadcrumbs(self): r = self.client.get(self.add_url) expected = [ ('Add-ons for Firefox', reverse('home')), ('Collections', reverse('collections.list')), ('Create', None) ] amo.tests.check_links(expected, pq(r.content)('#breadcrumbs li'))
def get_response(self, **kwargs): url = reverse('devhub.feed_all') if 'addon' in kwargs: url = reverse('devhub.feed', args=(kwargs['addon'],)) if kwargs: url += '?' + urlencode(kwargs) return self.client.get(url, follow=True)
def test_filename_not_uuidfied(self, validate_mock): validate_mock.return_value = json.dumps(amo.VALIDATOR_SKELETON_RESULTS) url = reverse('devhub.upload') data = open(get_image_path('animated.png'), 'rb') self.client.post(url, {'upload': data}) upload = FileUpload.objects.get() response = self.client.get( reverse('devhub.upload_detail', args=(upload.uuid.hex,))) assert 'Validation Results for animated.png' in response.content
def redirect(request, viewer, key): new = Token(data=[viewer.file.id, key]) new.save() url = reverse('files.serve', args=[viewer, key]) url = urlparams(url, token=new.token) return http.HttpResponseRedirect(url)
def links(self): return [Link(_(u'Add to {0}').format(unicode(self.app.pretty)), reverse('addons.detail', args=[amo.PERSONAS_ADDON_ID]))]
def get_context(self, name, value, attrs): context = super(SourceFileInput, self).get_context(name, value, attrs) if value and hasattr(value, 'instance'): context['download_url'] = reverse('downloads.source', args=(value.instance.pk, )) return context
def home(request): if (not acl.action_allowed(request, 'Addons', 'Review') and acl.action_allowed(request, 'Personas', 'Review')): return http.HttpResponseRedirect(reverse('editors.themes.home')) motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit') durations = (('new', _('New Add-ons (Under 5 days)')), ('med', _('Passable (5 to 10 days)')), ('old', _('Overdue (Over 10 days)'))) limited_reviewer = is_limited_reviewer(request) progress, percentage = _editor_progress(limited_reviewer=limited_reviewer) unlisted_progress, unlisted_percentage = _editor_progress( unlisted=True, limited_reviewer=limited_reviewer) reviews_max_display = getattr(settings, 'EDITOR_REVIEWS_MAX_DISPLAY', 5) reviews_total = ActivityLog.objects.total_reviews()[:reviews_max_display] reviews_monthly = ( ActivityLog.objects.monthly_reviews()[:reviews_max_display]) reviews_total_count = ActivityLog.objects.user_approve_reviews( request.user).count() reviews_monthly_count = ( ActivityLog.objects.current_month_user_approve_reviews( request.user).count()) # Try to read user position from retrieved reviews. # If not available, query for it. reviews_total_position = ( ActivityLog.objects.user_position(reviews_total, request.user) or ActivityLog.objects.total_reviews_user_position(request.user)) reviews_monthly_position = ( ActivityLog.objects.user_position(reviews_monthly, request.user) or ActivityLog.objects.monthly_reviews_user_position(request.user)) limited_reviewer = is_limited_reviewer(request) data = context( request, reviews_total=reviews_total, reviews_monthly=reviews_monthly, reviews_total_count=reviews_total_count, reviews_monthly_count=reviews_monthly_count, reviews_total_position=reviews_total_position, reviews_monthly_position=reviews_monthly_position, new_editors=EventLog.new_editors(), eventlog=ActivityLog.objects.editor_events()[:6], progress=progress, unlisted_progress=unlisted_progress, percentage=percentage, unlisted_percentage=unlisted_percentage, durations=durations, reviews_max_display=reviews_max_display, motd_editable=motd_editable, queue_counts_total=queue_counts(admin_reviewer=True, limited_reviewer=limited_reviewer), unlisted_queue_counts_total=queue_counts( admin_reviewer=True, unlisted=True, limited_reviewer=limited_reviewer), ) return render(request, 'editors/home.html', data)
def themes_queue_rereview(request): # By default, redirect back to the queue after a commit. request.session['theme_redirect_url'] = reverse( 'editors.themes.queue_rereview') return _themes_queue(request, rereview=True)
def test_perm(self): # Personas:Review allow access to deleted themes as well. self.login('*****@*****.**') response = self.client.get(reverse('reviewers.themes.deleted')) assert response.status_code == 200
def themes_single(request, slug): """ Like a detail page, manually review a single theme if it is pending and isn't locked. """ reviewer = request.user reviewable = True # Don't review an already reviewed theme. theme = get_object_or_404(Persona, addon__slug=slug) if (theme.addon.status != amo.STATUS_PENDING and not theme.rereviewqueuetheme_set.all()): reviewable = False if (not settings.ALLOW_SELF_REVIEWS and not acl.action_allowed(request, 'Admin', '%') and theme.addon.has_author(request.user)): reviewable = False else: # Don't review a locked theme (that's not locked to self). try: lock = theme.themelock if (lock.reviewer.id != reviewer.id and lock.expiry > datetime.datetime.now()): reviewable = False elif (lock.reviewer.id != reviewer.id and lock.expiry < datetime.datetime.now()): # Steal expired lock. lock.reviewer = reviewer lock.expiry = get_updated_expiry() lock.save() else: # Update expiry. lock.expiry = get_updated_expiry() lock.save() except ThemeLock.DoesNotExist: # Create lock if not created. ThemeLock.objects.create(theme=theme, reviewer=reviewer, expiry=get_updated_expiry()) ThemeReviewFormset = formset_factory(forms.ThemeReviewForm) formset = ThemeReviewFormset(initial=[{'theme': theme.id}]) # Since we started the review on the single page, we want to return to the # single page rather than get shot back to the queue. request.session['theme_redirect_url'] = reverse('editors.themes.single', args=[theme.addon.slug]) rereview = (theme.rereviewqueuetheme_set.all()[0] if theme.rereviewqueuetheme_set.exists() else None) return render( request, 'editors/themes/single.html', context( **{ 'formset': formset, 'theme': rereview if rereview else theme, 'theme_formsets': zip([rereview if rereview else theme], formset), 'theme_reviews': paginate( request, ActivityLog.objects.filter( action=amo.LOG.THEME_REVIEW.id, _arguments__contains=theme.addon.id)), 'actions': get_actions_json(), 'theme_count': 1, 'rereview': rereview, 'reviewable': reviewable, 'reject_reasons': rvw.THEME_REJECT_REASONS, 'action_dict': rvw.REVIEW_ACTIONS, 'tab': ('flagged' if theme.addon.status == amo.STATUS_REVIEW_PENDING else 'rereview' if rereview else 'pending') }))
def test_commit(self, copy_mock, create_preview_mock, send_mail_jinja_mock, version_changed_mock, theme_checksum_mock, message_mock): if self.flagged: # Feels redundant to test this for flagged queue. return themes = [] for x in range(5): themes.append(self.theme_factory().persona) form_data = amo.tests.formset(initial_count=5, total_count=6) # Create locks. reviewer = self.create_and_become_reviewer() for index, theme in enumerate(themes): ThemeLock.objects.create( theme=theme, reviewer=reviewer, expiry=datetime.datetime.now() + datetime.timedelta(minutes=amo.THEME_LOCK_EXPIRY)) form_data['form-%s-theme' % index] = str(theme.id) # Build formset. actions = ( (str(amo.ACTION_MOREINFO), 'moreinfo', ''), (str(amo.ACTION_FLAG), 'flag', ''), (str(amo.ACTION_DUPLICATE), 'duplicate', ''), (str(amo.ACTION_REJECT), 'reject', '1'), (str(amo.ACTION_APPROVE), '', ''), ) for index, action in enumerate(actions): action, comment, reject_reason = action form_data['form-%s-action' % index] = action form_data['form-%s-comment' % index] = comment form_data['form-%s-reject_reason' % index] = reject_reason old_version = themes[4].addon.current_version.version # Test edge case where pending theme also has re-review. for theme in (themes[3], themes[4]): RereviewQueueTheme.objects.create(theme=theme, header='', footer='') # Commit. # Activate another locale than en-US, and make sure emails to theme # authors are NOT translated, but the message to the review IS. with self.activate(locale='fr'): response = self.client.post(reverse('reviewers.themes.commit'), form_data) self.assert3xx(response, reverse('reviewers.themes.queue_themes')) if self.rereview: # Original design of reuploaded themes should stay public. for i in range(4): assert themes[i].addon.status == amo.STATUS_PUBLIC assert themes[i].header == 'header' assert themes[i].footer == 'footer' assert copy_mock.call_count == 2 assert copy_mock.call_args_list[0][0][0].endswith('pending_header') assert copy_mock.call_args_list[0][0][1].endswith('header') assert copy_mock.call_args_list[1][0][0].endswith('pending_footer') assert copy_mock.call_args_list[1][0][1].endswith('footer') assert create_preview_mock.call_count == 1 create_preview_args = create_preview_mock.call_args_list[0][1] assert create_preview_args['src'].endswith('header') assert create_preview_args['full_dst'][0].endswith('preview.png') assert create_preview_args['full_dst'][1].endswith('icon.png') # Approved/rejected/dupe themes have their images deleted # leaving only 2 RQT objects. Can't flag a rereview theme yet, and # moreinfo does nothing but email the artist. assert RereviewQueueTheme.objects.count() == 2 # Test version incremented. assert themes[4].addon.reload().current_version.version == ( str(float(old_version) + 1)) # Checksum was recalculated for that theme. assert theme_checksum_mock.call_count == 1 assert theme_checksum_mock.call_args_list[0][0][0] == themes[4] else: assert themes[0].addon.reload().status == amo.STATUS_REVIEW_PENDING assert themes[1].addon.reload().status == amo.STATUS_REVIEW_PENDING assert themes[2].addon.reload().status == amo.STATUS_REJECTED assert themes[3].addon.reload().status == amo.STATUS_REJECTED assert theme_checksum_mock.call_count == 0 assert themes[4].addon.reload().status == amo.STATUS_PUBLIC assert ActivityLog.objects.count() == 4 if self.rereview else 5 expected_calls = [ mock.call('A question about your Theme submission', 'reviewers/themes/emails/moreinfo.html', { 'reason': None, 'comment': u'moreinfo', 'theme': themes[0], 'reviewer_email': u'*****@*****.**', 'base_url': 'http://testserver' }, headers={'Reply-To': settings.THEMES_EMAIL}, from_email=settings.ADDONS_EMAIL, recipient_list=set([])), mock.call('Theme submission flagged for review', 'reviewers/themes/emails/flag_reviewer.html', { 'reason': None, 'comment': u'flag', 'theme': themes[1], 'base_url': 'http://testserver' }, headers={'Reply-To': settings.THEMES_EMAIL}, from_email=settings.ADDONS_EMAIL, recipient_list=[settings.THEMES_EMAIL]), mock.call('A problem with your Theme submission', 'reviewers/themes/emails/reject.html', { 'reason': u'Duplicate Submission', 'comment': u'duplicate', 'theme': themes[2], 'base_url': 'http://testserver' }, headers={'Reply-To': settings.THEMES_EMAIL}, from_email=settings.ADDONS_EMAIL, recipient_list=set([])), mock.call('A problem with your Theme submission', 'reviewers/themes/emails/reject.html', { 'reason': u'Sexual or pornographic content', 'comment': u'reject', 'theme': themes[3], 'base_url': 'http://testserver' }, headers={'Reply-To': settings.THEMES_EMAIL}, from_email=settings.ADDONS_EMAIL, recipient_list=set([])), mock.call('Thanks for submitting your Theme', 'reviewers/themes/emails/approve.html', { 'reason': None, 'comment': u'', 'theme': themes[4], 'base_url': 'http://testserver' }, headers={'Reply-To': settings.THEMES_EMAIL}, from_email=settings.ADDONS_EMAIL, recipient_list=set([])) ] if self.rereview: assert send_mail_jinja_mock.call_count == 4 assert send_mail_jinja_mock.call_args_list[0] == expected_calls[0] assert send_mail_jinja_mock.call_args_list[1] == expected_calls[2] assert send_mail_jinja_mock.call_args_list[2] == expected_calls[3] assert send_mail_jinja_mock.call_args_list[3] == expected_calls[4] else: assert send_mail_jinja_mock.call_count == 5 assert send_mail_jinja_mock.call_args_list[0] == expected_calls[0] assert send_mail_jinja_mock.call_args_list[1] == expected_calls[1] assert send_mail_jinja_mock.call_args_list[2] == expected_calls[2] assert send_mail_jinja_mock.call_args_list[3] == expected_calls[3] assert send_mail_jinja_mock.call_args_list[4] == expected_calls[4] assert message_mock.call_args_list[0][0][1] == ( u'5 validations de thèmes réalisées avec succès ' u'(+15 points, 15 au total).') # Reviewer points accrual. assert ReviewerScore.objects.all()[0].score > 0
def notify_compatibility_chunk(users, job, data, **kw): log.info('[%s@%s] Sending notification mail for job %s.' % (len(users), notify_compatibility.rate_limit, job.pk)) core.set_user(get_task_user()) dry_run = data['preview_only'] app_id = job.target_version.application stats = collections.defaultdict(int) stats['processed'] = 0 stats['is_dry_run'] = int(dry_run) for user in users: stats['processed'] += 1 try: for addon in chain(user.passing_addons, user.failing_addons): try: results = job.result_set.filter(file__version__addon=addon) addon.links = [ absolutify(reverse('devhub.bulk_compat_result', args=[addon.slug, r.pk])) for r in results] version = ( addon.current_version or addon.find_latest_version( channel=amo.RELEASE_CHANNEL_LISTED)) addon.compat_link = absolutify(reverse( 'devhub.versions.edit', args=[addon.pk, version.pk])) except: task_error = sys.exc_info() log.error(u'Bulk validation email error for user %s, ' u'addon %s: %s: %s' % (user.email, addon.slug, task_error[0], task_error[1]), exc_info=False) context = Context({ 'APPLICATION': unicode(amo.APP_IDS[job.application].pretty), 'VERSION': job.target_version.version, 'PASSING_ADDONS': user.passing_addons, 'FAILING_ADDONS': user.failing_addons, }) log.info(u'Emailing %s%s for %d addons about ' 'bulk validation job %s' % (user.email, ' [PREVIEW]' if dry_run else '', len(user.passing_addons) + len(user.failing_addons), job.pk)) args = (Template(data['subject']).render(context), Template(data['text']).render(context)) kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[user.email]) if dry_run: job.preview_notify_mail(*args, **kwargs) else: stats['author_emailed'] += 1 send_mail(*args, **kwargs) ActivityLog.create( amo.LOG.BULK_VALIDATION_USER_EMAILED, user, details={'passing': [a.id for a in user.passing_addons], 'failing': [a.id for a in user.failing_addons], 'target': job.target_version.version, 'application': app_id}) except: task_error = sys.exc_info() log.error(u'Bulk validation email error for user %s: %s: %s' % (user.email, task_error[0], task_error[1]), exc_info=False) log.info('[%s@%s] bulk email stats for job %s: {%s}' % (len(users), notify_compatibility.rate_limit, job.pk, ', '.join('%s: %s' % (k, stats[k]) for k in sorted(stats.keys()))))
def setUp(self): self.list_url = reverse('admin:files_file_changelist')
def update_info_redirect(request, version_id): version = get_object_or_404(Version.objects, pk=version_id) return redirect(reverse('addons.versions.update_info', args=(version.addon.id, version.version)), permanent=True)
def setUp(self): super(TestThemeQueue, self).setUp() self.queue_url = reverse('reviewers.themes.queue_themes')
def check_permissions(self, slug, status_code): for url in [ reverse('reviewers.themes.queue_themes'), reverse('reviewers.themes.single', args=[slug]) ]: assert self.client.get(url).status_code == status_code
def test_super_review_email(self): self.setup_data(amo.STATUS_NULL) self.helper.handler.process_super_review() url = reverse('editors.review', args=[self.addon.pk], add_prefix=False) assert url in mail.outbox[1].body
def test_redirect_to_new_dashboard(self): response = self.client.get('/en-US/reviewers/themes') self.assert3xx(response, reverse('reviewers.dashboard'), status_code=301)
def test_queue_page(self): url = reverse('reviewers.themes.single', args=[self.theme.slug]) self.assertNameAndNoXSS(url)
def test_update_legacy_theme(self, amo_copy_stored_file_mock, create_persona_preview_mock, copy_stored_filed_mock, theme_checksum_mock, send_mail_jinja_mock): """ Test updating themes that were submitted from GetPersonas. STR the bug this test fixes: - Reupload a legacy theme and approve it. - On approving, it would make a preview image with the destination as 'preview.png' and 'icon.png', but legacy themes use 'preview.jpg' and 'preview_small.jpg'. - Thus the preview images were not being updated, but the header/footer images were. """ theme = self.theme_factory(status=amo.STATUS_PUBLIC).persona theme.header = 'Legacy-header3H.png' theme.footer = 'Legacy-footer3H-Copy.jpg' theme.persona_id = 5 theme.save() form_data = amo.tests.formset(initial_count=5, total_count=6) RereviewQueueTheme.objects.create(theme=theme, header='pending_header.png', footer='pending_footer.png') # Create lock. reviewer = self.create_and_become_reviewer() ThemeLock.objects.create(theme=theme, reviewer=reviewer, expiry=self.days_ago(-1)) form_data['form-0-theme'] = str(theme.id) # Build formset. form_data['form-0-action'] = str(amo.ACTION_APPROVE) # Commit. self.client.post(reverse('reviewers.themes.commit'), form_data) # Check nothing has changed. assert theme.header == 'Legacy-header3H.png' assert theme.footer == 'Legacy-footer3H-Copy.jpg' theme.thumb_path.endswith('preview.jpg') theme.icon_path.endswith('preview_small.jpg') theme.preview_path.endswith('preview_large.jpg') # Test calling create_persona_preview_images. assert (create_persona_preview_mock.call_args_list[0][1]['full_dst'] [0].endswith('preview.jpg')) assert (create_persona_preview_mock.call_args_list[0][1]['full_dst'] [1].endswith('preview_small.jpg')) # pending_header should be mv'ed to Legacy-header3H.png. assert (amo_copy_stored_file_mock.call_args_list[0][0][0].endswith( 'pending_header')) assert (amo_copy_stored_file_mock.call_args_list[0][0][1].endswith( 'Legacy-header3H.png')) # pending_footer should be mv'ed to Legacy-footer-Copy3H.png. assert (amo_copy_stored_file_mock.call_args_list[1][0][0].endswith( 'pending_footer')) assert (amo_copy_stored_file_mock.call_args_list[1][0][1].endswith( 'Legacy-footer3H-Copy.jpg')) assert (copy_stored_filed_mock.call_args_list[0][0][0].endswith( 'preview.jpg')) assert (copy_stored_filed_mock.call_args_list[0][0][1].endswith( 'preview_large.jpg')) # We re-calculated the theme checksum from the newest data. assert theme_checksum_mock.call_count == 1 assert theme_checksum_mock.call_args_list[0][0][0] == theme
def queue(request): return redirect(reverse('editors.queue_pending'))
def test_table(self): self.login('*****@*****.**') response = self.client.get(reverse('reviewers.themes.deleted')) assert response.status_code == 200 assert (self.deleted.name.localized_string in smart_text(response.content))
def setUp(self): super(TestThemeQueueFlagged, self).setUp() self.status = amo.STATUS_REVIEW_PENDING self.flagged = True self.queue_url = reverse('reviewers.themes.queue_flagged')
def review(request, addon): if not addon.is_listed and not acl.check_unlisted_addons_reviewer(request): raise http.Http404 version = addon.latest_version if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user): amo.messages.warning(request, _('Self-reviews are not allowed.')) return redirect(reverse('editors.queue')) form_helper = ReviewHelper(request=request, addon=addon, version=version) form = forms.ReviewForm(request.POST or None, helper=form_helper) queue_type = ('prelim' if form.helper.review_type == 'preliminary' else form.helper.review_type) if addon.is_listed: redirect_url = reverse('editors.queue_%s' % queue_type) else: redirect_url = reverse('editors.unlisted_queue_%s' % queue_type) is_admin = acl.action_allowed(request, 'Addons', 'Edit') if request.method == 'POST' and form.is_valid(): form.helper.process() if form.cleaned_data.get('notify'): EditorSubscription.objects.get_or_create(user=request.user, addon=addon) if form.cleaned_data.get('adminflag') and is_admin: addon.update(admin_review=False) amo.messages.success(request, _('Review successfully processed.')) return redirect(redirect_url) # Kick off validation tasks for any files in this version which don't have # cached validation, since editors will almost certainly need to access # them. But only if we're not running in eager mode, since that could mean # blocking page load for several minutes. if version and not getattr(settings, 'CELERY_ALWAYS_EAGER', False): for file_ in version.files.all(): if not file_.has_been_validated: devhub_tasks.validate(file_) canned = AddonCannedResponse.objects.all() actions = form.helper.actions.items() statuses = [amo.STATUS_PUBLIC, amo.STATUS_LITE, amo.STATUS_LITE_AND_NOMINATED] try: show_diff = version and ( addon.versions.exclude(id=version.id).filter( files__isnull=False, created__lt=version.created, files__status__in=statuses).latest()) except Version.DoesNotExist: show_diff = None # The actions we should show a minimal form from. actions_minimal = [k for (k, a) in actions if not a.get('minimal')] versions = (Version.unfiltered.filter(addon=addon) .exclude(files__status=amo.STATUS_BETA) .order_by('-created') .transform(Version.transformer_activity) .transform(Version.transformer)) class PseudoVersion(object): def __init__(self): self.all_activity = [] all_files = () approvalnotes = None compatible_apps_ordered = () releasenotes = None status = 'Deleted' deleted = True @property def created(self): return self.all_activity[0].created @property def version(self): return (self.all_activity[0].activity_log .details.get('version', '[deleted]')) # Grab review history for deleted versions of this add-on # Version are soft-deleted now but we need this for older deletions. comments = (CommentLog.objects .filter(activity_log__action__in=amo.LOG_REVIEW_QUEUE, activity_log__versionlog=None, activity_log__addonlog__addon=addon) .order_by('created') .select_related('activity_log')) comment_versions = defaultdict(PseudoVersion) for c in comments: c.version = c.activity_log.details.get('version', c.created) comment_versions[c.version].all_activity.append(c) all_versions = comment_versions.values() all_versions.extend(versions) all_versions.sort(key=lambda v: v.created, reverse=True) pager = amo.utils.paginate(request, all_versions, 10) num_pages = pager.paginator.num_pages count = pager.paginator.count try: flags = ViewQueue.objects.get(id=addon.id).flags except ViewQueue.DoesNotExist: flags = [] user_changes_actions = [ amo.LOG.ADD_USER_WITH_ROLE.id, amo.LOG.CHANGE_USER_WITH_ROLE.id, amo.LOG.REMOVE_USER_WITH_ROLE.id] user_changes_log = AddonLog.objects.filter( activity_log__action__in=user_changes_actions, addon=addon).order_by('id') ctx = context(request, version=version, addon=addon, pager=pager, num_pages=num_pages, count=count, flags=flags, form=form, canned=canned, is_admin=is_admin, show_diff=show_diff, actions=actions, actions_minimal=actions_minimal, whiteboard_form=forms.WhiteboardForm(instance=addon), user_changes=user_changes_log, unlisted=not addon.is_listed) return render(request, 'editors/review.html', ctx)
def license_url(self, impala=False): return reverse('addons.license', args=[self.addon.slug, self.version])
def send_mail(subject, message, from_email=None, recipient_list=None, use_deny_list=True, perm_setting=None, manage_url=None, headers=None, cc=None, real_email=False, html_message=None, attachments=None, max_retries=3, reply_to=None): """ A wrapper around django.core.mail.EmailMessage. Adds deny checking and error logging. """ from olympia.amo.templatetags.jinja_helpers import absolutify from olympia.amo.tasks import send_email from olympia.users import notifications if not recipient_list: return True if isinstance(recipient_list, six.string_types): raise ValueError('recipient_list should be a list, not a string.') # Check against user notification settings if perm_setting: if isinstance(perm_setting, six.string_types): perm_setting = notifications.NOTIFICATIONS_BY_SHORT[perm_setting] perms = dict( UserNotification.objects.filter( user__email__in=recipient_list, notification_id=perm_setting.id).values_list( 'user__email', 'enabled')) d = perm_setting.default_checked recipient_list = [ e for e in recipient_list if e and perms.setdefault(e, d) ] # Prune denied emails. if use_deny_list: white_list = [] for email in recipient_list: if email and email.lower() in settings.EMAIL_DENY_LIST: log.debug('Blacklisted email removed from list: %s' % email) else: white_list.append(email) else: white_list = recipient_list if not from_email: from_email = settings.DEFAULT_FROM_EMAIL if cc: # If not six.string_types, assume it is already a list. if isinstance(cc, six.string_types): cc = [cc] if not headers: headers = {} # Avoid auto-replies per rfc 3834 and the Microsoft variant headers['X-Auto-Response-Suppress'] = 'RN, NRN, OOF, AutoReply' headers['Auto-Submitted'] = 'auto-generated' def send(recipients, message, **options): kwargs = { 'attachments': attachments, 'cc': cc, 'from_email': from_email, 'headers': headers, 'html_message': html_message, 'max_retries': max_retries, 'real_email': real_email, 'reply_to': reply_to, } kwargs.update(options) # Email subject *must not* contain newlines args = (list(recipients), ' '.join(subject.splitlines()), message) return send_email.delay(*args, **kwargs) if white_list: if perm_setting: html_template = loader.get_template('amo/emails/unsubscribe.html') text_template = loader.get_template('amo/emails/unsubscribe.ltxt') if not manage_url: manage_url = urlparams( absolutify(reverse('users.edit', add_prefix=False)), 'acct-notify') for recipient in white_list: # Add unsubscribe link to footer. token, hash = UnsubscribeCode.create(recipient) unsubscribe_url = absolutify( reverse('users.unsubscribe', args=[token, hash, perm_setting.short], add_prefix=False)) context = { 'message': message, 'manage_url': manage_url, 'unsubscribe_url': unsubscribe_url, 'perm_setting': perm_setting.label, 'SITE_URL': settings.SITE_URL, 'mandatory': perm_setting.mandatory, } # Render this template in the default locale until # bug 635840 is fixed. with translation.override(settings.LANGUAGE_CODE): message_with_unsubscribe = text_template.render(context) if html_message: context['message'] = html_message with translation.override(settings.LANGUAGE_CODE): html_with_unsubscribe = html_template.render(context) result = send([recipient], message_with_unsubscribe, html_message=html_with_unsubscribe, attachments=attachments) else: result = send([recipient], message_with_unsubscribe, attachments=attachments) else: result = send(recipient_list, message=message, html_message=html_message, attachments=attachments) else: result = True return result
def setUp(self): super(TestThemeQueueRereview, self).setUp() self.status = amo.STATUS_PUBLIC self.rereview = True self.queue_url = reverse('reviewers.themes.queue_rereview')
def test_no_django_debug_toolbar(self): with self.settings(DEBUG=False): res = self.client.get(reverse('home'), follow=True) assert 'djDebug' not in res.content assert 'debug_toolbar' not in res.content
def get_url_path(self): if self.channel == amo.RELEASE_CHANNEL_UNLISTED: return '' return reverse('addons.versions', args=[self.addon.slug, self.version])
def unlisted_queue(request): return redirect(reverse('editors.unlisted_queue_pending'))
def test_eula_with_contrib_roadblock(self): url = reverse('addons.eula', args=[11730, 53612]) response = self.client.get(url, follow=True) doc = PyQuery(response.content) assert doc('[data-search]').attr('class') == 'install '
def get_validation_url(self, obj): return reverse('devhub.file_validation', args=[obj.addon.slug, obj.current_file.id])
def test_theme_list(self): self.create_and_become_reviewer() self.theme_factory() res = self.client.get(reverse('reviewers.themes.list')) assert res.status_code == 200 assert pq(res.content)('#addon-queue tbody tr').length == 1