Example #1
0
    def obj_create(self, bundle, request=None, **kwargs):
        """
        Handle POST requests to the resource. If the data validates, create a
        new Review from bundle data.
        """
        form = ReviewForm(bundle.data)

        if not form.is_valid():
            raise self.form_errors(form)

        app = self.get_app(bundle.data['app'])

        # Return 409 if the user has already reviewed this app.
        if self._meta.queryset.filter(addon=app, user=request.user).exists():
            raise ImmediateHttpResponse(response=http.HttpConflict())

        # Return 403 if the user is attempting to review their own app:
        if app.has_author(request.user):
            raise ImmediateHttpResponse(response=http.HttpForbidden())

        # Return 403 if not a free app and the user hasn't purchased it.
        if app.is_premium() and not app.is_purchased(request.amo_user):
            raise ImmediateHttpResponse(response=http.HttpForbidden())

        bundle.obj = Review.objects.create(**self._review_data(request, app,
                                                               form))

        amo.log(amo.LOG.ADD_REVIEW, app, bundle.obj)
        log.debug('[Review:%s] Created by user %s ' %
                  (bundle.obj.id, request.user.id))
        record_action('new-review', request, {'app-id': app.id})

        return bundle
Example #2
0
def add(request, addon, template=None):
    if addon.has_author(request.user):
        return http.HttpResponseForbidden()
    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)
        amo.log(amo.LOG.ADD_REVIEW, addon, review)
        log.debug('New review: %s' % review.id)

        reply_url = shared_url('reviews.reply', addon, review.id,
                               add_prefix=False)
        data = {'name': addon.name,
                'rating': '%s out of 5 stars' % details['rating'],
                'review': details['body'],
                'reply_url': 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')

        # Update the ratings and counts for the add-on.
        addon_review_aggregates.delay(addon.id, using='default')

        return redirect(shared_url('reviews.list', addon))
    return jingo.render(request, template, dict(addon=addon, form=form))
Example #3
0
    def save(self, log_for_developer=True):
        u = super(UserEditForm, self).save(commit=False)
        data = self.cleaned_data
        photo = data['photo']
        if photo:
            u.picture_type = 'image/png'
            tmp_destination = u.picture_path + '__unconverted'

            with storage.open(tmp_destination, 'wb') as fh:
                for chunk in photo.chunks():
                    fh.write(chunk)

            tasks.resize_photo.delay(tmp_destination, u.picture_path,
                                     set_modified_on=[u])

        if data['password']:
            u.set_password(data['password'])
            log_cef('Password Changed', 5, self.request, username=u.username,
                    signature='PASSWORDCHANGED', msg='User changed password')
            if log_for_developer:
                amo.log(amo.LOG.CHANGE_PASSWORD)
                log.info(u'User (%s) changed their password' % u)

        for (i, n) in email.NOTIFICATIONS_BY_ID.items():
            enabled = n.mandatory or (str(i) in data['notifications'])
            UserNotification.update_or_create(user=u, notification_id=i,
                    update={'enabled': enabled})

        log.debug(u'User (%s) updated their profile' % u)

        u.save()
        return u
Example #4
0
    def test_rejected(self):
        comments = "oh no you di'nt!!"
        amo.set_user(UserProfile.objects.get(username="******"))
        amo.log(
            amo.LOG.REJECT_VERSION,
            self.webapp,
            self.webapp.current_version,
            user_id=999,
            details={"comments": comments, "reviewtype": "pending"},
        )
        self.webapp.update(status=amo.STATUS_REJECTED)
        (self.webapp.versions.latest().all_files[0].update(status=amo.STATUS_DISABLED))

        r = self.client.get(self.url)
        eq_(r.status_code, 200)
        doc = pq(r.content)("#version-status")
        eq_(doc(".status-rejected").length, 1)
        eq_(doc("#rejection").length, 1)
        eq_(doc("#rejection blockquote").text(), comments)

        my_reply = "fixed just for u, brah"
        r = self.client.post(self.url, {"notes": my_reply, "resubmit-app": ""})
        self.assertRedirects(r, self.url, 302)

        webapp = self.get_webapp()
        eq_(webapp.status, amo.STATUS_PENDING, "Reapplied apps should get marked as pending")
        eq_(
            webapp.versions.latest().all_files[0].status,
            amo.STATUS_PENDING,
            "Files for reapplied apps should get marked as pending",
        )
        action = amo.LOG.WEBAPP_RESUBMIT
        assert AppLog.objects.filter(addon=webapp, activity_log__action=action.id).exists(), (
            "Didn't find `%s` action in logs." % action.short
        )
Example #5
0
def verify(request, uuid):
    # Because this will be called at any point in the future,
    # use guid in the URL.
    addon = get_object_or_404(Addon, guid=uuid)
    receipt = request.read()
    verify = Verify(receipt, request)
    output = verify(check_purchase=False)

    # Ensure CORS headers are set.
    def response(data):
        response = http.HttpResponse(data)
        for header, value in get_headers(len(output)):
            response[header] = value
        return response

    # Only reviewers or the developers can use this which is different
    # from the standard receipt verification. The user is contained in the
    # receipt.
    if verify.user_id:
        try:
            user = UserProfile.objects.get(pk=verify.user_id)
        except UserProfile.DoesNotExist:
            user = None

        if user and (acl.action_allowed_user(user, 'Apps', 'Review')
            or addon.has_author(user)):
            amo.log(amo.LOG.RECEIPT_CHECKED, addon, user=user)
            return response(output)

    return response(verify.invalid())
Example #6
0
 def flag(cls, addon, event, message=None):
     cls.objects.get_or_create(addon=addon)
     if message:
         amo.log(event, addon, addon.current_version,
                 details={'comments': message})
     else:
         amo.log(event, addon, addon.current_version)
Example #7
0
def payments(request, addon_id, addon):
    charity = None if addon.charity_id == amo.FOUNDATION_ORG else addon.charity
    charity_form = forms.CharityForm(request.POST or None, instance=charity,
                                     prefix='charity')
    contrib_form = forms.ContribForm(request.POST or None, instance=addon,
                                     initial=forms.ContribForm.initial(addon))
    profile_form = forms.ProfileForm(request.POST or None, instance=addon,
                                     required=True)
    if request.method == 'POST':
        if contrib_form.is_valid():
            addon = contrib_form.save(commit=False)
            addon.wants_contributions = True
            valid = _save_charity(addon, contrib_form, charity_form)
            if not addon.has_full_profile():
                valid &= profile_form.is_valid()
                if valid:
                    profile_form.save()
            if valid:
                addon.save()
                messages.success(request, _('Changes successfully saved.'))
                amo.log(amo.LOG.EDIT_CONTRIBUTIONS, addon)

                return redirect('devhub.addons.payments', addon.slug)
    errors = charity_form.errors or contrib_form.errors or profile_form.errors
    if errors:
        messages.error(request, _('There were errors in your submission.'))
    return jingo.render(request, 'devhub/addons/payments.html',
        dict(addon=addon, charity_form=charity_form, errors=errors,
             contrib_form=contrib_form, profile_form=profile_form))
Example #8
0
 def save(self):
     v = self.product.versions.latest()
     v.update(approvalnotes=self.cleaned_data['notes'])
     amo.log(amo.LOG.EDIT_VERSION, v.addon, v)
     # Mark app as pending again.
     self.product.mark_done()
     return v
Example #9
0
def find_abuse_escalations(addon_id, **kw):
    amo.set_user(get_task_user())
    weekago = datetime.date.today() - datetime.timedelta(days=7)

    for abuse in AbuseReport.recent_high_abuse_reports(1, weekago, addon_id):
        if EscalationQueue.objects.filter(addon=abuse.addon).exists():
            # App is already in the queue, no need to re-add it.
            # TODO: If not in queue b/c of abuse reports, add an
            #       amo.LOG.ESCALATED_HIGH_ABUSE for reviewers.
            log.info(u'[addon:%s] High abuse reports, but already escalated' %
                     (abuse.addon,))
            continue

        # We have an abuse report and this add-on isn't currently in the
        # escalated queue... let's see if it has been detected and dealt with
        # already.
        logs = (AppLog.objects.filter(
            activity_log__action=amo.LOG.ESCALATED_HIGH_ABUSE.id,
            addon=abuse.addon).order_by('-created'))
        if logs:
            abuse_since_log = AbuseReport.recent_high_abuse_reports(
                1, logs[0].created, addon_id)
            # If no abuse reports have happened since the last logged abuse
            # report, do not add to queue.
            if not abuse_since_log:
                log.info(u'[addon:%s] High abuse reports, but none since last '
                          'escalation' % abuse.addon)
                continue

        # If we haven't bailed out yet, escalate this app.
        msg = u'High number of abuse reports detected'
        EscalationQueue.objects.create(addon=abuse.addon)
        amo.log(amo.LOG.ESCALATED_HIGH_ABUSE, abuse.addon,
                abuse.addon.current_version, details={'comments': msg})
        log.info(u'[addon:%s] %s' % (abuse.addon, msg))
Example #10
0
    def save(self, log_for_developer=True):
        u = super(UserEditForm, self).save(commit=False)
        data = self.cleaned_data
        photo = data['photo']
        if photo:
            u.picture_type = 'image/png'
            tmp_destination = u.picture_path + '__unconverted'

            if not os.path.exists(u.picture_dir):
                os.makedirs(u.picture_dir)

            fh = open(tmp_destination, 'w')
            for chunk in photo.chunks():
                fh.write(chunk)

            fh.close()
            tasks.resize_photo.delay(tmp_destination, u.picture_path,
                                     set_modified_on=[u])

        if data['password']:
            u.set_password(data['password'])
            if log_for_developer:
                amo.log(amo.LOG.CHANGE_PASSWORD)
                log.info(u'User (%s) changed their password' % u)

        for (i, n) in email.NOTIFICATIONS_BY_ID.items():
            enabled = n.mandatory or (str(i) in data['notifications'])
            UserNotification.update_or_create(user=u, notification_id=i,
                    update={'enabled': enabled})

        log.debug(u'User (%s) updated their profile' % u)

        u.save()
        return u
Example #11
0
    def create_note(self, action):
        """
        Permissions default to developers + reviewers + Mozilla contacts.
        For escalation/comment, exclude the developer from the conversation.
        """
        details = {'comments': self.data['comments'],
                   'reviewtype': self.review_type}
        if self.files:
            details['files'] = [f.id for f in self.files]

        # Commbadge (the future).
        perm_overrides = {
            comm.ESCALATION: {'developer': False},
            comm.REVIEWER_COMMENT: {'developer': False},
        }
        note_type = comm.ACTION_MAP(action.id)
        self.comm_thread, self.comm_note = create_comm_note(
            self.addon, self.version, self.request.amo_user,
            self.data['comments'], note_type=note_type,
            # Ignore switch so we don't have to re-migrate new notes.
            perms=perm_overrides.get(note_type), no_switch=True,
            attachments=self.attachment_formset)

        # ActivityLog (ye olde).
        amo.log(action, self.addon, self.version, user=self.user.get_profile(),
                created=datetime.now(), details=details,
                attachments=self.attachment_formset)
Example #12
0
 def test_json_failboat(self):
     a = Addon.objects.get()
     amo.log(amo.LOG['CREATE_ADDON'], a)
     entry = ActivityLog.objects.get()
     entry._arguments = 'failboat?'
     entry.save()
     eq_(entry.arguments, None)
Example #13
0
    def create_note(self, action):
        """
        Permissions default to developers + reviewers + Mozilla contacts.
        For escalation/comment, exclude the developer from the conversation.
        """
        details = {'comments': self.data['comments'],
                   'reviewtype': self.review_type}
        if self.files:
            details['files'] = [f.id for f in self.files]

        tested = self.get_tested()  # You really should...
        if tested:
            self.data['comments'] += '\n\n%s' % tested

        # Commbadge (the future).
        note_type = comm.ACTION_MAP(action.id)
        self.comm_thread, self.comm_note = create_comm_note(
            self.addon, self.version, self.request.user,
            self.data['comments'], note_type=note_type,
            attachments=self.attachment_formset)

        # ActivityLog (ye olde).
        amo.log(action, self.addon, self.version, user=self.user,
                created=datetime.now(), details=details,
                attachments=self.attachment_formset)
Example #14
0
 def test_hidden(self):
     version = Version.objects.create(addon=self.addon)
     amo.log(amo.LOG.COMMENT_VERSION, self.addon, version)
     res = self.get_response(addon=self.addon.id)
     key = RssKey.objects.get()
     res = self.get_response(privaterss=key.key)
     assert "<title>Comment on" not in res.content
Example #15
0
    def manifest_updated(self, manifest, upload):
        """The manifest has updated, update the version and file.

        This is intended to be used for hosted apps only, which have only a
        single version and a single file.
        """
        data = parse_addon(upload, self)
        version = self.versions.latest()
        version.update(version=data['version'])
        path = smart_path(nfd_str(upload.path))
        file = version.files.latest()
        file.filename = file.generate_filename(extension='.webapp')
        file.size = int(max(1, round(storage.size(path) / 1024, 0)))
        file.hash = (file.generate_hash(path) if
                     waffle.switch_is_active('file-hash-paranoia') else
                     upload.hash)
        log.info('Updated file hash to %s' % file.hash)
        file.save()

        # Move the uploaded file from the temp location.
        copy_stored_file(path, os.path.join(version.path_prefix,
                                            nfd_str(file.filename)))
        log.info('[Webapp:%s] Copied updated manifest to %s' % (
            self, version.path_prefix))

        amo.log(amo.LOG.MANIFEST_UPDATED, self)
Example #16
0
def delete_photo(request):
    request.amo_user.update(picture_type='')
    delete_photo_task.delay(request.amo_user.picture_path)
    log.debug(u'User (%s) deleted photo' % request.amo_user)
    messages.success(request, _('Photo Deleted'))
    amo.log(amo.LOG.USER_EDITED)
    return http.HttpResponse()
Example #17
0
 def test_empty_comment(self):
     amo.log(amo.LOG.REQUEST_VERSION, self.app, self.version,
             user=self.user, details={})
     call_command('migrate_activity_log')
     note = CommunicationNote.objects.get()
     eq_(note.thread.addon, self.app)
     eq_(note.body, '')
Example #18
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 #19
0
    def save(self, *args, **kw):
        """Save all form data.

        This will only create a new license if it's not one of the builtin
        ones.

        Keyword arguments

        **log=True**
            Set to False if you do not want to log this action for display
            on the developer dashboard.
        """
        log = kw.pop('log', True)
        changed = self.changed_data

        builtin = self.cleaned_data['builtin']
        if builtin != License.OTHER:
            license = License.objects.get(builtin=builtin)
        else:
            # Save the custom license:
            license = super(LicenseForm, self).save(*args, **kw)

        if self.version:
            if changed or license != self.version.license:
                self.version.update(license=license)
                if log:
                    amo.log(amo.LOG.CHANGE_LICENSE, license,
                            self.version.addon)
        return license
Example #20
0
def account_settings(request):
    ctx = {}

    # Don't use `request.amo_user` because it's too cached.
    user = request.user
    if user.is_authenticated():
        amo_user = user.get_profile()
        form = forms.UserEditForm(request.POST or None, instance=amo_user)
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                messages.success(request, _('Settings Updated.'))
                amo.log(amo.LOG.USER_EDITED)
                response = redirect('account.settings')
                # TODO: Detect when we're changing the user's locale and region
                # and bust on '/', bust on '/settings' for everything else.
                bust_fragments(response, '/')
                return response
            else:
                messages.form_errors(request)
        ctx = {'form': form, 'amouser': amo_user}
    else:
        if request.method == 'POST':
            messages.success(request, _('Settings Updated.'))

    return jingo.render(request, 'account/settings.html', ctx)
Example #21
0
    def save(self, commit=False):
        from .tasks import create_persona_preview_image, save_persona_image
        # We ignore `commit`, since we need it to be `False` so we can save
        # the ManyToMany fields on our own.
        addon = super(NewPersonaForm, self).save(commit=False)
        addon.status = amo.STATUS_UNREVIEWED
        addon.type = amo.ADDON_PERSONA
        addon.save()
        addon._current_version = Version.objects.create(addon=addon,
                                                        version='0')
        addon.save()
        amo.log(amo.LOG.CREATE_ADDON, addon)
        log.debug('New persona %r uploaded' % addon)

        data = self.cleaned_data

        header = data['header_hash']
        footer = data['footer_hash']

        header = os.path.join(settings.TMP_PATH, 'persona_header', header)
        footer = os.path.join(settings.TMP_PATH, 'persona_footer', footer)
        dst = os.path.join(settings.PERSONAS_PATH, str(addon.id))

        # Save header, footer, and preview images.
        save_persona_image(src=header, dst=dst, img_basename='header.jpg')
        save_persona_image(src=footer, dst=dst, img_basename='footer.jpg')
        create_persona_preview_image(src=header, dst=dst,
                                     img_basename='preview.jpg',
                                     set_modified_on=[addon])

        # Save user info.
        user = self.request.amo_user
        AddonUser(addon=addon, user=user).save()

        p = Persona()
        p.persona_id = 0
        p.addon = addon
        p.header = 'header.jpg'
        p.footer = 'footer.jpg'
        if data['accentcolor']:
            p.accentcolor = data['accentcolor'].lstrip('#')
        if data['textcolor']:
            p.textcolor = data['textcolor'].lstrip('#')
        p.license_id = data['license']
        p.submit = datetime.now()
        p.author = user.name
        p.display_username = user.username
        p.save()

        # Save tags.
        for t in data['tags']:
            Tag(tag_text=t).save_tag(addon)

        # Save categories.
        tb_c = Category.objects.get(application=amo.THUNDERBIRD.id,
                                    name__id=data['category'].name_id)
        AddonCategory(addon=addon, category=data['category']).save()
        AddonCategory(addon=addon, category=tb_c).save()

        return addon
Example #22
0
def notify_failed(file_pks, job_pk, data, **kw):
    log.info("[%s@None] Notifying failed for job %s." % (len(file_pks), job_pk))
    job = ValidationJob.objects.get(pk=job_pk)
    set_user(get_task_user())
    for result in ValidationResult.objects.filter(validation_job=job, file__pk__in=file_pks):
        file = result.file
        version = file.version
        addon = version.addon
        context = get_context(addon, version, job, [result], fileob=file)
        for author in addon.authors.all():
            log.info(
                u"Emailing %s%s for addon %s, file %s about "
                "error from bulk validation job %s"
                % (author.email, " [PREVIEW]" if data["preview_only"] else "", addon.pk, file.pk, job_pk)
            )
            args = (Template(data["subject"]).render(context), Template(data["text"]).render(context))
            kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[author.email])
            if data["preview_only"]:
                job.preview_failure_mail(*args, **kwargs)
            else:
                send_mail(*args, **kwargs)

        amo.log(
            amo.LOG.BULK_VALIDATION_EMAILED,
            addon,
            version,
            details={"version": version.version, "file": file.filename, "target": job.target_version.version},
        )
