Example #1
0
def extension_detail(request, addon):
    """Extensions details page."""
    # If current version is incompatible with this app, redirect.
    comp_apps = addon.compatible_apps
    if comp_apps and request.APP not in comp_apps:
        prefixer = urlresolvers.get_url_prefix()
        prefixer.app = comp_apps.keys()[0].short
        return redirect('addons.detail', addon.slug, permanent=True)

    # Popular collections this addon is part of.
    collections = Collection.objects.listed().filter(
        addons=addon, application=request.APP.id)

    ctx = {
        'addon': addon,
        'src': request.GET.get('src', 'dp-btn-primary'),
        'version_src': request.GET.get('src', 'dp-btn-version'),
        'tags': addon.tags.not_blacklisted(),
        'grouped_ratings': GroupedRating.get(addon.id),
        'review_form': ReviewForm(),
        'reviews': Review.without_replies.all().filter(
            addon=addon, is_latest=True),
        'get_replies': Review.get_replies,
        'collections': collections.order_by('-subscribers')[:3],
        'abuse_form': AbuseForm(request=request),
    }

    # details.html just returns the top half of the page for speed. The bottom
    # does a lot more queries we don't want on the initial page load.
    if request.is_ajax():
        # Other add-ons/apps from the same author(s).
        ctx['author_addons'] = addon.authors_other_addons(app=request.APP)[:6]
        return render(request, 'addons/impala/details-more.html', ctx)
    else:
        return render(request, 'addons/impala/details.html', ctx)
Example #2
0
def extension_detail(request, addon):
    """Extensions details page."""
    # If current version is incompatible with this app, redirect.
    comp_apps = addon.compatible_apps
    if comp_apps and request.APP not in comp_apps:
        prefixer = get_url_prefix()
        prefixer.app = list(comp_apps.keys())[0].short
        return redirect('addons.detail', addon.slug, permanent=True)

    ctx = {
        'addon': addon,
        'src': request.GET.get('src', 'dp-btn-primary'),
        'version_src': request.GET.get('src', 'dp-btn-version'),
        'tags': addon.tags.not_denied(),
        'grouped_ratings': GroupedRating.get(addon.id),
        'review_form': RatingForm(),
        'reviews': Rating.without_replies.all().filter(
            addon=addon, is_latest=True).exclude(body=None),
        'get_replies': Rating.get_replies,
        'abuse_form': AbuseForm(request=request),
    }

    # details.html just returns the top half of the page for speed. The bottom
    # does a lot more queries we don't want on the initial page load.
    if request.is_ajax():
        # Other add-ons/apps from the same author(s).
        ctx['author_addons'] = addon.authors_other_addons(app=request.APP)[:6]
        return render(request, 'addons/impala/details-more.html', ctx)
    else:
        return render(request, 'addons/impala/details.html', ctx)
Example #3
0
def test_app_in_fragment_cache_key(cache_mock):
    cache_mock.return_value = ""
    request = mock.Mock()
    request.APP.id = "<app>"
    request.user.is_authenticated.return_value = False
    template = jingo.get_env().from_string("{% cache 1 %}{% endcache %}")
    render(request, template)
    assert cache_mock.call_args[0][0].endswith("<app>")
Example #4
0
def handler404(request, exception=None, **kwargs):
    if request.is_api:
        # It's a v3+ api request
        return JsonResponse(
            {'detail': six.text_type(NotFound.default_detail)}, status=404)
    # X_IS_MOBILE_AGENTS is set by nginx as an env variable when it detects
    # a mobile User Agent or when the mamo cookie is present.
    if request.META.get('X_IS_MOBILE_AGENTS') == '1':
        return render(request, 'amo/404-responsive.html', status=404)
    else:
        return render(request, 'amo/404.html', status=404)
Example #5
0
def handler404(request):
    if request.path_info.startswith('/api/v3/'):
        return JsonResponse(
            {'detail': unicode(NotFound.default_detail)}, status=404)
    elif request.path_info.startswith('/api/'):
        # Pass over to handler404 view in api if api was targeted.
        return legacy_api.views.handler404(request)
    if request.MOBILE:
        return render(request, 'amo/404-responsive.html', status=404)
    else:
        return render(request, 'amo/404.html', status=404)
Example #6
0
def handler404(request):
    if re.match(settings.DRF_API_REGEX, request.path_info):
        return JsonResponse(
            {'detail': unicode(NotFound.default_detail)}, status=404)
    elif request.path_info.startswith('/api/'):
        # Pass over to handler404 view in api if api was targeted.
        return legacy_api.views.handler404(request)
    # X_IS_MOBILE_AGENTS is set by nginx as an env variable when it detects
    # a mobile User Agent or when the mamo cookie is present.
    if request.META.get('X_IS_MOBILE_AGENTS') == '1':
        return render(request, 'amo/404-responsive.html', status=404)
    else:
        return render(request, 'amo/404.html', status=404)
Example #7
0
def handler404(request):
    if request.is_api:
        # It's a v3+ api request
        return JsonResponse(
            {'detail': unicode(NotFound.default_detail)}, status=404)
    elif request.is_legacy_api:
        # It's a legacy api request - pass over to legacy api handler404.
        return legacy_api.views.handler404(request)
    # X_IS_MOBILE_AGENTS is set by nginx as an env variable when it detects
    # a mobile User Agent or when the mamo cookie is present.
    if request.META.get('X_IS_MOBILE_AGENTS') == '1':
        return render(request, 'amo/404-responsive.html', status=404)
    else:
        return render(request, 'amo/404.html', status=404)
Example #8
0
def addon_manage(request, addon):
    form = AddonStatusForm(request.POST or None, instance=addon)
    pager = amo.utils.paginate(
        request, Version.unfiltered.filter(addon=addon), 30)
    # A list coercion so this doesn't result in a subquery with a LIMIT which
    # MySQL doesn't support (at this time).
    versions = list(pager.object_list)
    files = File.objects.filter(version__in=versions).select_related('version')
    formset = FileFormSet(request.POST or None, queryset=files)

    if form.is_valid() and formset.is_valid():
        if 'status' in form.changed_data:
            ActivityLog.create(amo.LOG.CHANGE_STATUS, addon,
                               form.cleaned_data['status'])
            log.info('Addon "%s" status changed to: %s' % (
                addon.slug, form.cleaned_data['status']))
            form.save()

        for form in formset:
            if 'status' in form.changed_data:
                log.info('Addon "%s" file (ID:%d) status changed to: %s' % (
                    addon.slug, form.instance.id, form.cleaned_data['status']))
                form.save()
        return redirect('zadmin.addon_manage', addon.slug)

    # Build a map from file.id to form in formset for precise form display
    form_map = dict((form.instance.id, form) for form in formset.forms)
    # A version to file map to avoid an extra query in the template
    file_map = {}
    for file in files:
        file_map.setdefault(file.version_id, []).append(file)

    return render(request, 'zadmin/addon_manage.html', {
        'addon': addon, 'pager': pager, 'versions': versions, 'form': form,
        'formset': formset, 'form_map': form_map, 'file_map': file_map})
