Exemple #1
0
class AppStats(CORSMixin, SlugOrIdMixin, ListAPIView):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    cors_allowed_methods = ['get']
    permission_classes = [AnyOf(PublicStats, AllowAppOwner,
                                GroupPermission('Stats', 'View'))]
    queryset = Webapp.objects.all()
    slug_field = 'app_slug'

    def get(self, request, pk, metric):
        if metric not in APP_STATS:
            raise http.Http404('No metric by that name.')

        app = self.get_object()

        stat = APP_STATS[metric]

        # Perform form validation.
        form = StatsForm(request.GET)
        if not form.is_valid():
            exc = ParseError()
            exc.detail = {'detail': dict(form.errors.items())}
            raise exc

        qs = form.cleaned_data

        dimensions = {'app-id': app.id}

        if 'dimensions' in stat:
            for key, default in stat['dimensions'].items():
                val = request.GET.get(key, default)
                if val is not None:
                    # Avoid passing kwargs to the monolith client when the
                    # dimension is None to avoid facet filters being applied.
                    dimensions[key] = request.GET.get(key, default)

        return Response(_get_monolith_data(stat, qs.get('start'),
                                           qs.get('end'), qs.get('interval'),
                                           dimensions))
Exemple #2
0
class GroupsViewSet(CORSMixin, ListModelMixin, DestroyModelMixin,
                    GenericViewSet):
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    cors_allowed_methods = ['get', 'post', 'delete']
    serializer_class = GroupsSerializer
    permission_classes = [GroupPermission('Admin', '%')]

    def paginate_queryset(self, queryset, page_size=None):
        return None

    def get_queryset(self):
        return self.get_user().groups.all()

    def get_user(self):
        try:
            return UserProfile.objects.get(pk=self.kwargs.get('pk'))
        except UserProfile.DoesNotExist:
            raise ParseError('User must exist.')

    def get_group(self):
        try:
            group = (self.request.data.get('group')
                     or self.request.query_params.get('group'))
            return Group.objects.get(pk=group)
        except Group.DoesNotExist:
            raise ParseError('Group does not exist.')

    def get_object(self):
        user = self.get_user()
        group = self.get_group()
        try:
            obj = GroupUser.objects.get(user=user, group=group)
        except GroupUser.DoesNotExist, e:
            raise ParseError('User isn\'t in that group? %s' % e)
        return obj
Exemple #3
0
class FailureNotificationView(MarketplaceView, GenericAPIView):
    authentication_classes = [RestOAuthAuthentication,
                              RestSharedSecretAuthentication]
    permission_classes = [GroupPermission('Transaction', 'NotifyFailure')]
    queryset = Contribution.objects.filter(uuid__isnull=False)

    def patch(self, request, *args, **kwargs):
        form = FailureForm(request.data)
        if not form.is_valid():
            return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)

        obj = self.get_object()
        data = {
            'transaction_id': obj,
            'transaction_url': absolutify(
                urlparams(reverse('mkt.developers.transactions'),
                          transaction_id=obj.uuid)),
            'url': form.cleaned_data['url'],
            'retries': form.cleaned_data['attempts']}
        owners = obj.addon.authors.values_list('email', flat=True)
        send_mail_jinja('Payment notification failure.',
                        'webpay/failure.txt',
                        data, recipient_list=owners)
        return Response(status=status.HTTP_202_ACCEPTED)
Exemple #4
0
class _WebsiteAction(object):
    permission_classes = [GroupPermission('Websites', 'Review')]
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    model = Website
    queryset = Website.objects.all()
Exemple #5
0
class AppDisable(_AppAction, CreateAPIView):
    permission_classes = [GroupPermission('Apps', 'Edit')]
    verb = "disable"
Exemple #6
0
class ReviewerExtensionSearchView(ExtensionSearchView):
    filter_backends = [
        ExtensionSearchFormFilter, SearchQueryFilter, SortingFilter
    ]
    permission_classes = [GroupPermission('ContentTools', 'AddonReview')]