Example #23
0
def find_abuse_escalations(addon_id, **kw):
    weekago = datetime.date.today() - datetime.timedelta(days=7)
    add_to_queue = True

    for abuse in AbuseReport.recent_high_abuse_reports(1, weekago, addon_id):
        if EscalationQueue.objects.filter(addon=abuse.addon).exists():
            # App is already in the queue, no need to re-add it.
            task_log.info(u'[app:%s] High abuse reports, but already '
                          u'escalated' % abuse.addon)
            add_to_queue = False

        # We have an abuse report... has it been detected and dealt with?
        logs = (AppLog.objects.filter(
            activity_log__action=amo.LOG.ESCALATED_HIGH_ABUSE.id,
            addon=abuse.addon).order_by('-created'))
        if logs:
            abuse_since_log = AbuseReport.recent_high_abuse_reports(
                1, logs[0].created, addon_id)
            # If no abuse reports have happened since the last logged abuse
            # report, do not add to queue.
            if not abuse_since_log:
                task_log.info(u'[app:%s] High abuse reports, but none since '
                              u'last escalation' % abuse.addon)
                continue

        # If we haven't bailed out yet, escalate this app.
        msg = u'High number of abuse reports detected'
        if add_to_queue:
            EscalationQueue.objects.create(addon=abuse.addon)
        amo.log(amo.LOG.ESCALATED_HIGH_ABUSE, abuse.addon,
                abuse.addon.current_version, details={'comments': msg})
        task_log.info(u'[app:%s] %s' % (abuse.addon, msg))