Example #9
0
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))
Example #10
0
def unsubscribe(request, hash=None, token=None, perm_setting=None):
    """
    Pulled from django contrib so that we can add user into the form
    so then we can show relevant messages about the user.
    """
    assert hash is not None and token is not None
    user = None

    try:
        email = UnsubscribeCode.parse(token, hash)
        user = UserProfile.objects.get(email=email)
    except (ValueError, UserProfile.DoesNotExist):
        pass

    perm_settings = []
    if user is not None and perm_setting is not None:
        unsubscribed = True
        perm_setting = notifications.NOTIFICATIONS_BY_SHORT[perm_setting]
        UserNotification.objects.update_or_create(
            user=user, notification_id=perm_setting.id,
            defaults={'enabled': False})
        perm_settings = [perm_setting]
    else:
        unsubscribed = False
        email = ''

    return render(request, 'users/unsubscribe.html',
                  {'unsubscribed': unsubscribed, 'email': email,
                   'perm_settings': perm_settings})
Example #11
0
def monthly_pick(request):
    form = MonthlyPickFormSet(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        form.save()
        messages.success(request, 'Changes successfully saved.')
        return redirect('zadmin.monthly_pick')
    return render(request, 'zadmin/monthly_pick.html', dict(form=form))
Example #12
0
def stats_report(request, addon, report):
    check_stats_permission(request, addon)
    stats_base_url = reverse('stats.overview', args=[addon.slug])
    view = get_report_view(request)
    return render(request, 'stats/reports/%s.html' % report,
                  {'addon': addon, 'report': report, 'view': view,
                   'stats_base_url': stats_base_url})
Example #13
0
def addon_detail(request, addon):
    """Add-ons details page dispatcher."""
    if addon.is_deleted or (addon.is_pending() and not addon.is_persona()):
        # Allow pending themes to be listed.
        raise http.Http404
    if addon.is_disabled:
        return render(request, 'addons/impala/disabled.html',
                      {'addon': addon}, status=404)

    # addon needs to have a version and be valid for this app.
    if addon.type in request.APP.types:
        if addon.type == amo.ADDON_PERSONA:
            return persona_detail(request, addon)
        else:
            if not addon.current_version:
                raise http.Http404
            return extension_detail(request, addon)
    else:
        # Redirect to an app that supports this type.
        try:
            new_app = [a for a in amo.APP_USAGE if addon.type
                       in a.types][0]
        except IndexError:
            raise http.Http404
        else:
            prefixer = urlresolvers.get_url_prefix()
            prefixer.app = new_app.short
            return http.HttpResponsePermanentRedirect(reverse(
                'addons.detail', args=[addon.slug]))
Example #14
0
def themes(request, user, category=None):
    cats = Category.objects.filter(type=amo.ADDON_PERSONA)

    ctx = {
        'profile': user,
        'categories': sorted(cats, key=attrgetter('weight', 'name')),
        'search_cat': 'themes'
    }

    if user.is_artist:
        base = user.addons.public().filter(
            type=amo.ADDON_PERSONA,
            addonuser__user=user, addonuser__listed=True)

        if category:
            qs = cats.filter(slug=category)
            ctx['category'] = cat = get_list_or_404(qs)[0]
            base = base.filter(categories__id=cat.id)

    else:
        base = Addon.objects.none()

    filter_ = PersonasFilter(request, base, key='sort',
                             default='popular')
    addons = amo.utils.paginate(request, filter_.qs, 30,
                                count=base.count())

    ctx.update({
        'addons': addons,
        'filter': filter_,
        'sorting': filter_.field,
        'sort_opts': filter_.opts
    })

    return render(request, 'browse/personas/grid.html', ctx)
Example #15
0
def promos(request, context, version, platform, compat_mode='strict'):
    if platform:
        platform = platform.lower()
    platform = amo.PLATFORM_DICT.get(platform, amo.PLATFORM_ALL)
    modules = get_modules(request, platform.api_name, version)
    return render(request, 'addons/impala/homepage_promos.html',
                  {'modules': modules, 'module_context': context})
Example #16
0
def home(request):
    # Add-ons.
    base = Addon.objects.listed(request.APP).filter(type=amo.ADDON_EXTENSION)
    # This is lame for performance. Kill it with ES.
    frozen = list(FrozenAddon.objects.values_list('addon', flat=True))

    # We want to display 6 Featured Extensions, Up & Coming Extensions and
    # Featured Themes.
    featured = Addon.objects.featured(request.APP, request.LANG,
                                      amo.ADDON_EXTENSION)[:6]
    hotness = base.exclude(id__in=frozen).order_by('-hotness')[:6]
    personas = Addon.objects.featured(request.APP, request.LANG,
                                      amo.ADDON_PERSONA)[:6]

    # Most Popular extensions is a simple links list, we display slightly more.
    popular = base.exclude(id__in=frozen).order_by('-average_daily_users')[:10]

    # We want a maximum of 6 Featured Collections as well (though we may get
    # fewer than that).
    collections = Collection.objects.filter(listed=True,
                                            application=request.APP.id,
                                            type=amo.COLLECTION_FEATURED)[:6]

    return render(request, 'addons/home.html',
                  {'popular': popular, 'featured': featured,
                   'hotness': hotness, 'personas': personas,
                   'src': 'homepage', 'collections': collections})
Example #17
0
def compare(request, diff, key=None, type='file'):
    form = forms.FileCompareForm(request.POST or None, addon=diff.addon,
                                 initial={'left': diff.left.file,
                                          'right': diff.right.file},
                                 request=request)
    response = check_compare_form(request, form)
    if response:
        return response

    data = setup_viewer(request, diff.left.file)
    data['diff'] = diff
    data['poll_url'] = reverse('files.compare.poll',
                               args=[diff.left.file.id,
                                     diff.right.file.id])
    data['form'] = form

    if not diff.is_extracted():
        extract_file(diff.left)
        extract_file(diff.right)

    if diff.is_extracted():
        data.update({'status': True,
                     'files': diff.get_files(),
                     'files_deleted': diff.get_deleted_files()})
        key = diff.left.get_default(key)
        if key not in data['files'] and key not in data['files_deleted']:
            raise http.Http404

        diff.select(key)
        data['key'] = key
        if diff.is_diffable():
            data['left'], data['right'] = diff.read_file()

    tmpl = 'files/content.html' if type == 'fragment' else 'files/viewer.html'
    return render(request, tmpl, data)
Example #18
0
def home(request):
    # Shuffle the list and get 3 items.
    def rand(xs):
        return random.shuffle(xs) or xs[:3]

    # Get some featured add-ons with randomness.
    featured = Addon.featured_random(request.APP, request.LANG)[:3]
    # Get 10 popular add-ons, then pick 3 at random.
    qs = list(Addon.objects.listed(request.APP)
                   .filter(type=amo.ADDON_EXTENSION)
                   .order_by('-average_daily_users')
                   .values_list('id', flat=True)[:10])
    popular = rand(qs)
    # Do one query and split up the add-ons.
    addons = (Addon.objects.filter(id__in=featured + popular)
              .filter(type=amo.ADDON_EXTENSION))
    featured = [a for a in addons if a.id in featured]
    popular = sorted([a for a in addons if a.id in popular],
                     key=attrgetter('average_daily_users'), reverse=True)

    if not request.META.get('HTTP_USER_AGENT'):
        ios_user = False
    else:
        ios_user = '******' in request.META.get('HTTP_USER_AGENT')
    return render(request, 'addons/mobile/home.html',
                  {'featured': featured, 'popular': popular,
                   'ios_user': ios_user})
Example #19
0
def browse(request, viewer, key=None, type='file'):
    form = forms.FileCompareForm(request.POST or None, addon=viewer.addon,
                                 initial={'left': viewer.file},
                                 request=request)
    response = check_compare_form(request, form)
    if response:
        return response

    data = setup_viewer(request, viewer.file)
    data['viewer'] = viewer
    data['poll_url'] = reverse('files.poll', args=[viewer.file.id])
    data['form'] = form

    if not viewer.is_extracted():
        extract_file(viewer)

    if viewer.is_extracted():
        data.update({'status': True, 'files': viewer.get_files()})
        key = viewer.get_default(key)
        if key not in data['files']:
            raise http.Http404

        viewer.select(key)
        data['key'] = key
        if (not viewer.is_directory() and not viewer.is_binary()):
            data['content'] = viewer.read_file()

    tmpl = 'files/content.html' if type == 'fragment' else 'files/viewer.html'
    return render(request, tmpl, data)
def deleted_themes(request):
    data = request.GET.copy()
    deleted = Addon.unfiltered.filter(type=amo.ADDON_PERSONA,
                                      status=amo.STATUS_DELETED)

    if not data.get('start') and not data.get('end'):
        today = datetime.date.today()
        data['start'] = datetime.date(today.year, today.month, 1)

    form = forms.DeletedThemeLogForm(data)
    if form.is_valid():
        data = form.cleaned_data
        if data.get('start'):
            deleted = deleted.filter(modified__gte=data['start'])
        if data.get('end'):
            deleted = deleted.filter(modified__lte=data['end'])
        if data.get('search'):
            term = data['search']
            deleted = deleted.filter(
                Q(name__localized_string__icontains=term))

    return render(request, 'reviewers/themes/deleted.html', {
        'form': form,
        'pager': paginate(request, deleted.order_by('-modified'), 30),
        'tab': 'deleted'
    })
Example #21
0
    def add_view(self, request, form_url='', extra_context=None):
        """Override the default admin add view for bulk add."""
        form = self.model_add_form()
        if request.method == 'POST':
            form = self.model_add_form(request.POST)
            if form.is_valid():
                inserted = 0
                duplicates = 0

                for x in form.cleaned_data[self.add_form_field].splitlines():
                    # check with the cache
                    if self.deny_list_model.blocked(x):
                        duplicates += 1
                        continue
                    try:
                        self.deny_list_model.objects.create(
                            **{self.model_field: x.lower()})
                        inserted += 1
                    except IntegrityError:
                        # although unlikely, someone else could have added
                        # the same value.
                        # note: unless we manage the transactions manually,
                        # we do lose a primary id here.
                        duplicates += 1
                msg = '%s new values added to the deny list.' % (inserted)
                if duplicates:
                    msg += ' %s duplicates were ignored.' % (duplicates)
                messages.success(request, msg)
                form = self.model_add_form()
        return render(request, self.template_path, {'form': form})
def themes_list(request, flagged=False, rereview=False):
    """Themes queue in list format."""
    themes = []
    if flagged:
        # TODO (ngoke): rename to STATUS_FLAGGED.
        themes = Addon.objects.filter(status=amo.STATUS_REVIEW_PENDING,
                                      type=amo.ADDON_PERSONA,
                                      persona__isnull=False)
    elif rereview:
        themes = [
            rqt.theme.addon for rqt in
            RereviewQueueTheme.objects.select_related('theme__addon')]
    else:
        themes = Addon.objects.filter(status=amo.STATUS_PENDING,
                                      type=amo.ADDON_PERSONA,
                                      persona__isnull=False)

    search_form = forms.ThemeSearchForm(request.GET)
    per_page = request.GET.get('per_page', QUEUE_PER_PAGE)
    pager = paginate(request, themes, per_page)

    return render(request, 'reviewers/themes/queue_list.html', context(
        **{'addons': pager.object_list,
           'flagged': flagged,
           'pager': pager,
           'rereview': rereview,
           'theme_search_form': search_form,
           'statuses': dict((k, unicode(v)) for k, v in
                            amo.STATUS_CHOICES_API.items()),
           'tab': ('rereview_themes' if rereview else
                   'flagged_themes' if flagged else 'pending_themes')}))
def themes_logs(request):
    data = request.GET.copy()

    if not data.get('start') and not data.get('end'):
        today = datetime.date.today()
        data['start'] = datetime.date(today.year, today.month, 1)

    form = forms.ReviewThemeLogForm(data)

    theme_logs = ActivityLog.objects.filter(action=amo.LOG.THEME_REVIEW.id)

    if form.is_valid():
        data = form.cleaned_data
        if data.get('start'):
            theme_logs = theme_logs.filter(created__gte=data['start'])
        if data.get('end'):
            theme_logs = theme_logs.filter(created__lte=data['end'])
        if data.get('search'):
            term = data['search']
            theme_logs = theme_logs.filter(
                Q(_details__icontains=term) |
                Q(user__display_name__icontains=term) |
                Q(user__username__icontains=term)).distinct()

    pager = paginate(request, theme_logs, 30)
    data = context(form=form, pager=pager,
                   ACTION_DICT=amo.REVIEW_ACTIONS,
                   REJECT_REASONS=amo.THEME_REJECT_REASONS, tab='themes')
    return render(request, 'reviewers/themes/logs.html', data)
Example #24
0
def eventlog_detail(request, id):
    log = get_object_or_404(ActivityLog.objects.editor_events(), pk=id)

    review = None
    # I really cannot express the depth of the insanity incarnate in
    # our logging code...
    if len(log.arguments) > 1 and isinstance(log.arguments[1], Review):
        review = log.arguments[1]

    is_admin = acl.action_allowed(request, 'ReviewerAdminTools', 'View')

    can_undelete = review and review.deleted and (
        is_admin or request.user.pk == log.user.pk)

    if request.method == 'POST':
        # A Form seems overkill for this.
        if request.POST['action'] == 'undelete':
            if not can_undelete:
                raise PermissionDenied

            ReviewerScore.award_moderation_points(
                log.user, review.addon, review.id, undo=True)
            review.undelete()
        return redirect('editors.eventlog.detail', id)

    data = context(request, log=log, can_undelete=can_undelete)
    return render(request, 'editors/eventlog_detail.html', data)
Example #25
0
def extension_detail(request, addon):
    if not request.META.get('HTTP_USER_AGENT'):
        ios_user = False
    else:
        ios_user = '******' in request.META.get('HTTP_USER_AGENT')
    return render(request, 'addons/mobile/details.html',
                  {'addon': addon, 'ios_user': ios_user})
Example #26
0
def extensions(request, category=None):
    TYPE = amo.ADDON_EXTENSION

    if category is not None:
        q = Category.objects.filter(application=request.APP.id, type=TYPE)
        category = get_object_or_404(q, slug=category)

    sort = request.GET.get('sort')
    if not sort and category and category.count > 4:
        return category_landing(request, category)

    addons, filter = addon_listing(request, [TYPE])
    sorting = filter.field
    src = 'cb-btn-%s' % sorting
    dl_src = 'cb-dl-%s' % sorting

    if category:
        addons = addons.filter(categories__id=category.id)

    addons = amo.utils.paginate(request, addons, count=addons.count())
    return render(request, 'browse/extensions.html',
                  {'section': 'extensions', 'addon_type': TYPE,
                   'category': category, 'addons': addons,
                   'filter': filter, 'sorting': sorting,
                   'sort_opts': filter.opts, 'src': src,
                   'dl_src': dl_src, 'search_cat': '%s,0' % TYPE})
Example #27
0
def queue_moderated(request):
    qs = Review.objects.all().to_moderate().order_by('reviewflag__created')
    page = paginate(request, qs, per_page=20)
    motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit')

    flags = dict(ReviewFlag.FLAGS)

    reviews_formset = ReviewFlagFormSet(request.POST or None,
                                        queryset=page.object_list,
                                        request=request)

    if request.method == 'POST':
        if reviews_formset.is_valid():
            reviews_formset.save()
        else:
            amo.messages.error(
                request, ' '.join(e.as_text() or _('An unknown error occurred')
                                  for e in reviews_formset.errors))
        return redirect(reverse('editors.queue_moderated'))

    return render(request, 'editors/queue.html',
                  context(request, reviews_formset=reviews_formset,
                          tab='moderated', page=page, flags=flags,
                          search_form=None,
                          point_types=amo.REVIEWED_AMO,
                          motd_editable=motd_editable))
Example #28
0
def home(request):
    data = context(
        reviews_total=ActivityLog.objects.total_reviews(theme=True)[:5],
        reviews_monthly=ActivityLog.objects.monthly_reviews(theme=True)[:5],
        queue_counts=queue_counts_themes(request)
    )
    return render(request, 'editors/themes/home.html', data)
Example #29
0
def compat(request):
    minimum = 10
    ratio = .8
    binary = None

    # Expected usage:
    #     For Firefox 8.0 reports:      ?appver=1-8.0
    #     For over 70% incompatibility: ?appver=1-8.0&ratio=0.7
    #     For binary-only add-ons:      ?appver=1-8.0&type=binary
    data = {'appver': '%s' % FIREFOX_COMPAT[0]['main'],
            'minimum': minimum, 'ratio': ratio, 'type': 'all'}
    version = data['appver']
    data.update(request.GET.items())

    form = CompatForm(data)
    if request.GET and form.is_valid():
        version = form.cleaned_data['appver']
        if form.cleaned_data['ratio'] is not None:
            ratio = float(form.cleaned_data['ratio'])
        if form.cleaned_data['minimum'] is not None:
            minimum = int(form.cleaned_data['minimum'])
        if form.cleaned_data['type'] == 'binary':
            binary = True
    usage_addons, usage_total = compat_stats(
        request, version, minimum, ratio, binary)

    return render(request, 'zadmin/compat.html', {
        'form': form, 'usage_addons': usage_addons,
        'usage_total': usage_total})
Example #30
0
def render_cat(request, template, data=None, extra=None):
    if extra is None:
        extra = {}
    if data is None:
        data = {}
    data.update(dict(search_cat='collections'))
    return render(request, template, data, **extra)
Example #31
0
def mail(request):
    backend = DevEmailBackend()
    if request.method == 'POST':
        backend.clear()
        return redirect('zadmin.mail')
    return render(request, 'zadmin/mail.html', dict(mail=backend.view_all()))
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')
            }))