Exemple #7
0
class RatingViewSet(CORSMixin, MarketplaceView, ModelViewSet):
    # Unfortunately, the model class name for ratings is "Review".
    # We prefetch 'version' because it's often going to be similar, and select
    # related 'user' to avoid extra queries.
    queryset = (Review.objects.valid().prefetch_related(
        'version').select_related('user'))
    cors_allowed_methods = ('get', 'post', 'put', 'delete')
    permission_classes = [
        ByHttpMethod({
            'options':
            AllowAny,  # Needed for CORS.
            'get':
            AllowAny,
            'head':
            AllowAny,
            'post':
            IsAuthenticated,
            'put':
            AnyOf(AllowOwner, GroupPermission('Apps', 'Edit')),
            'delete':
            AnyOf(AllowOwner, AllowRelatedAppOwner,
                  GroupPermission('Users', 'Edit'),
                  GroupPermission('Apps', 'Edit')),
        })
    ]
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    serializer_class = RatingSerializer

    def paginator_class(self, *args, **kwargs):
        paginator = super(RatingViewSet, self).paginator_class(*args, **kwargs)
        if hasattr(self, 'app'):
            # If an app is passed, we want the paginator count to match the
            # number of reviews on the app, without doing an extra query.
            paginator._count = self.app.total_reviews
        return paginator

    # FIXME: Add throttling ? Original tastypie version didn't have it...

    def filter_queryset(self, queryset):
        """
        Custom filter method allowing us to filter on app slug/pk and user pk
        (or the special user value "mine"). A full FilterSet is overkill here.
        """
        filters = Q()
        app = self.request.GET.get('app')
        user = self.request.GET.get('user')
        lang = self.request.GET.get('lang')
        match_lang = self.request.GET.get('match_lang')
        if app:
            self.app = self.get_app(app)
            filters &= Q(addon=self.app)
        if user:
            filters &= Q(user=self.get_user(user))
        elif lang and match_lang == '1':
            filters &= Q(lang=lang)

        if filters:
            queryset = queryset.filter(filters)
        return queryset

    def get_user(self, ident):
        pk = ident
        if pk == 'mine':
            user = mkt.get_user()
            if not user or not user.is_authenticated():
                # You must be logged in to use "mine".
                raise NotAuthenticated()
            pk = user.pk
        return pk

    def get_app(self, ident):
        try:
            app = Webapp.objects.by_identifier(ident)
        except Webapp.DoesNotExist:
            raise Http404

        if not app.is_public() and not check_addon_ownership(
                self.request, app):
            # App owners and admin can see the app even if it's not public.
            # Regular users or anonymous users can't.
            raise PermissionDenied('The app requested is not public')
        return app

    def list(self, request, *args, **kwargs):
        response = super(RatingViewSet, self).list(request, *args, **kwargs)
        app = getattr(self, 'app', None)
        if app:
            user, info = self.get_extra_data(app, request.user)
            response.data['user'] = user
            response.data['info'] = info
        return response

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        mkt.log(mkt.LOG.DELETE_REVIEW,
                obj.addon,
                obj,
                details=dict(title=unicode(obj.title),
                             body=unicode(obj.body),
                             addon_id=obj.addon.id,
                             addon_title=unicode(obj.addon.name)))
        log.debug('[Review:%s] Deleted by %s' % (obj.pk, self.request.user.id))
        return super(RatingViewSet, self).destroy(request, *args, **kwargs)

    def post_save(self, obj, created=False):
        app = obj.addon
        if created:
            mkt.log(mkt.LOG.ADD_REVIEW, app, obj)
            log.debug('[Review:%s] Created by user %s ' %
                      (obj.pk, self.request.user.id))
            record_action('new-review', self.request, {'app-id': app.id})
        else:
            mkt.log(mkt.LOG.EDIT_REVIEW, app, obj)
            log.debug('[Review:%s] Edited by %s' %
                      (obj.pk, self.request.user.id))

    def partial_update(self, *args, **kwargs):
        # We don't need/want PATCH for now.
        raise MethodNotAllowed('PATCH is not supported for this endpoint.')

    def get_extra_data(self, app, user):
        extra_user = None

        if user.is_authenticated():
            if app.is_premium():
                # If the app is premium, you need to purchase it to rate it.
                can_rate = app.has_purchased(user)
            else:
                # If the app is free, you can not be one of the authors.
                can_rate = not app.has_author(user)

            filters = {'addon': app, 'user': user}
            if app.is_packaged:
                filters['version'] = app.current_version

            extra_user = {
                'can_rate': can_rate,
                'has_rated': Review.objects.valid().filter(**filters).exists()
            }

        extra_info = {
            'average': app.average_rating,
            'slug': app.app_slug,
            'total_reviews': app.total_reviews,
            'current_version': getattr(app.current_version, 'version', None)
        }

        return extra_user, extra_info

    @action(methods=['POST'], permission_classes=[AllowAny])
    def flag(self, request, pk=None):
        self.kwargs[self.lookup_field] = pk
        self.get_object()  # Will check that the Review instance is valid.
        request._request.CORS = RatingFlagViewSet.cors_allowed_methods
        view = RatingFlagViewSet.as_view({'post': 'create'})
        return view(request, *self.args, **{'review': pk})
Exemple #8
0
class PriceTierViewSet(generics.CreateAPIView,
                       generics.RetrieveUpdateDestroyAPIView, ModelViewSet):
    permission_classes = [GroupPermission('Prices', 'Edit')]
    authentication_classes = [RestOAuthAuthentication]
    serializer_class = PriceTierSerializer
    model = Price