Example #24
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = ReviewReplyForm(request.POST or None)
    if form.is_valid():
        d = dict(reply_to=review, addon=addon,
                 defaults=dict(user=request.amo_user))
        reply, new = Review.objects.get_or_create(**d)
        for k, v in _review_details(request, addon, form).items():
            setattr(reply, k, v)
        reply.save()
        action = 'New' if new else 'Edited'
        if new:
            amo.log(amo.LOG.ADD_REVIEW, addon, reply)
        else:
            amo.log(amo.LOG.EDIT_REVIEW, addon, reply)

        log.debug('%s reply to %s: %s' % (action, review_id, reply.id))
        messages.success(request,
                         _('Your reply was successfully added.') if new else
                         _('Your reply was successfully updated.'))

    return http.HttpResponse()
Example #25
0
    def test_rejected(self):
        comments = "oh no you di'nt!!"
        amo.set_user(UserProfile.objects.get(username='******'))
        amo.log(amo.LOG.REJECT_VERSION, self.webapp,
                self.webapp.current_version, user_id=999,
                details={'comments': comments, 'reviewtype': 'pending'})
        self.webapp.update(status=amo.STATUS_REJECTED)
        amo.tests.make_rated(self.webapp)
        (self.webapp.versions.latest()
                             .all_files[0].update(status=amo.STATUS_DISABLED))

        r = self.client.get(self.url)
        eq_(r.status_code, 200)
        doc = pq(r.content)('#version-status')
        eq_(doc('.status-rejected').length, 1)
        eq_(doc('#rejection').length, 1)
        eq_(doc('#rejection blockquote').text(), comments)

        my_reply = 'fixed just for u, brah'
        r = self.client.post(self.url, {'notes': my_reply,
                                        'resubmit-app': ''})
        self.assertRedirects(r, self.url, 302)

        webapp = self.get_webapp()
        eq_(webapp.status, amo.STATUS_PENDING,
            'Reapplied apps should get marked as pending')
        eq_(webapp.versions.latest().all_files[0].status, amo.STATUS_PENDING,
            'Files for reapplied apps should get marked as pending')
        action = amo.LOG.WEBAPP_RESUBMIT
        assert AppLog.objects.filter(
            addon=webapp, activity_log__action=action.id).exists(), (
                "Didn't find `%s` action in logs." % action.short)