Example #33
0
def review(request, addon, channel=None):
    # channel is passed in as text, but we want the constant.
    channel = amo.CHANNEL_CHOICES_LOOKUP.get(
        channel, amo.RELEASE_CHANNEL_LISTED)
    unlisted_only = (channel == amo.RELEASE_CHANNEL_UNLISTED or
                     not addon.has_listed_versions())
    if unlisted_only and not acl.check_unlisted_addons_reviewer(request):
        raise PermissionDenied

    version = addon.find_latest_version(
        channel=channel, exclude=(amo.STATUS_BETA,))

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(
            request, ugettext('Self-reviews are not allowed.'))
        return redirect(reverse('editors.queue'))

    form_helper = ReviewHelper(request=request, addon=addon, version=version)
    form = forms.ReviewForm(request.POST if request.method == 'POST' else None,
                            helper=form_helper)
    if channel == amo.RELEASE_CHANNEL_LISTED:
        queue_type = form.helper.handler.review_type
        redirect_url = reverse('editors.queue_%s' % queue_type)
    else:
        redirect_url = reverse('editors.unlisted_queue_all')

    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
    is_post_reviewer = acl.action_allowed(request,
                                          amo.permissions.ADDONS_POST_REVIEW)

    approvals_info = None
    reports = None
    user_reviews = None
    was_auto_approved = False
    if channel == amo.RELEASE_CHANNEL_LISTED:
        if addon.current_version:
            was_auto_approved = addon.current_version.was_auto_approved
        if is_post_reviewer and version and version.is_webextension:
            try:
                approvals_info = addon.addonapprovalscounter
            except AddonApprovalsCounter.DoesNotExist:
                pass

        developers = addon.listed_authors
        reports = Paginator(
            (AbuseReport.objects
                        .filter(Q(addon=addon) | Q(user__in=developers))
                        .order_by('-created')), 5).page(1)
        user_reviews = Paginator(
            (Review.without_replies
                   .filter(addon=addon, rating__lte=3, body__isnull=False)
                   .order_by('-created')), 5).page(1)

    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, ugettext('Review successfully processed.'))
        clear_reviewing_cache(addon.id)
        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.all_files:
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    canned = AddonCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        show_diff = version and (
            addon.versions.exclude(id=version.id).filter(
                channel=channel,
                files__isnull=False,
                created__lt=version.created,
                files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form for.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # The actions we should show the comments form for (contrary to minimal
    # form above, it defaults to True, because most actions do need to have
    # the comments form).
    actions_comments = [k for (k, a) in actions if a.get('comments', True)]

    versions = (Version.unfiltered.filter(addon=addon, channel=channel)
                                  .select_related('autoapprovalsummary')
                                  .exclude(files__status=amo.STATUS_BETA)
                                  .order_by('-created')
                                  .transform(Version.transformer_activity)
                                  .transform(Version.transformer))

    # We assume comments on old deleted versions are for listed versions.
    # See _get_comments_for_hard_deleted_versions above for more detail.
    all_versions = (_get_comments_for_hard_deleted_versions(addon)
                    if channel == amo.RELEASE_CHANNEL_LISTED else [])
    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

    max_average_daily_users = int(
        get_config('AUTO_APPROVAL_MAX_AVERAGE_DAILY_USERS') or 0),
    min_approved_updates = int(
        get_config('AUTO_APPROVAL_MIN_APPROVED_UPDATES') or 0)
    auto_approval_info = {}
    # Now that we've paginated the versions queryset, iterate on them to
    # generate auto approvals info. Note that the variable should not clash
    # the already existing 'version'.
    for a_version in pager.object_list:
        if not is_post_reviewer or not a_version.is_ready_for_auto_approval:
            continue
        try:
            summary = a_version.autoapprovalsummary
        except AutoApprovalSummary.DoesNotExist:
            auto_approval_info[a_version.pk] = None
            continue
        # Call calculate_verdict() again, it will use the data already stored.
        # Need to pass max_average_daily_users and min_approved_updates current
        # values.
        verdict_info = summary.calculate_verdict(
            max_average_daily_users=max_average_daily_users,
            min_approved_updates=min_approved_updates,
            pretty=True)
        auto_approval_info[a_version.pk] = verdict_info

    if version:
        flags = get_flags(version)
    else:
        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,
                  actions_comments=actions_comments,
                  whiteboard_form=forms.WhiteboardForm(instance=addon),
                  user_changes=user_changes_log,
                  unlisted=(channel == amo.RELEASE_CHANNEL_UNLISTED),
                  approvals_info=approvals_info,
                  is_post_reviewer=is_post_reviewer,
                  auto_approval_info=auto_approval_info,
                  reports=reports, user_reviews=user_reviews,
                  was_auto_approved=was_auto_approved)

    return render(request, 'editors/review.html', ctx)
Example #34
0
def js(request):
    return render(request,
                  'addons/popups.html',
                  content_type='text/javascript')
Example #35
0
def _queue(request,
           TableObj,
           tab,
           qs=None,
           unlisted=False,
           SearchForm=QueueSearchForm):
    if qs is None:
        qs = TableObj.Meta.model.objects.all()

    if SearchForm:
        if request.GET:
            search_form = SearchForm(request.GET)
            if search_form.is_valid():
                qs = search_form.filter_qs(qs)
        else:
            search_form = SearchForm()
        is_searching = search_form.data.get('searching')
    else:
        search_form = None
        is_searching = False
    admin_reviewer = is_admin_reviewer(request)

    # Those restrictions will only work with our RawSQLModel, so we need to
    # make sure we're not dealing with a regular Django ORM queryset first.
    if hasattr(qs, 'sql_model'):
        if not is_searching and not admin_reviewer:
            qs = filter_admin_review_for_legacy_queue(qs)
        if not unlisted:
            if is_limited_reviewer(request):
                qs = qs.having('waiting_time_hours >=',
                               amo.REVIEW_LIMITED_DELAY_HOURS)

            qs = filter_static_themes(
                qs, acl.action_allowed(request, amo.permissions.ADDONS_REVIEW),
                acl.action_allowed(request,
                                   amo.permissions.STATIC_THEMES_REVIEW))
            # Most WebExtensions are picked up by auto_approve cronjob, they
            # don't need to appear in the queues, unless auto approvals have
            # been disabled for them.  Webextension static themes aren't auto
            # approved.
            qs = qs.filter(
                Q(addon_type_id=amo.ADDON_STATICTHEME)
                | Q(**{'files.is_webextension': False}) |
                Q(**{'addons_addonreviewerflags.auto_approval_disabled': True})
            )

    order_by = request.GET.get('sort', TableObj.default_order_by())
    if hasattr(TableObj, 'translate_sort_cols'):
        order_by = TableObj.translate_sort_cols(order_by)
    table = TableObj(data=qs, order_by=order_by)
    per_page = request.GET.get('per_page', REVIEWS_PER_PAGE)
    try:
        per_page = int(per_page)
    except ValueError:
        per_page = REVIEWS_PER_PAGE
    if per_page <= 0 or per_page > REVIEWS_PER_PAGE_MAX:
        per_page = REVIEWS_PER_PAGE
    page = paginate(request, table.rows, per_page=per_page)
    table.set_page(page)
    return render(
        request, 'reviewers/queue.html',
        context(request,
                table=table,
                page=page,
                tab=tab,
                search_form=search_form,
                point_types=amo.REVIEWED_AMO,
                unlisted=unlisted))
Example #36
0
def credits(request):

    developers = (UserProfile.objects
                  .exclude(display_name=None)
                  .filter(groupuser__group__name='Developers Credits')
                  .order_by('display_name')
                  .distinct())
    past_developers = (UserProfile.objects
                       .exclude(display_name=None)
                       .filter(
                           groupuser__group__name='Past Developers Credits')
                       .order_by('display_name')
                       .distinct())
    other_contribs = (UserProfile.objects
                      .exclude(display_name=None)
                      .filter(
                          groupuser__group__name='Other Contributors Credits')
                      .order_by('display_name')
                      .distinct())

    languages = sorted(list(set(settings.AMO_LANGUAGES) - set(['en-US'])))

    localizers = []
    for lang in languages:
        users = (UserProfile.objects
                 .exclude(display_name=None)
                 .filter(groupuser__group__name='%s Localizers' % lang)
                 .order_by('display_name')
                 .distinct())
        if users:
            localizers.append((lang, users))

    total_reviews = (ActivityLog.objects.total_reviews()
                                        .filter(approval_count__gt=10))
    reviewers = defaultdict(list)
    for total in total_reviews:
        cnt = total.get('approval_count', 0)
        if cnt > 10000:
            reviewers[10000].append(total)
        elif cnt > 5000:
            reviewers[5000].append(total)
        elif cnt > 2000:
            reviewers[2000].append(total)
        elif cnt > 1000:
            reviewers[1000].append(total)
        elif cnt > 500:
            reviewers[500].append(total)
        elif cnt > 100:
            reviewers[100].append(total)
        elif cnt > 10:
            reviewers[10].append(total)

    context = {
        'developers': developers,
        'past_developers': past_developers,
        'other_contribs': other_contribs,
        'localizers': localizers,
        'reviewers': reviewers,
    }

    return render(request, 'pages/credits.html', context)
Example #37
0
def appversions(request):
    apps, versions = get_versions()
    return render(request, 'applications/appversions.html', {
        'apps': apps,
        'versions': versions
    })
Example #38
0
def privacy(request, addon):
    if not addon.privacy_policy:
        return http.HttpResponseRedirect(addon.get_url_path())

    return render(request, 'addons/privacy.html', {'addon': addon})
Example #39
0
def login(request):
    return render(request, 'users/login.html')
Example #40
0
def leaderboard(request):
    return render(request, 'editors/leaderboard.html', context(
        request, scores=ReviewerScore.all_users_by_score()))
Example #41
0
def find_replacement_addon(request):
    guid = request.GET.get('guid')
    if not guid:
        raise http.Http404
    return render(request, 'addons/replacement_addons.html')
Example #42
0
def performance(request, user_id=False):
    user = request.user
    editors = _recent_editors()

    is_admin = (acl.action_allowed(request, amo.permissions.ADMIN) or
                acl.action_allowed(request,
                                   amo.permissions.REVIEWER_ADMIN_TOOLS_VIEW))

    if is_admin and user_id:
        try:
            user = UserProfile.objects.get(pk=user_id)
        except UserProfile.DoesNotExist:
            pass  # Use request.user from above.

    motd_editable = acl.action_allowed(
        request, amo.permissions.ADDON_REVIEWER_MOTD_EDIT)

    monthly_data = _performance_by_month(user.id)
    performance_total = _performance_total(monthly_data)

    # Incentive point breakdown.
    today = date.today()
    month_ago = today - timedelta(days=30)
    year_ago = today - timedelta(days=365)
    point_total = ReviewerScore.get_total(user)
    totals = ReviewerScore.get_breakdown(user)
    months = ReviewerScore.get_breakdown_since(user, month_ago)
    years = ReviewerScore.get_breakdown_since(user, year_ago)

    def _sum(iter, types, exclude=False):
        """Sum the `total` property for items in `iter` that have an `atype`
        that is included in `types` when `exclude` is False (default) or not in
        `types` when `exclude` is True."""
        return sum(s.total
                   for s in iter
                   if (s.atype in types) == (not exclude))

    breakdown = {
        'month': {
            'addons': _sum(months, amo.GROUP_TYPE_ADDON),
            'themes': _sum(months, amo.GROUP_TYPE_THEME),
            'other': _sum(months, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME,
                          exclude=True)
        },
        'year': {
            'addons': _sum(years, amo.GROUP_TYPE_ADDON),
            'themes': _sum(years, amo.GROUP_TYPE_THEME),
            'other': _sum(years, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME,
                          exclude=True)
        },
        'total': {
            'addons': _sum(totals, amo.GROUP_TYPE_ADDON),
            'themes': _sum(totals, amo.GROUP_TYPE_THEME),
            'other': _sum(totals, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME,
                          exclude=True)
        }
    }

    data = context(request,
                   monthly_data=json.dumps(monthly_data),
                   performance_month=performance_total['month'],
                   performance_year=performance_total['year'],
                   breakdown=breakdown, point_total=point_total,
                   editors=editors, current_user=user, is_admin=is_admin,
                   is_user=(request.user.id == user.id),
                   motd_editable=motd_editable)

    return render(request, 'editors/performance.html', data)
Example #43
0
def handler500(request):
    if request.is_legacy_api:
        # Pass over to handler500 view in api if api was targeted.
        return legacy_api.views.handler500(request)
    else:
        return render(request, 'amo/500.html', status=500)
Example #44
0
def search(request, tag_name=None):
    APP = request.APP
    types = (amo.ADDON_EXTENSION, amo.ADDON_THEME, amo.ADDON_DICT,
             amo.ADDON_SEARCH, amo.ADDON_LPAPP)

    category = request.GET.get('cat')

    if category == 'collections':
        extra_params = {'sort': {'newest': 'created'}}
    else:
        extra_params = None

    fixed = fix_search_query(request.GET, extra_params=extra_params)
    if fixed is not request.GET:
        # We generally want a 301, except if it's a "type", because that's only
        # here to support the new frontend, so a permanent redirect could mess
        # things up when the user is going back and forth between the old and
        # new frontend. https://github.com/mozilla/addons-server/issues/6846
        status = 302 if 'type' in request.GET else 301
        return http.HttpResponseRedirect(
            urlparams(request.path, **fixed), status=status)

    facets = request.GET.copy()

    # In order to differentiate between "all versions" and an undefined value,
    # we use "any" instead of "" in the frontend.
    if 'appver' in facets and facets['appver'] == 'any':
        facets['appver'] = ''

    form = ESSearchForm(facets or {})
    form.is_valid()  # Let the form try to clean data.

    form_data = form.cleaned_data
    if tag_name:
        form_data['tag'] = tag_name

    if category == 'themes' or form_data.get('atype') == amo.ADDON_PERSONA:
        return _personas(request)

    sort, extra_sort = split_choices(form.sort_choices, 'created')
    if form_data.get('atype') == amo.ADDON_SEARCH:
        # Search add-ons should not be searched by ADU, so replace 'Users'
        # sort with 'Weekly Downloads'.
        sort, extra_sort = list(sort), list(extra_sort)
        sort[1] = extra_sort[1]
        del extra_sort[1]

    # Perform search, using aggregation so that we can build the facets UI.
    # Note that we don't need to aggregate on platforms, that facet it built
    # from our constants directly, using the current application for this
    # request (request.APP).
    appversion_field = 'current_version.compatible_apps.%s.max' % APP.id
    qs = (Addon.search_public().filter(app=APP.id)
          .aggregate(tags={'terms': {'field': 'tags'}},
                     appversions={'terms': {'field': appversion_field}},
                     categories={'terms': {'field': 'category', 'size': 200}})
          )

    filters = ['atype', 'appver', 'cat', 'sort', 'tag', 'platform']
    mapping = {'users': '-average_daily_users',
               'rating': '-bayesian_rating',
               'created': '-created',
               'name': 'name.raw',
               'downloads': '-weekly_downloads',
               'updated': '-last_updated',
               'hotness': '-hotness'}
    qs = _filter_search(request, qs, form_data, filters, mapping, types=types)

    pager = amo.utils.paginate(request, qs)

    ctx = {
        'is_pjax': request.META.get('HTTP_X_PJAX'),
        'pager': pager,
        'query': form_data,
        'form': form,
        'sort_opts': sort,
        'extra_sort_opts': extra_sort,
        'sorting': sort_sidebar(request, form_data, form),
        'sort': form_data.get('sort'),
    }
    if not ctx['is_pjax']:
        aggregations = pager.object_list.aggregations
        ctx.update({
            'tag': tag_name,
            'categories': category_sidebar(request, form_data, aggregations),
            'platforms': platform_sidebar(request, form_data),
            'versions': version_sidebar(request, form_data, aggregations),
            'tags': tag_sidebar(request, form_data, aggregations),
        })
    return render(request, 'search/results.html', ctx)
Example #45
0
def _queue(request,
           TableObj,
           tab,
           qs=None,
           unlisted=False,
           SearchForm=forms.QueueSearchForm):
    if qs is None:
        qs = TableObj.Meta.model.objects.all()

    if SearchForm:
        if request.GET:
            search_form = SearchForm(request.GET)
            if search_form.is_valid():
                qs = search_form.filter_qs(qs)
        else:
            search_form = SearchForm()
        is_searching = search_form.data.get('searching')
    else:
        search_form = None
        is_searching = False
    admin_reviewer = is_admin_reviewer(request)

    if hasattr(qs, 'filter'):
        if not is_searching and not admin_reviewer:
            qs = exclude_admin_only_addons(qs)

        # Those additional restrictions will only work with our RawSQLModel,
        # so we need to make sure we're not dealing with a regular Django ORM
        # queryset first.
        if hasattr(qs, 'sql_model') and not unlisted:
            if is_limited_reviewer(request):
                qs = qs.having('waiting_time_hours >=',
                               REVIEW_LIMITED_DELAY_HOURS)

            if waffle.switch_is_active('post-review'):
                # Hide webextensions from the queues so that human reviewers
                # don't pick them up: auto-approve cron should take care of
                # them.
                qs = qs.filter(**{'files.is_webextension': False})

    motd_editable = acl.action_allowed(
        request, amo.permissions.ADDON_REVIEWER_MOTD_EDIT)
    order_by = request.GET.get('sort', TableObj.default_order_by())
    if hasattr(TableObj, 'translate_sort_cols'):
        order_by = TableObj.translate_sort_cols(order_by)
    table = TableObj(data=qs, order_by=order_by)
    per_page = request.GET.get('per_page', REVIEWS_PER_PAGE)
    try:
        per_page = int(per_page)
    except ValueError:
        per_page = REVIEWS_PER_PAGE
    if per_page <= 0 or per_page > REVIEWS_PER_PAGE_MAX:
        per_page = REVIEWS_PER_PAGE
    page = paginate(request, table.rows, per_page=per_page)
    table.set_page(page)
    return render(
        request, 'editors/queue.html',
        context(request,
                table=table,
                page=page,
                tab=tab,
                search_form=search_form,
                point_types=amo.REVIEWED_AMO,
                unlisted=unlisted,
                motd_editable=motd_editable))
Example #46
0
def handler403(request, exception=None, **kwargs):
    return render(request, 'amo/403.html', status=403)
Example #47
0
def dashboard(request):
    stats_base_url = reverse('stats.dashboard')
    view = get_report_view(request)
    return render(request, 'stats/dashboard.html',
                  {'report': 'site', 'view': view,
                   'stats_base_url': stats_base_url})
Example #48
0
def export_email_addresses(request):
    return render(request, 'zadmin/export_button.html', {})
Example #49
0
def review(request, addon, channel=None):
    whiteboard_url = reverse('reviewers.whiteboard',
                             args=(channel or 'listed',
                                   addon.slug if addon.slug else addon.pk))
    channel, content_review_only = determine_channel(channel)

    was_auto_approved = (channel == amo.RELEASE_CHANNEL_LISTED
                         and addon.current_version
                         and addon.current_version.was_auto_approved)
    is_static_theme = addon.type == amo.ADDON_STATICTHEME

    # If we're just looking (GET) we can bypass the specific permissions checks
    # if we have ReviewerTools:View.
    bypass_more_specific_permissions_because_read_only = (
        request.method == 'GET'
        and acl.action_allowed(request, amo.permissions.REVIEWER_TOOLS_VIEW))

    if not bypass_more_specific_permissions_because_read_only:
        perform_review_permission_checks(
            request, addon, channel, content_review_only=content_review_only)

    version = addon.find_latest_version(channel=channel, exclude=())

    if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.user):
        amo.messages.warning(request,
                             ugettext('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.queue'))

    # Get the current info request state to set as the default.
    form_initial = {'info_request': addon.pending_info_request}

    form_helper = ReviewHelper(request=request,
                               addon=addon,
                               version=version,
                               content_review_only=content_review_only)
    form = ReviewForm(request.POST if request.method == 'POST' else None,
                      helper=form_helper,
                      initial=form_initial)
    is_admin = acl.action_allowed(request, amo.permissions.REVIEWS_ADMIN)

    approvals_info = None
    reports = None
    user_ratings = None
    if channel == amo.RELEASE_CHANNEL_LISTED:
        if was_auto_approved:
            try:
                approvals_info = addon.addonapprovalscounter
            except AddonApprovalsCounter.DoesNotExist:
                pass

        developers = addon.listed_authors
        reports = Paginator((AbuseReport.objects.filter(
            Q(addon=addon) | Q(user__in=developers)).order_by('-created')),
                            5).page(1)
        user_ratings = Paginator((Rating.without_replies.filter(
            addon=addon, rating__lte=3,
            body__isnull=False).order_by('-created')), 5).page(1)

        if content_review_only:
            queue_type = 'content_review'
        elif was_auto_approved:
            queue_type = 'auto_approved'
        else:
            queue_type = form.helper.handler.review_type
        redirect_url = reverse('reviewers.queue_%s' % queue_type)
    else:
        redirect_url = reverse('reviewers.unlisted_queue_all')

    if request.method == 'POST' and form.is_valid():
        form.helper.process()
        amo.messages.success(request,
                             ugettext('Review successfully processed.'))
        clear_reviewing_cache(addon.id)
        return redirect(redirect_url)

    # Kick off validation tasks for any files in this version which don't have
    # cached validation, since reviewers 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.all_files:
            if not file_.has_been_validated:
                devhub_tasks.validate(file_)

    actions = form.helper.actions.items()

    try:
        # Find the previously approved version to compare to.
        show_diff = version and (addon.versions.exclude(id=version.id).filter(
            # We're looking for a version that was either manually approved
            # (either it has no auto approval summary, or it has one but
            # with a negative verdict because it was locked by a reviewer
            # who then approved it themselves), or auto-approved but then
            # confirmed.
            Q(autoapprovalsummary__isnull=True)
            | Q(autoapprovalsummary__verdict=amo.NOT_AUTO_APPROVED)
            | Q(autoapprovalsummary__verdict=amo.AUTO_APPROVED,
                autoapprovalsummary__confirmed=True)).filter(
                    channel=channel,
                    files__isnull=False,
                    created__lt=version.created,
                    files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we shouldn't show a minimal form for.
    actions_full = [
        k for (k, a) in actions if not (is_static_theme or a.get('minimal'))
    ]

    # The actions we should show the comments form for (contrary to minimal
    # form above, it defaults to True, because most actions do need to have
    # the comments form).
    actions_comments = [k for (k, a) in actions if a.get('comments', True)]

    versions = (Version.unfiltered.filter(
        addon=addon, channel=channel).select_related(
            'autoapprovalsummary').order_by('-created').transform(
                Version.transformer_activity).transform(Version.transformer))

    # We assume comments on old deleted versions are for listed versions.
    # See _get_comments_for_hard_deleted_versions above for more detail.
    all_versions = (_get_comments_for_hard_deleted_versions(addon)
                    if channel == amo.RELEASE_CHANNEL_LISTED else [])
    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

    auto_approval_info = {}
    # Now that we've paginated the versions queryset, iterate on them to
    # generate auto approvals info. Note that the variable should not clash
    # the already existing 'version'.
    for a_version in pager.object_list:
        if not a_version.is_ready_for_auto_approval:
            continue
        try:
            summary = a_version.autoapprovalsummary
        except AutoApprovalSummary.DoesNotExist:
            auto_approval_info[a_version.pk] = None
            continue
        # Call calculate_verdict() again, it will use the data already stored.
        verdict_info = summary.calculate_verdict(pretty=True)
        auto_approval_info[a_version.pk] = verdict_info

    flags = get_flags(addon, version) if version else []

    if not is_static_theme:
        try:
            whiteboard = Whiteboard.objects.get(pk=addon.pk)
        except Whiteboard.DoesNotExist:
            whiteboard = Whiteboard(pk=addon.pk)

        whiteboard_form = WhiteboardForm(instance=whiteboard,
                                         prefix='whiteboard')
    else:
        whiteboard_form = None

    backgrounds = version.get_background_image_urls() if version else []

    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,
                  actions=actions,
                  actions_comments=actions_comments,
                  actions_full=actions_full,
                  addon=addon,
                  api_token=request.COOKIES.get(API_TOKEN_COOKIE, None),
                  approvals_info=approvals_info,
                  auto_approval_info=auto_approval_info,
                  backgrounds=backgrounds,
                  content_review_only=content_review_only,
                  count=count,
                  flags=flags,
                  form=form,
                  is_admin=is_admin,
                  num_pages=num_pages,
                  pager=pager,
                  reports=reports,
                  show_diff=show_diff,
                  subscribed=ReviewerSubscription.objects.filter(
                      user=request.user, addon=addon).exists(),
                  unlisted=(channel == amo.RELEASE_CHANNEL_UNLISTED),
                  user_changes=user_changes_log,
                  user_ratings=user_ratings,
                  version=version,
                  was_auto_approved=was_auto_approved,
                  whiteboard_form=whiteboard_form,
                  whiteboard_url=whiteboard_url)
    return render(request, 'reviewers/review.html', ctx)
def home(request):
    data = context(
        reviews_total=ActivityLog.objects.total_reviews(theme=True)[:5],
        reviews_monthly=ActivityLog.objects.monthly_reviews(theme=True)[:5],
        queue_counts=queue_counts_themes(request))
    return render(request, 'editors/themes/home.html', data)
Example #51
0
def blocked_detail(request, id):
    bltypes = dict((m._type, m) for m in (BlocklistItem, BlocklistPlugin))
    item = get_object_or_404(bltypes[id[0]], details=id[1:])
    return render(request, 'blocklist/blocked_detail.html', {'item': item})
Example #52
0
def index(request):
    log = ActivityLog.objects.admin_events()[:5]
    return render(request, 'zadmin/index.html', {'log': log})
Example #53
0
def site_stats_report(request, report):
    stats_base_url = reverse('stats.dashboard')
    view = get_report_view(request)
    return render(request, 'stats/reports/%s.html' % report,
                  {'report': report, 'view': view,
                   'stats_base_url': stats_base_url})
Example #54
0
def show_settings(request):
    settings_dict = debug.get_safe_settings()
    return render(request, 'zadmin/settings.html', {
        'settings_dict': settings_dict,
        'title': 'Settings!'
    })
Example #55
0
def handler404(request, exception=None, **kwargs):
    if getattr(request, 'is_api', False):
        # It's a v3+ api request
        return JsonResponse({'detail': str(NotFound.default_detail)},
                            status=404)
    return render(request, 'amo/404.html', status=404)
Example #56
0
def login(request, template=None):
    return render(request, template)
Example #57
0
def blocked_list(request, apiver=3):
    app = request.APP.guid
    objs = get_items(apiver, app)[1].values() + get_plugins(apiver, app)
    items = sorted(objs, key=attrgetter('created'), reverse=True)
    return render(request, 'blocklist/blocked_list.html', {'items': items})
Example #58
0
def search(request, tag_name=None):
    APP = request.APP
    types = (amo.ADDON_EXTENSION, amo.ADDON_THEME, amo.ADDON_DICT,
             amo.ADDON_SEARCH, amo.ADDON_LPAPP)

    category = request.GET.get('cat')

    if category == 'collections':
        extra_params = {'sort': {'newest': 'created'}}
    else:
        extra_params = None
    fixed = fix_search_query(request.GET, extra_params=extra_params)
    if fixed is not request.GET:
        return http.HttpResponsePermanentRedirect(urlparams(request.path,
                                                            **fixed))

    facets = request.GET.copy()

    # In order to differentiate between "all versions" and an undefined value,
    # we use "any" instead of "" in the frontend.
    if 'appver' in facets and facets['appver'] == 'any':
        facets['appver'] = ''

    form = ESSearchForm(facets or {})
    form.is_valid()  # Let the form try to clean data.

    form_data = form.cleaned_data
    if tag_name:
        form_data['tag'] = tag_name

    if category == 'collections':
        return _collections(request)
    elif category == 'themes' or form_data.get('atype') == amo.ADDON_PERSONA:
        return _personas(request)

    sort, extra_sort = split_choices(form.sort_choices, 'created')
    if form_data.get('atype') == amo.ADDON_SEARCH:
        # Search add-ons should not be searched by ADU, so replace 'Users'
        # sort with 'Weekly Downloads'.
        sort, extra_sort = list(sort), list(extra_sort)
        sort[1] = extra_sort[1]
        del extra_sort[1]

    # Perform search, using aggregation so that we can build the facets UI.
    # Note that we don't need to aggregate on platforms, that facet it built
    # from our constants directly, using the current application for this
    # request (request.APP).
    appversion_field = 'current_version.compatible_apps.%s.max' % APP.id
    qs = (Addon.search_public().filter(app=APP.id)
          .aggregate(tags={'terms': {'field': 'tags'}},
                     appversions={'terms': {'field': appversion_field}},
                     categories={'terms': {'field': 'category', 'size': 200}})
          )
    if waffle.switch_is_active('boost-webextensions-in-search'):
        qs = qs.score({
            # Boost webextensions if the waffle switch is enabled.
            'weight': WEBEXTENSIONS_WEIGHT,
            'filter': {'term': {'current_version.files.is_webextension': True}}
        })

    filters = ['atype', 'appver', 'cat', 'sort', 'tag', 'platform']
    mapping = {'users': '-average_daily_users',
               'rating': '-bayesian_rating',
               'created': '-created',
               'name': 'name_sort',
               'downloads': '-weekly_downloads',
               'updated': '-last_updated',
               'hotness': '-hotness'}
    qs = _filter_search(request, qs, form_data, filters, mapping, types=types)

    pager = amo.utils.paginate(request, qs)

    ctx = {
        'is_pjax': request.META.get('HTTP_X_PJAX'),
        'pager': pager,
        'query': form_data,
        'form': form,
        'sort_opts': sort,
        'extra_sort_opts': extra_sort,
        'sorting': sort_sidebar(request, form_data, form),
        'sort': form_data.get('sort'),
    }
    if not ctx['is_pjax']:
        aggregations = pager.object_list.aggregations
        ctx.update({
            'tag': tag_name,
            'categories': category_sidebar(request, form_data, aggregations),
            'platforms': platform_sidebar(request, form_data),
            'versions': version_sidebar(request, form_data, aggregations),
            'tags': tag_sidebar(request, form_data, aggregations),
        })
    return render(request, 'search/results.html', ctx)
Example #59
0
def motd(request):
    form = None
    form = MOTDForm(initial={'motd': get_config('reviewers_review_motd')})
    data = context(request, form=form)
    return render(request, 'reviewers/motd.html', data)
Example #60
0
def env(request):
    env = {}
    for k in request.META.keys():
        env[k] = debug.cleanse_setting(k, request.META[k])
    return render(request, 'zadmin/settings.html',
                  {'settings_dict': env, 'title': 'Env!'})