Example #26
0
    def test_user_review_history(self):
        addon_factory(type=amo.ADDON_PERSONA, status=self.status)

        reviewer = self.create_and_become_reviewer()

        res = self.client.get(reverse('reviewers.themes.history'))
        eq_(res.status_code, 200)
        doc = pq(res.content)
        eq_(doc('tbody tr').length, 0)

        theme = Persona.objects.all()[0]
        for x in range(3):
            amo.log(amo.LOG.THEME_REVIEW, theme.addon, user=reviewer,
                    details={'action': rvw.ACTION_APPROVE,
                             'comment': '', 'reject_reason': ''})

        res = self.client.get(reverse('reviewers.themes.history'))
        eq_(res.status_code, 200)
        doc = pq(res.content)
        eq_(doc('tbody tr').length, 3)

        res = self.client.get(reverse('reviewers.themes.logs'))
        eq_(res.status_code, 200)
        doc = pq(res.content)
        eq_(doc('tbody tr').length, 3 * 2)  # Double for comment rows.
Example #27
0
def user_activity(request, user_id):
    """Shows the user activity page for another user."""
    user = get_object_or_404(UserProfile, pk=user_id)
    products, contributions, listing = purchase_list(request, user, None)
    is_admin = acl.action_allowed(request, "Users", "Edit")

    collections = Collection.objects.filter(author=user_id)
    user_items = ActivityLog.objects.for_user(user).exclude(action__in=amo.LOG_HIDE_DEVELOPER)
    admin_items = ActivityLog.objects.for_user(user).filter(action__in=amo.LOG_HIDE_DEVELOPER)
    amo.log(amo.LOG.ADMIN_VIEWED_LOG, request.amo_user, user=user)
    return jingo.render(
        request,
        "lookup/user_activity.html",
        {
            "pager": products,
            "account": user,
            "is_admin": is_admin,
            "listing_filter": listing,
            "collections": collections,
            "contributions": contributions,
            "single": bool(None),
            "user_items": user_items,
            "admin_items": admin_items,
            "show_link": False,
        },
    )
Example #28
0
    def save(self):
        for form in self.forms:
            if form.cleaned_data:
                action = int(form.cleaned_data['action'])

                is_flagged = (form.instance.reviewflag_set.count() > 0)

                if action != reviews.REVIEW_MODERATE_SKIP:  # Delete flags.
                    for flag in form.instance.reviewflag_set.all():
                        flag.delete()

                review = form.instance
                addon = review.addon
                if action == reviews.REVIEW_MODERATE_DELETE:
                    review_addon = review.addon
                    review_id = review.id
                    review.delete()
                    amo.log(amo.LOG.DELETE_REVIEW, review_addon, review_id,
                            details=dict(title=unicode(review.title),
                                         body=unicode(review.body),
                                         addon_id=addon.id,
                                         addon_title=unicode(addon.name),
                                         is_flagged=is_flagged))
                elif action == reviews.REVIEW_MODERATE_KEEP:
                    review.editorreview = False
                    review.save()
                    amo.log(amo.LOG.APPROVE_REVIEW, review.addon, review,
                            details=dict(title=unicode(review.title),
                                         body=unicode(review.body),
                                         addon_id=addon.id,
                                         addon_title=unicode(addon.name),
                                         is_flagged=is_flagged))
Example #29
0
 def test_dedupe_date(self):
     # Test that a log spanning
     old = amo.log(amo.LOG.APPROVE_VERSION, self.addon, self.version)
     old.update(created=datetime.today() - timedelta(days=1))
     amo.log(amo.LOG.APPROVE_VERSION, self.addon, self.version)
     dedupe_approvals([self.addon.pk])
     eq_(ActivityLog.objects.for_addons(self.addon).count(), 2)
Example #30
0
 def test_addon_logging_pseudo(self):
     """
     If we are given (Addon, 3615) it should log in the AddonLog as well.
     """
     a = Addon.objects.get()
     amo.log(amo.LOG.CREATE_ADDON, (Addon, a.id))
     eq_(AddonLog.objects.count(), 1)
Example #31
0
def _record(request, addon):
    # TODO(andym): we have an API now, replace this with that.
    logged = request.user.is_authenticated()
    premium = addon.is_premium()

    # Require login for premium.
    if not logged and premium:
        return http.HttpResponseRedirect(reverse('users.login'))

    ctx = {'addon': addon.pk}

    # Don't generate receipts if we're allowing logged-out install.
    if logged:
        is_dev = request.check_ownership(addon, require_owner=False,
                                         ignore_disabled=True, admin=False)
        is_reviewer = acl.check_reviewer(request)
        if (not addon.is_webapp() or not addon.is_public() and
            not (is_reviewer or is_dev)):
            raise http.Http404

        if (premium and
            not addon.has_purchased(request.amo_user) and
            not is_reviewer and not is_dev):
            raise PermissionDenied

        # If you are reviewer, you get a user receipt. Use the reviewer tools
        # to get a reviewer receipt. App developers still get their special
        # receipt.
        install_type = (apps.INSTALL_TYPE_DEVELOPER if is_dev
                        else apps.INSTALL_TYPE_USER)
        # Log the install.
        installed, c = Installed.objects.get_or_create(addon=addon,
            user=request.amo_user, install_type=install_type)

        # Get download source from GET if it exists, if so get the download
        # source object if it exists. Then grab a client data object to hook up
        # with the Installed object.
        download_source = DownloadSource.objects.filter(
            name=request.REQUEST.get('src', None))
        download_source = download_source[0] if download_source else None
        try:
            region = request.REGION.id
        except AttributeError:
            region = mkt.regions.WORLDWIDE.id
        client_data, c = ClientData.objects.get_or_create(
            download_source=download_source,
            device_type=request.POST.get('device_type', ''),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            is_chromeless=request.POST.get('chromeless', False),
            language=request.LANG,
            region=region)
        installed.update(client_data=client_data)

        error = ''
        receipt_cef.log(request, addon, 'sign', 'Receipt requested')
        try:
            receipt = create_receipt(installed)
        except SigningError:
            error = _('There was a problem installing the app.')

        ctx.update(receipt=receipt, error=error)
    else:
        if not addon.is_public() or not addon.is_webapp():
            raise http.Http404

    amo.log(amo.LOG.INSTALL_ADDON, addon)
    record_action('install', request, {
        'app-domain': addon.domain_from_url(addon.origin, allow_none=True),
        'app-id': addon.pk,
        'anonymous': request.user.is_anonymous(),
    })

    return ctx
Example #32
0
 def test_there(self):
     self.client.login(username=self.reviewer.email, password='******')
     amo.log(amo.LOG.RECEIPT_CHECKED, self.app, user=self.reviewer)
     res = self.client.get(self.url)
     eq_(res.status_code, 200)
     eq_(json.loads(res.content)['status'], True)
Example #33
0
def _review(request, addon, version):

    if (not settings.ALLOW_SELF_REVIEWS
            and not acl.action_allowed(request, 'Admin', '%')
            and addon.has_author(request.amo_user)):
        messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.home'))

    if (addon.status == amo.STATUS_BLOCKED
            and not acl.action_allowed(request, 'Apps', 'ReviewEscalated')):
        messages.warning(
            request, _('Only senior reviewers can review blocklisted apps.'))
        return redirect(reverse('reviewers.home'))

    attachment_formset = CommAttachmentFormSet(data=request.POST or None,
                                               files=request.FILES or None,
                                               prefix='attachment')
    form = forms.get_review_form(data=request.POST or None,
                                 files=request.FILES or None,
                                 request=request,
                                 addon=addon,
                                 version=version,
                                 attachment_formset=attachment_formset)
    postdata = request.POST if request.method == 'POST' else None
    all_forms = [form, attachment_formset]

    if version:
        features_list = [unicode(f) for f in version.features.to_list()]
        appfeatures_form = AppFeaturesForm(data=postdata,
                                           instance=version.features)
        all_forms.append(appfeatures_form)
    else:
        appfeatures_form = None
        features_list = None

    queue_type = form.helper.review_type
    redirect_url = reverse('reviewers.apps.queue_%s' % queue_type)
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    if request.method == 'POST' and all(f.is_valid() for f in all_forms):

        old_types = set(o.id for o in addon.device_types)
        new_types = set(form.cleaned_data.get('device_override'))

        old_features = set(features_list)
        new_features = set(
            unicode(f) for f in appfeatures_form.instance.to_list())

        if form.cleaned_data.get('action') == 'public':
            if old_types != new_types:
                # The reviewer overrode the device types. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                # And update the device types to what the reviewer set.
                AddonDeviceType.objects.filter(addon=addon).delete()
                for device in form.cleaned_data.get('device_override'):
                    addon.addondevicetype_set.create(device_type=device)

                # Log that the reviewer changed the device types.
                added_devices = new_types - old_types
                removed_devices = old_types - new_types
                msg = _(
                    u'Device(s) changed by '
                    'reviewer: {0}'
                ).format(', '.join([
                    _(u'Added {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                    for d in added_devices
                ] + [
                    _(u'Removed {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                    for d in removed_devices
                ]))
                amo.log(amo.LOG.REVIEW_DEVICE_OVERRIDE,
                        addon,
                        addon.current_version,
                        details={'comments': msg})

            if old_features != new_features:
                # The reviewer overrode the requirements. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                appfeatures_form.save(mark_for_rereview=False)

                # Log that the reviewer changed the minimum requirements.
                added_features = new_features - old_features
                removed_features = old_features - new_features

                fmt = ', '.join(
                    [_(u'Added {0}').format(f) for f in added_features] +
                    [_(u'Removed {0}').format(f) for f in removed_features])
                # L10n: {0} is the list of requirements changes.
                msg = _(u'Requirements changed by reviewer: {0}').format(fmt)
                amo.log(amo.LOG.REVIEW_FEATURES_OVERRIDE,
                        addon,
                        addon.current_version,
                        details={'comments': msg})

        score = form.helper.process()

        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.amo_user,
                                                     addon=addon)

        # Success message.
        if score:
            score = ReviewerScore.objects.filter(user=request.amo_user)[0]
            rev_str = amo.REVIEWED_CHOICES[score.note_key]
            try:
                rev_str = str(unicode(rev_str))
            except UnicodeEncodeError:
                pass
            # L10N: {0} is the type of review. {1} is the points they earned.
            #       {2} is the points they now have total.
            success = _(
                '"{0}" successfully processed (+{1} points, {2} total).'.
                format(rev_str, score.score,
                       ReviewerScore.get_total(request.amo_user)))
        else:
            success = _('Review successfully processed.')
        messages.success(request, success)

        return redirect(redirect_url)

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

    try:
        if not version:
            raise Version.DoesNotExist
        show_diff = (addon.versions.exclude(id=version.id).filter(
            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 from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # We only allow the user to check/uncheck files for "pending"
    allow_unchecking_files = form.helper.review_type == "pending"

    versions = (Version.with_deleted.filter(
        addon=addon).order_by('-created').transform(
            Version.transformer_activity).transform(Version.transformer))

    product_attrs = {
        'product':
        json.dumps(product_as_dict(request, addon, False, 'reviewer'),
                   cls=JSONEncoder),
        'manifest_url':
        addon.manifest_url,
    }

    pager = paginate(request, versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    ctx = context(request,
                  version=version,
                  product=addon,
                  pager=pager,
                  num_pages=num_pages,
                  count=count,
                  flags=Review.objects.filter(addon=addon, flag=True),
                  form=form,
                  canned=canned,
                  is_admin=is_admin,
                  status_types=amo.MKT_STATUS_CHOICES,
                  show_diff=show_diff,
                  allow_unchecking_files=allow_unchecking_files,
                  actions=actions,
                  actions_minimal=actions_minimal,
                  tab=queue_type,
                  product_attrs=product_attrs,
                  attachment_formset=attachment_formset,
                  appfeatures_form=appfeatures_form)

    if features_list is not None:
        ctx['feature_list'] = features_list

    return jingo.render(request, 'reviewers/review.html', ctx)
Example #34
0
 def test_total_few(self):
     for x in range(0, 5):
         amo.log(amo.LOG['APPROVE_VERSION'], Addon.objects.get())
     result = ActivityLog.objects.total_reviews()
     eq_(len(result), 1)
     eq_(result[0]['approval_count'], 5)
Example #35
0
    def save(self):
        action = self.cleaned_data['action']
        comment = self.cleaned_data.get('comment')
        reject_reason = self.cleaned_data.get('reject_reason')
        theme = self.cleaned_data['theme']

        is_rereview = (theme.rereviewqueuetheme_set.exists()
                       and theme.addon.status
                       not in (amo.STATUS_PENDING, amo.STATUS_REVIEW_PENDING))

        theme_lock = ThemeLock.objects.get(theme=self.cleaned_data['theme'])

        mail_and_log = True
        if action == rvw.ACTION_APPROVE:
            if is_rereview:
                approve_rereview(theme)
            theme.addon.update(status=amo.STATUS_PUBLIC)
            theme.approve = datetime.datetime.now()
            theme.save()

        elif action == rvw.ACTION_REJECT:
            if is_rereview:
                reject_rereview(theme)
            else:
                theme.addon.update(status=amo.STATUS_REJECTED)

        elif action == rvw.ACTION_DUPLICATE:
            if is_rereview:
                reject_rereview(theme)
            else:
                theme.addon.update(status=amo.STATUS_REJECTED)

        elif action == rvw.ACTION_FLAG:
            if is_rereview:
                mail_and_log = False
            else:
                theme.addon.update(status=amo.STATUS_REVIEW_PENDING)

        elif action == rvw.ACTION_MOREINFO:
            if not is_rereview:
                theme.addon.update(status=amo.STATUS_REVIEW_PENDING)

        if mail_and_log:
            send_mail(self.cleaned_data, theme_lock)

            # Log.
            amo.log(amo.LOG.THEME_REVIEW,
                    theme.addon,
                    details={
                        'theme': theme.addon.name.localized_string,
                        'action': action,
                        'reject_reason': reject_reason,
                        'comment': comment
                    },
                    user=theme_lock.reviewer)
            log.info('%sTheme %s (%s) - %s' %
                     ('[Rereview] ' if is_rereview else '', theme.addon.name,
                      theme.id, action))

        score = 0
        if action not in [rvw.ACTION_MOREINFO, rvw.ACTION_FLAG]:
            score = ReviewerScore.award_points(theme_lock.reviewer,
                                               theme.addon, theme.addon.status)
        theme_lock.delete()

        return score
Example #36
0
def _review(request, addon, version):

    if (not settings.ALLOW_SELF_REVIEWS
            and not acl.action_allowed(request, 'Admin', '%')
            and addon.has_author(request.amo_user)):
        messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.home'))

    if (addon.status == amo.STATUS_BLOCKED
            and not acl.action_allowed(request, 'Apps', 'ReviewEscalated')):
        messages.warning(
            request, _('Only senior reviewers can review blocklisted apps.'))
        return redirect(reverse('reviewers.home'))

    attachment_formset = forms.AttachmentFormSet(data=request.POST or None,
                                                 files=request.FILES or None,
                                                 prefix='attachment')
    form = forms.get_review_form(data=request.POST or None,
                                 files=request.FILES or None,
                                 request=request,
                                 addon=addon,
                                 version=version,
                                 attachment_formset=attachment_formset)
    queue_type = form.helper.review_type
    redirect_url = reverse('reviewers.apps.queue_%s' % queue_type)
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    forms_valid = lambda: form.is_valid() and attachment_formset.is_valid()
    if request.method == 'POST' and forms_valid():

        old_types = set(o.id for o in addon.device_types)
        new_types = set(form.cleaned_data.get('device_override'))

        if (form.cleaned_data.get('action') == 'public'
                and old_types != new_types):

            # The reviewer overrode the device types. We need to not publish
            # this app immediately.
            if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                addon.update(make_public=amo.PUBLIC_WAIT)

            # And update the device types to what the reviewer set.
            AddonDeviceType.objects.filter(addon=addon).delete()
            for device in form.cleaned_data.get('device_override'):
                addon.addondevicetype_set.create(device_type=device)

            # Log that the reviewer changed the device types.
            added_devices = new_types - old_types
            removed_devices = old_types - new_types
            msg = _(u'Device(s) changed by reviewer: {0}').format(', '.join([
                _(u'Added {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                for d in added_devices
            ] + [
                _(u'Removed {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                for d in removed_devices
            ]))
            amo.log(amo.LOG.REVIEW_DEVICE_OVERRIDE,
                    addon,
                    addon.current_version,
                    details={'comments': msg})

        form.helper.process()

        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.amo_user,
                                                     addon=addon)

        messages.success(request, _('Review successfully processed.'))
        return redirect(redirect_url)

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

    try:
        show_diff = (addon.versions.exclude(id=version.id).filter(
            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 from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # We only allow the user to check/uncheck files for "pending"
    allow_unchecking_files = form.helper.review_type == "pending"

    versions = (Version.with_deleted.filter(
        addon=addon).order_by('-created').transform(
            Version.transformer_activity).transform(Version.transformer))

    product_attrs = {
        'product':
        json.dumps(product_as_dict(request, addon, False, 'reviewer'),
                   cls=JSONEncoder),
        'manifest_url':
        addon.manifest_url,
    }

    pager = paginate(request, versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    ctx = context(version=version,
                  product=addon,
                  pager=pager,
                  num_pages=num_pages,
                  count=count,
                  flags=Review.objects.filter(addon=addon, flag=True),
                  form=form,
                  canned=canned,
                  is_admin=is_admin,
                  status_types=amo.STATUS_CHOICES,
                  show_diff=show_diff,
                  allow_unchecking_files=allow_unchecking_files,
                  actions=actions,
                  actions_minimal=actions_minimal,
                  tab=queue_type,
                  product_attrs=product_attrs,
                  attachment_formset=attachment_formset)

    return jingo.render(request, 'reviewers/review.html', ctx)
Example #37
0
 def remove_addon(self, addon):
     CollectionAddon.objects.filter(addon=addon, collection=self).delete()
     if self.listed:
         amo.log(amo.LOG.REMOVE_FROM_COLLECTION, addon, self)
     self.save()  # To invalidate Collection.
Example #38
0
 def add_addon(self, addon):
     "Adds an addon to the collection."
     CollectionAddon.objects.get_or_create(addon=addon, collection=self)
     if self.listed:
         amo.log(amo.LOG.ADD_TO_COLLECTION, addon, self)
     self.save()  # To invalidate Collection.
Example #39
0
 def test_log_not_admin(self):
     amo.log(amo.LOG['EDIT_VERSION'], Addon.objects.get())
     eq_(len(ActivityLog.objects.admin_events()), 0)
     eq_(len(ActivityLog.objects.for_developer()), 1)
Example #40
0
 def test_log_admin(self):
     amo.log(amo.LOG['OBJECT_EDITED'], Addon.objects.get())
     eq_(len(ActivityLog.objects.admin_events()), 1)
     eq_(len(ActivityLog.objects.for_developer()), 0)
Example #41
0
def payment(request, status=None):
    # Note this is not post required, because PayPal does not reply with a
    # POST but a GET, that's a sad face.
    pre, created = (PreApprovalUser.objects.safer_get_or_create(
        user=request.amo_user))

    context = {
        'preapproval': pre,
        'currency': CurrencyForm(initial={'currency': pre.currency or 'USD'})
    }

    if status:
        data = request.session.get('setup-preapproval', {})

        context['status'] = status

        if status == 'complete':
            # The user has completed the setup at PayPal and bounced back.
            if 'setup-preapproval' in request.session:
                if waffle.flag_is_active(request, 'solitude-payments'):
                    client.put_preapproval(data={'uuid': request.amo_user},
                                           pk=data['solitude-key'])

                paypal_log.info(u'Preapproval key created: %s' %
                                request.amo_user.pk)
                amo.log(amo.LOG.PREAPPROVAL_ADDED)
                # TODO(solitude): once this is turned off, we will want to
                # keep preapproval table populated with something, perhaps
                # a boolean inplace of pre-approval key.
                pre.update(paypal_key=data.get('key'),
                           paypal_expiry=data.get('expiry'))

                # If there is a target, bounce to it and don't show a message
                # we'll let whatever set this up worry about that.
                if data.get('complete'):
                    return http.HttpResponseRedirect(data['complete'])

                messages.success(
                    request,
                    _("You're all set for instant app purchases with PayPal."))
                del request.session['setup-preapproval']

        elif status == 'cancel':
            # The user has chosen to cancel out of PayPal. Nothing really
            # to do here, PayPal just bounce to the cancel page if defined.
            if data.get('cancel'):
                return http.HttpResponseRedirect(data['cancel'])

            messages.success(
                request, _('Your payment pre-approval has been cancelled.'))

        elif status == 'remove':
            # The user has an pre approval key set and chooses to remove it
            if waffle.flag_is_active(request, 'solitude-payments'):
                other = client.lookup_buyer_paypal(request.amo_user)
                if other:
                    client.patch_buyer_paypal(pk=other['resource_pk'],
                                              data={'key': ''})

            if pre.paypal_key:
                # TODO(solitude): again, we'll want to maintain some local
                # state in zamboni, so this will probably change to a
                # boolean in the future.
                pre.update(paypal_key='')

                amo.log(amo.LOG.PREAPPROVAL_REMOVED)
                messages.success(
                    request, _('Your payment pre-approval has been disabled.'))
                paypal_log.info(u'Preapproval key removed for user: %s' %
                                request.amo_user)

    return jingo.render(request, 'account/payment.html', context)
Example #42
0
def disable(request, addon_id, addon):
    addon.update(disabled_by_user=True)
    amo.log(amo.LOG.USER_DISABLE, addon)
    return redirect(addon.get_dev_url('versions'))
Example #43
0
def process_iarc_changes(date=None):
    """
    Queries IARC for recent changes in the past 24 hours (or date provided).

    If date provided use it. It should be in the form YYYY-MM-DD.

    """
    if not date:
        date = datetime.date.today()
    else:
        date = datetime.datetime.strptime(date, '%Y-%m-%d').date()

    client = lib.iarc.client.get_iarc_client('services')
    xml = lib.iarc.utils.render_xml('get_rating_changes.xml', {
        'date_from': date - datetime.timedelta(days=1),
        'date_to': date,
    })
    resp = client.Get_Rating_Changes(XMLString=xml)
    data = lib.iarc.utils.IARC_XML_Parser().parse_string(resp)

    for row in data.get('rows', []):
        iarc_id = row.get('submission_id')
        if not iarc_id:
            log.debug('IARC changes contained no submission ID: %s' % row)
            continue

        try:
            app = Webapp.objects.get(iarc_info__submission_id=iarc_id)
        except Webapp.DoesNotExist:
            log.debug('Could not find app associated with IARC submission ID: '
                      '%s' % iarc_id)
            continue

        try:  # Any exceptions we catch, log, and keep going.
            # Process 'new_rating'.
            ratings_body = row.get('rating_system')
            rating = RATINGS_MAPPING[ratings_body].get(row['new_rating'])

            _flag_rereview_adult(app, ratings_body, rating)

            # Process 'new_descriptors'.
            native_descs = filter(None, [
                s.strip() for s in row.get('new_descriptors', '').split(',')])
            descriptors = filter(None, [DESC_MAPPING[ratings_body].get(desc)
                                        for desc in native_descs])
            app.set_descriptors(descriptors)

            # Process 'new_interactiveelements'.
            native_interactives = filter(None, [
                s.strip() for s in
                row.get('new_interactiveelements', '').split(',')])
            interactives = filter(None, [INTERACTIVES_MAPPING.get(desc)
                                         for desc in native_interactives])
            app.set_interactives(interactives)

            # Save new rating.
            app.set_content_ratings({ratings_body: rating})

            # Log change reason.
            reason = row.get('change_reason')
            amo.log(amo.LOG.CONTENT_RATING_CHANGED, app,
                    details={'comments': '%s:%s, %s' %
                             (ratings_body.name, rating.name, reason)})

        except Exception as e:
            log.debug('Exception: %s' % e)
            continue
Example #44
0
 def test_review_last_month(self):
     log = amo.log(amo.LOG['APPROVE_VERSION'], Addon.objects.get())
     log.update(created=self.lm)
     eq_(len(ActivityLog.objects.monthly_reviews()), 0)
Example #45
0
 def delete(self):
     log.info(u'Version deleted: %r (%s)' % (self, self.id))
     amo.log(amo.LOG.DELETE_VERSION, self.addon, str(self.version))
     super(Version, self).delete()
Example #46
0
 def test_not_total(self):
     amo.log(amo.LOG['EDIT_VERSION'], Addon.objects.get())
     eq_(len(ActivityLog.objects.total_reviews()), 0)
Example #47
0
 def test_not_review_count(self):
     amo.log(amo.LOG['EDIT_VERSION'], Addon.objects.get())
     eq_(len(ActivityLog.objects.monthly_reviews()), 0)
Example #48
0
 def test_review_count(self):
     amo.log(amo.LOG['APPROVE_VERSION'], Addon.objects.get())
     result = ActivityLog.objects.monthly_reviews()
     eq_(len(result), 1)
     eq_(result[0]['approval_count'], 1)
     eq_(result[0]['user'], self.user.pk)
Example #49
0
 def destroy(self, request, *args, **kwargs):
     obj = self.get_object()
     amo.log(amo.LOG.DELETE_REVIEW, obj.addon, obj)
     log.debug('[Review:%s] Deleted by %s' %
               (obj.pk, self.request.amo_user.id))
     return super(RatingViewSet, self).destroy(request, *args, **kwargs)
Example #50
0
 def delete(self):
     amo.log(amo.LOG.DELETE_VERSION, self.addon, str(self.version))
     super(Version, self).delete()
Example #51
0
File: views.py Project: vdt/zamboni
                   'user': request.amo_user,
                   'contribution': contribution,
                   'refund_url': contribution.get_absolute_refund_url(),
                   'refund_reason': reason,
                   'request': request}
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))

        # L10n: %s is the app name.
        support_mail(_(u'New Refund Request for %s' % addon.name),
                     wizard.tpl('emails/refund-request.txt'), context,
                     settings.NOBODY_EMAIL, [smart_str(addon.support_email)])

        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        amo.log(amo.LOG.REFUND_REQUESTED, addon)
        return redirect(reverse('support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'),
                         {'product': addon, 'contribution': contribution,
                          'form': form, 'title': _('Request Refund')})


class SupportWizard(Wizard):
    title = _lazy('Support')
    steps = SortedDict([('start', plain),
                        ('site', plain),
                        ('resources', support_resources),
                        ('mozilla', support_mozilla),
                        ('mozilla-sent', plain),
Example #52
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))
    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 a in chain(user.passing_addons, user.failing_addons):
                try:
                    results = job.result_set.filter(file__version__addon=a)

                    a.links = [
                        absolutify(
                            reverse('devhub.bulk_compat_result',
                                    args=[a.slug, r.pk])) for r in results
                    ]

                    v = a.current_version or a.latest_version
                    a.compat_link = absolutify(
                        reverse('devhub.versions.edit', args=[a.pk, v.pk]))
                except:
                    task_error = sys.exc_info()
                    log.error(
                        u'Bulk validation email error for user %s, '
                        u'addon %s: %s: %s' %
                        (user.email, a.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)
                amo.log(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()))))
Example #53
0
def issue_refund(request, addon_id, addon, webapp=False):
    txn_id = request.REQUEST.get('transaction_id')
    if not txn_id:
        raise http.Http404
    form_enabled = True
    contribution = get_object_or_404(
        Contribution,
        transaction_id=txn_id,
        type__in=[amo.CONTRIB_PURCHASE, amo.CONTRIB_INAPP])

    if (hasattr(contribution, 'refund') and contribution.refund.status
            not in (amo.REFUND_PENDING, amo.REFUND_FAILED)):
        # If it's not pending, we've already taken action.
        messages.error(request, _('Refund already processed.'))
        form_enabled = False

    elif request.method == 'POST':
        if 'issue' in request.POST:
            try:
                results = paypal.refund(contribution.paykey)
            except PaypalError, e:
                contribution.record_failed_refund(e)
                paypal_log.error('Refund failed for: %s' % txn_id,
                                 exc_info=True)
                messages.error(request,
                               _('There was an error with '
                                 'the refund.'))
                return redirect(addon.get_dev_url('refunds'))

            for res in results:
                if res['refundStatus'] == 'ALREADY_REVERSED_OR_REFUNDED':
                    paypal_log.debug(
                        'Refund attempt for already-refunded paykey: %s, %s' %
                        (contribution.paykey, res['receiver.email']))
                    messages.error(
                        request,
                        _('Refund was previously issued; '
                          'no action taken.'))
                    return redirect(addon.get_dev_url('refunds'))
                elif res['refundStatus'] == 'NO_API_ACCESS_TO_RECEIVER':
                    paypal_log.debug(
                        'Refund attempt for product %s with no '
                        'refund token: %s, %s' %
                        (contribution.addon.pk, contribution.paykey,
                         res['receiver.email']))
                    messages.error(
                        request,
                        _("A refund can't be issued at this time. We've "
                          "notified an admin; please try again later."))
                    return redirect(addon.get_dev_url('refunds'))

            contribution.mail_approved()
            amo.log(amo.LOG.REFUND_GRANTED, addon, contribution.user)
            refund = contribution.enqueue_refund(amo.REFUND_APPROVED)
            paypal_log.info('Refund %r issued for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund issued.'))
        else:
            contribution.mail_declined()
            amo.log(amo.LOG.REFUND_DECLINED, addon, contribution.user)
            # TODO: Consider requiring a rejection reason for declined refunds.
            refund = contribution.enqueue_refund(amo.REFUND_DECLINED)
            paypal_log.info('Refund %r declined for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund declined.'))
        return redirect(addon.get_dev_url('refunds'))
Example #54
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        raise PermissionDenied

    # Get user agent of user submitting review. If there is an install with
    # logged user agent that matches the current user agent, hook up that
    # install's client data with the rating. If there aren't any install that
    # match, use the most recent install. This implies that user must have an
    # install to submit a review, but not sure if that logic is worked in, so
    # default client_data to None.
    client_data = None
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    install = (Installed.objects.filter(user=request.user,
                                        addon=addon).order_by('-created'))
    install_w_user_agent = (install.filter(
        client_data__user_agent=user_agent).order_by('-created'))
    has_review = False
    try:
        if install_w_user_agent:
            client_data = install_w_user_agent[0].client_data
        elif install:
            client_data = install[0].client_data
    except ClientData.DoesNotExist:
        client_data = None

    data = request.POST or None

    # Try to get an existing review of the app by this user if we can.
    try:
        existing_review = Review.objects.valid().filter(addon=addon,
                                                        user=request.user)[0]
    except IndexError:
        # If one doesn't exist, set it to None.
        existing_review = None

    # If the user is posting back, try to process the submission.
    if data:
        form = ReviewForm(data)
        if form.is_valid():
            cleaned = form.cleaned_data
            if existing_review:
                # If there's a review to overwrite, overwrite it.
                if (cleaned['body'] != existing_review.body
                        or cleaned['rating'] != existing_review.rating):
                    existing_review.body = cleaned['body']
                    existing_review.rating = cleaned['rating']
                    ip = request.META.get('REMOTE_ADDR', '')
                    existing_review.ip_address = ip
                    if 'flag' in cleaned and cleaned['flag']:
                        existing_review.flag = True
                        existing_review.editorreview = True
                        rf = ReviewFlag(review=existing_review,
                                        user_id=request.user.id,
                                        flag=ReviewFlag.OTHER,
                                        note='URLs')
                        rf.save()
                    existing_review.save()
                    # Update ratings and review counts.
                    addon_review_aggregates.delay(addon.id, using='default')

                amo.log(amo.LOG.EDIT_REVIEW, addon, existing_review)
                log.debug('[Review:%s] Edited by %s' %
                          (existing_review.id, request.user.id))
                messages.success(request,
                                 _('Your review was updated successfully!'))

                # If there is a developer reply to the review, delete it. We do
                # this per bug 777059.
                try:
                    reply = existing_review.replies.all()[0]
                except IndexError:
                    pass
                else:
                    log.debug('[Review:%s] Deleted reply to %s' %
                              (reply.id, existing_review.id))
                    reply.delete()

            else:
                # If there isn't a review to overwrite, create a new review.
                review = Review.objects.create(client_data=client_data,
                                               **_review_details(
                                                   request, addon, form))
                if 'flag' in cleaned and cleaned['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('[Review:%s] Created by user %s ' %
                          (review.id, request.user.id))
                messages.success(request,
                                 _('Your review was successfully added!'))

            Addon.objects.invalidate(*[addon])
            return redirect(addon.get_ratings_url('list'))

        # If the form isn't valid, we've set `form` so that it can be used when
        # the template is rendered below.

    elif existing_review:
        # If the user isn't posting back but has an existing review, populate
        # the form with their existing review and rating.
        form = ReviewForm({
            'rating': existing_review.rating or 1,
            'body': existing_review.body
        })
        has_review = True
    else:
        # If the user isn't posting back and doesn't have an existing review,
        # just show a blank version of the form.
        form = ReviewForm()

    # Get app's support url, either from support flow if contribution exists or
    # author's support url.
    support_email = str(addon.support_email) if addon.support_email else None
    try:
        contrib_id = (Contribution.objects.filter(
            user=request.user,
            addon=addon,
            type__in=(amo.CONTRIB_PURCHASE, amo.CONTRIB_INAPP,
                      amo.CONTRIB_REFUND)).order_by('-created')[0].id)
        support_url = reverse('support', args=[contrib_id])
    except IndexError:
        support_url = addon.support_url

    return jingo.render(
        request, 'ratings/add.html', {
            'product': addon,
            'form': form,
            'support_url': support_url,
            'has_review': has_review,
            'support_email': support_email
        })
Example #55
0
 def log_review(self, num):
     r = Review(addon=self.addon)
     for i in xrange(num):
         amo.log(amo.LOG.ADD_REVIEW, self.addon, r)
Example #56
0
def addons_section(request,
                   addon_id,
                   addon,
                   section,
                   editable=False,
                   webapp=False):
    basic = AppFormBasic if webapp else addon_forms.AddonFormBasic
    models = {
        'basic': basic,
        'media': AppFormMedia,
        'details': AppFormDetails,
        'support': AppFormSupport,
        'technical': addon_forms.AddonFormTechnical,
        'admin': forms.AdminSettingsForm
    }

    if section not in models:
        raise http.Http404()

    tags = previews = restricted_tags = []
    cat_form = device_type_form = None

    if section == 'basic':
        tags = addon.tags.not_blacklisted().values_list('tag_text', flat=True)
        cat_form = addon_forms.CategoryFormSet(request.POST or None,
                                               addon=addon,
                                               request=request)
        restricted_tags = addon.tags.filter(restricted=True)
        device_type_form = addon_forms.DeviceTypeForm(request.POST or None,
                                                      addon=addon)

    elif section == 'media':
        previews = PreviewFormSet(request.POST or None,
                                  prefix='files',
                                  queryset=addon.get_previews())

    elif (section == 'admin'
          and not acl.action_allowed(request, 'Apps', 'Configure')
          and not acl.action_allowed(request, 'Apps', 'ViewConfiguration')):
        return http.HttpResponseForbidden()

    # Get the slug before the form alters it to the form data.
    valid_slug = addon.app_slug
    if editable:
        if request.method == 'POST':

            if (section == 'admin'
                    and not acl.action_allowed(request, 'Apps', 'Configure')):
                return http.HttpResponseForbidden()

            form = models[section](request.POST,
                                   request.FILES,
                                   instance=addon,
                                   request=request)
            if form.is_valid() and (not previews or previews.is_valid()):

                addon = form.save(addon)

                if 'manifest_url' in form.changed_data:
                    addon.update(
                        app_domain=addon.domain_from_url(addon.manifest_url))
                    update_manifests([addon.pk])

                if previews:
                    for preview in previews.forms:
                        preview.save(addon)

                editable = False
                if section == 'media':
                    amo.log(amo.LOG.CHANGE_ICON, addon)
                else:
                    amo.log(amo.LOG.EDIT_PROPERTIES, addon)

                valid_slug = addon.app_slug
            if cat_form:
                if cat_form.is_valid():
                    cat_form.save()
                    addon.save()
                else:
                    editable = True
            if device_type_form:
                if device_type_form.is_valid():
                    device_type_form.save(addon)
                    addon.save()
                else:
                    editable = True
        else:
            form = models[section](instance=addon, request=request)
    else:
        form = False

    data = {
        'addon': addon,
        'webapp': webapp,
        'form': form,
        'editable': editable,
        'tags': tags,
        'restricted_tags': restricted_tags,
        'cat_form': cat_form,
        'preview_form': previews,
        'valid_slug': valid_slug,
        'device_type_form': device_type_form
    }

    return jingo.render(request, 'developers/apps/edit/%s.html' % section,
                        data)
Example #57
0
 def log_collection(self, num, prefix='foo'):
     for i in xrange(num):
         c = Collection.objects.create(name='%s %d' % (prefix, i))
         amo.log(amo.LOG.ADD_TO_COLLECTION, self.addon, c)
Example #58
0
def publicise(request, addon_id, addon):
    if addon.status == amo.STATUS_PUBLIC_WAITING:
        addon.update(status=amo.STATUS_PUBLIC)
        amo.log(amo.LOG.CHANGE_STATUS, addon.get_status_display(), addon)
    return redirect(addon.get_dev_url('versions'))
Example #59
0
 def log_status(self, num):
     for i in xrange(num):
         amo.log(amo.LOG.USER_DISABLE, self.addon)
Example #60
0
 def log_tag(self, num, prefix='foo'):
     for i in xrange(num):
         t = Tag.objects.create(tag_text='%s %d' % (prefix, i))
         amo.log(amo.LOG.ADD_TAG, self.addon, t)