示例#1
0
    def approve_content(self):
        """Approve content of an add-on."""
        channel = self.version.channel
        version = self.addon.current_version

        # Content review only action.
        assert self.content_review

        # Doesn't make sense for unlisted versions.
        assert channel == amo.RELEASE_CHANNEL_LISTED

        # Like confirm auto approval, the approve content action should not
        # show the comment box, so override the text in case the reviewer
        # switched between actions and accidently submitted some comments from
        # another action.
        self.data['comments'] = ''

        # When doing a content review, don't increment the approvals counter,
        # just record the date of the content approval and log it.
        AddonApprovalsCounter.approve_content_for_addon(addon=self.addon)
        self.log_action(amo.LOG.APPROVE_CONTENT, version=version)

        # Assign reviewer incentive scores.
        if self.human_review:
            is_post_review = channel == amo.RELEASE_CHANNEL_LISTED
            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       self.addon.status,
                                       version=version,
                                       post_review=is_post_review,
                                       content_review=self.content_review)
示例#2
0
    def award_all_points_for_action(self, action, content_review=False):
        for activity_log in ActivityLog.objects.filter(action=action.id):
            user = activity_log.user
            try:
                addon = activity_log.arguments[0]
                version = activity_log.arguments[1]
            except IndexError:
                log.error('ActivityLog %d is missing one or more arguments',
                          activity_log.pk)
                continue

            # If there is already a score recorded in the database for this
            # event, with our special note, it means we already processed it
            # somehow (maybe we ran the script twice...), so ignore it.
            # Otherwise award the points!
            event = ReviewerScore.get_event(addon,
                                            amo.STATUS_PUBLIC,
                                            version=version,
                                            post_review=True,
                                            content_review=content_review)
            if not ReviewerScore.objects.filter(
                    user=user, addon=addon, note_key=event,
                    note=MANUAL_NOTE).exists():
                ReviewerScore.award_points(user,
                                           addon,
                                           amo.STATUS_PUBLIC,
                                           version=version,
                                           post_review=True,
                                           content_review=content_review,
                                           extra_note=MANUAL_NOTE)
            else:
                log.error('Already awarded points for "%s" action on %s %s',
                          action.short, addon, version)
                continue
示例#3
0
    def confirm_auto_approved(self):
        """Confirm an auto-approval decision.

        We don't need to really store that information, what we care about
        is logging something for future reviewers to be aware of, and, if the
        version is listed, incrementing AddonApprovalsCounter, which also
        resets the last human review date to now, and log it so that it's
        displayed later in the review page."""
        status = self.addon.status
        latest_version = self.version
        # The confirm auto-approval action should not show the comment box,
        # so override the text in case the reviewer switched between actions
        # and accidently submitted some comments from another action.
        self.data['comments'] = ''
        if self.content_review_only:
            # If we're only doing a content review, then don't increment
            # the counter, just record the date of the content approval
            # and log it.
            AddonApprovalsCounter.approve_content_for_addon(addon=self.addon)
            self.log_action(amo.LOG.APPROVE_CONTENT)
        else:
            if self.version.channel == amo.RELEASE_CHANNEL_LISTED:
                self.version.autoapprovalsummary.update(confirmed=True)
                AddonApprovalsCounter.increment_for_addon(addon=self.addon)
            self.log_action(amo.LOG.CONFIRM_AUTO_APPROVED)

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(
                self.request.user, self.addon, status, version=latest_version,
                post_review=True, content_review=self.content_review_only)
    def award_all_points_for_action(self, action, content_review=False):
        for activity_log in ActivityLog.objects.filter(action=action.id):
            user = activity_log.user
            try:
                addon = activity_log.arguments[0]
                version = activity_log.arguments[1]
            except IndexError:
                log.error('ActivityLog %d is missing one or more arguments',
                          activity_log.pk)
                continue

            # If there is already a score recorded in the database for this
            # event, with our special note, it means we already processed it
            # somehow (maybe we ran the script twice...), so ignore it.
            # Otherwise award the points!
            event = ReviewerScore.get_event(
                addon, amo.STATUS_PUBLIC, version=version,
                post_review=True, content_review=content_review)
            if not ReviewerScore.objects.filter(
                    user=user, addon=addon, note_key=event,
                    note=MANUAL_NOTE).exists():
                ReviewerScore.award_points(
                    user, addon, amo.STATUS_PUBLIC,
                    version=version, post_review=True,
                    content_review=content_review, extra_note=MANUAL_NOTE)
            else:
                log.error('Already awarded points for "%s" action on %s %s',
                          action.short, addon, version)
                continue
示例#5
0
 def create_and_review_addon(self, user, weight, verdict, content_review):
     addon = addon_factory()
     AutoApprovalSummary.objects.create(
         version=addon.current_version, verdict=verdict, weight=weight)
     ReviewerScore.award_points(
         user, addon, addon.status, version=addon.versions.all()[0],
         post_review=True, content_review=content_review)
示例#6
0
    def process_sandbox(self):
        """Set an addon or a version back to sandbox."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use reject_multiple_versions instead).
        assert not self.content_review_only

        # Hold onto the status before we change it.
        status = self.addon.status

        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_NULL)
        self.set_files(amo.STATUS_DISABLED,
                       self.files,
                       hide_disabled_file=True)

        self.log_action(amo.LOG.REJECT_VERSION)
        template = u'%s_to_sandbox' % self.review_type
        subject = u'Mozilla Add-ons: %s %s didn\'t pass review'
        self.notify_email(template, subject)

        self.log_sandbox_message()
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(self.request.user,
                                       self.addon,
                                       status,
                                       version=self.version)
示例#7
0
    def process_sandbox(self):
        """Set an addon or a version back to sandbox."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use reject_multiple_versions instead).
        assert not self.content_review_only

        # Hold onto the status before we change it.
        status = self.addon.status

        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_NULL)
        self.set_files(amo.STATUS_DISABLED, self.files,
                       hide_disabled_file=True)

        self.log_action(amo.LOG.REJECT_VERSION)
        template = u'%s_to_sandbox' % self.review_type
        subject = u'Mozilla Add-ons: %s %s didn\'t pass review'
        self.notify_email(template, subject)

        self.log_sandbox_message()
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(
                self.request.user, self.addon, status, version=self.version)
示例#8
0
    def confirm_auto_approved(self):
        """Confirm an auto-approval decision."""

        channel = self.version.channel
        if channel == amo.RELEASE_CHANNEL_LISTED:
            # When doing an approval in listed channel, the version we care
            # about is always current_version and *not* self.version.
            # This allows reviewers to confirm approval of a public add-on even
            # when their latest version is disabled.
            version = self.addon.current_version
        else:
            # For unlisted, we just use self.version.
            version = self.version
        # The confirm auto-approval action should not show the comment box,
        # so override the text in case the reviewer switched between actions
        # and accidently submitted some comments from another action.
        self.data['comments'] = ''
        if channel == amo.RELEASE_CHANNEL_LISTED:
            version.autoapprovalsummary.update(confirmed=True)
            AddonApprovalsCounter.increment_for_addon(addon=self.addon)
        self.log_action(amo.LOG.CONFIRM_AUTO_APPROVED, version=version)

        # Assign reviewer incentive scores.
        if self.request:
            is_post_review = channel == amo.RELEASE_CHANNEL_LISTED
            ReviewerScore.award_points(
                self.request.user, self.addon, self.addon.status,
                version=version, post_review=is_post_review,
                content_review=self.content_review_only)
示例#9
0
    def approve_latest_version(self):
        """Approve the add-on latest version (potentially setting the add-on to
        approved if it was awaiting its first review)."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use confirm_auto_approved instead).
        assert not self.content_review

        # Sign addon.
        self.sign_files()

        # Hold onto the status before we change it.
        status = self.addon.status

        # Save files first, because set_addon checks to make sure there
        # is at least one public file or it won't make the addon public.
        self.set_files(amo.STATUS_APPROVED, self.files)
        self.set_promoted()
        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_APPROVED)

        if self.human_review:
            # No need for a human review anymore in this channel.
            self.clear_all_needs_human_review_flags_in_channel()

            # Clear pending rejection since we approved that version.
            VersionReviewerFlags.objects.filter(
                version=self.version, ).update(pending_rejection=None)

            # An approval took place so we can reset this.
            AddonReviewerFlags.objects.update_or_create(
                addon=self.addon,
                defaults={'auto_approval_disabled_until_next_approval': False})

            # The counter can be incremented.
            AddonApprovalsCounter.increment_for_addon(addon=self.addon)

            # Assign reviewer incentive scores.
            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       status,
                                       version=self.version)
        else:
            # Automatic approval, reset the counter.
            AddonApprovalsCounter.reset_for_addon(addon=self.addon)

        self.log_action(amo.LOG.APPROVE_VERSION)
        template = u'%s_to_approved' % self.review_type
        if self.review_type in ['extension_pending', 'theme_pending']:
            subject = u'Mozilla Add-ons: %s %s Updated'
        else:
            subject = u'Mozilla Add-ons: %s %s Approved'
        self.notify_email(template, subject)

        self.log_public_message()
        log.info(u'Sending email for %s' % (self.addon))
示例#10
0
    def reject_multiple_versions(self):
        """Reject a list of versions."""
        # self.version and self.files won't point to the versions we want to
        # modify in this action, so set them to None before finding the right
        # versions.
        status = self.addon.status
        latest_version = self.version
        self.version = None
        self.files = None
        action_id = (amo.LOG.REJECT_CONTENT
                     if self.content_review else amo.LOG.REJECT_VERSION)
        timestamp = datetime.now()
        log.info(
            u'Making %s versions %s disabled' %
            (self.addon, u', '.join(str(v.pk) for v in self.data['versions'])))
        for version in self.data['versions']:
            files = version.files.all()
            self.set_files(amo.STATUS_DISABLED, files, hide_disabled_file=True)
            self.log_action(action_id,
                            version=version,
                            files=files,
                            timestamp=timestamp)
            if self.human_review:
                # Unset needs_human_review on rejected versions, we consider
                # that the reviewer looked at them before rejecting.
                if version.needs_human_review:
                    version.update(needs_human_review=False)

        self.addon.update_status()

        # Assign reviewer incentive scores and send email, if it's an human
        # reviewer: if it's not, it's coming from some automation where we
        # don't need to notify the developer (we should already have done that
        # before) and don't need to award points.
        if self.human_review:
            channel = latest_version.channel
            # Send the email to the developer. We need to pass the latest
            # version of the add-on instead of one of the versions we rejected,
            # it will be used to generate a token allowing the developer to
            # reply, and that only works with the latest version.
            self.data['version_numbers'] = u', '.join(
                str(v.version) for v in self.data['versions'])
            if (self.addon.status != amo.STATUS_APPROVED
                    and channel == amo.RELEASE_CHANNEL_LISTED):
                template = u'reject_multiple_versions_disabled_addon'
                subject = (u'Mozilla Add-ons: %s%s has been disabled on '
                           u'addons.mozilla.org')
            else:
                template = u'reject_multiple_versions'
                subject = u'Mozilla Add-ons: Versions disabled for %s%s'
            log.info(u'Sending email for %s' % (self.addon))
            self.notify_email(template, subject, version=latest_version)

            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       status,
                                       version=latest_version,
                                       post_review=True,
                                       content_review=self.content_review)
示例#11
0
    def process_public(self):
        """Set an add-on or a version to public."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use confirm_auto_approved instead).
        assert not self.content_review_only

        # Sign addon.
        use_autograph = waffle.flag_is_active(self.request,
                                              'activate-autograph-signing')
        for file_ in self.files:
            sign_file(file_, use_autograph=use_autograph)

        # Hold onto the status before we change it.
        status = self.addon.status

        # Save files first, because set_addon checks to make sure there
        # is at least one public file or it won't make the addon public.
        self.set_files(amo.STATUS_PUBLIC, self.files)
        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_PUBLIC)

        # If we've approved a webextension, add a tag identifying them as such.
        if any(file_.is_webextension for file_ in self.files):
            Tag(tag_text='firefox57').save_tag(self.addon)

        # If we've approved a mozilla signed add-on, add the firefox57 tag
        if all(file_.is_mozilla_signed_extension for file_ in self.files):
            Tag(tag_text='firefox57').save_tag(self.addon)

        # Increment approvals counter if we have a request (it means it's a
        # human doing the review) otherwise reset it as it's an automatic
        # approval.
        if self.request:
            AddonApprovalsCounter.increment_for_addon(addon=self.addon)
        else:
            AddonApprovalsCounter.reset_for_addon(addon=self.addon)

        self.log_action(amo.LOG.APPROVE_VERSION)
        template = u'%s_to_public' % self.review_type
        if self.review_type == 'pending':
            subject = u'Mozilla Add-ons: %s %s Updated'
        else:
            subject = u'Mozilla Add-ons: %s %s Approved'
        self.notify_email(template, subject)

        self.log_public_message()
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(self.request.user,
                                       self.addon,
                                       status,
                                       version=self.version)
示例#12
0
    def confirm_auto_approved(self):
        """Confirm an auto-approval decision."""

        channel = self.version.channel
        if channel == amo.RELEASE_CHANNEL_LISTED:
            # When doing an approval in listed channel, the version we care
            # about is always current_version and *not* self.version.
            # This allows reviewers to confirm approval of a public add-on even
            # when their latest version is disabled.
            version = self.addon.current_version
        else:
            # For unlisted, we just use self.version.
            version = self.version
        # The confirm auto-approval action should not show the comment box,
        # so override the text in case the reviewer switched between actions
        # and accidently submitted some comments from another action.
        self.data['comments'] = ''

        self.log_action(amo.LOG.CONFIRM_AUTO_APPROVED, version=version)

        if self.human_review:
            # Mark the approval as confirmed (handle DoesNotExist, it may have
            # been auto-approved before we unified workflow for unlisted and
            # listed).
            try:
                version.autoapprovalsummary.update(confirmed=True)
            except AutoApprovalSummary.DoesNotExist:
                pass

            if channel == amo.RELEASE_CHANNEL_LISTED:
                # Clear needs_human_review flags on past versions in channel.
                self.unset_past_needs_human_review()
                AddonApprovalsCounter.increment_for_addon(addon=self.addon)
            else:
                # For now, for unlisted versions, only drop the
                # needs_human_review flag on the latest version.
                if self.version.needs_human_review:
                    self.version.update(needs_human_review=False)

            # Clear the "needs_human_review_by_mad" and "pending_rejection"
            # flags too, if any, only for the specified version.
            VersionReviewerFlags.objects.filter(version=self.version).update(
                needs_human_review_by_mad=False, pending_rejection=None)

            # For other versions, we also clear pending_rejection (Note that
            # the action should only be accessible to admins if the current
            # version is pending rejection).
            VersionReviewerFlags.objects.filter(
                version__addon=self.addon).update(pending_rejection=None)

            is_post_review = channel == amo.RELEASE_CHANNEL_LISTED
            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       self.addon.status,
                                       version=version,
                                       post_review=is_post_review,
                                       content_review=self.content_review)
示例#13
0
    def process_public(self):
        """Set an add-on or a version to public."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use confirm_auto_approved instead).
        assert not self.content_review

        # Sign addon.
        self.sign_files()

        # Hold onto the status before we change it.
        status = self.addon.status

        # Save files first, because set_addon checks to make sure there
        # is at least one public file or it won't make the addon public.
        self.set_files(amo.STATUS_APPROVED, self.files)
        self.set_recommended()
        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_APPROVED)

        # Clear needs_human_review flags on past listed versions.
        if self.human_review:
            self.unset_past_needs_human_review()
            # Clear the "needs_human_review" scanner flags too, if any, and
            # only for the specified version.
            VersionReviewerFlags.objects.filter(version=self.version).update(
                needs_human_review_by_mad=False)

        # Increment approvals counter if we have a request (it means it's a
        # human doing the review) otherwise reset it as it's an automatic
        # approval.
        if self.human_review:
            AddonApprovalsCounter.increment_for_addon(addon=self.addon)
        else:
            AddonApprovalsCounter.reset_for_addon(addon=self.addon)

        self.log_action(amo.LOG.APPROVE_VERSION)
        template = u'%s_to_approved' % self.review_type
        if self.review_type in ['extension_pending', 'theme_pending']:
            subject = u'Mozilla Add-ons: %s %s Updated'
        else:
            subject = u'Mozilla Add-ons: %s %s Approved'
        self.notify_email(template, subject)

        self.log_public_message()
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.human_review:
            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       status,
                                       version=self.version)
示例#14
0
    def process_public(self):
        """Set an add-on or a version to public."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use confirm_auto_approved instead).
        assert not self.content_review_only

        # Sign addon.
        for file_ in self.files:
            sign_file(file_)

        # Hold onto the status before we change it.
        status = self.addon.status

        # Save files first, because set_addon checks to make sure there
        # is at least one public file or it won't make the addon public.
        self.set_files(amo.STATUS_PUBLIC, self.files)
        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_PUBLIC)

        # If we've approved a webextension, add a tag identifying them as such.
        if any(file_.is_webextension for file_ in self.files):
            Tag(tag_text='firefox57').save_tag(self.addon)

        # If we've approved a mozilla signed add-on, add the firefox57 tag
        if all(file_.is_mozilla_signed_extension for file_ in self.files):
            Tag(tag_text='firefox57').save_tag(self.addon)

        # Increment approvals counter if we have a request (it means it's a
        # human doing the review) otherwise reset it as it's an automatic
        # approval.
        if self.request:
            AddonApprovalsCounter.increment_for_addon(addon=self.addon)
        else:
            AddonApprovalsCounter.reset_for_addon(addon=self.addon)

        self.log_action(amo.LOG.APPROVE_VERSION)
        template = u'%s_to_public' % self.review_type
        if self.review_type == 'pending':
            subject = u'Mozilla Add-ons: %s %s Updated'
        else:
            subject = u'Mozilla Add-ons: %s %s Approved'
        self.notify_email(template, subject)

        self.log_public_message()
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(
                self.request.user, self.addon, status, version=self.version)
示例#15
0
    def reject_multiple_versions(self):
        """Reject a list of versions."""
        # self.version and self.files won't point to the versions we want to
        # modify in this action, so set them to None before finding the right
        # versions.
        status = self.addon.status
        latest_version = self.version
        self.version = None
        self.files = None
        action_id = (amo.LOG.REJECT_CONTENT
                     if self.content_review_only else amo.LOG.REJECT_VERSION)
        timestamp = datetime.now()
        for version in self.data['versions']:
            files = version.files.all()
            self.set_files(amo.STATUS_DISABLED, files, hide_disabled_file=True)
            self.log_action(action_id,
                            version=version,
                            files=files,
                            timestamp=timestamp)
        self.addon.update_status()
        self.data['version_numbers'] = u', '.join(
            unicode(v.version) for v in self.data['versions'])

        # Send the email to the developer. We need to pass the latest version
        # of the add-on instead of one of the versions we rejected, it will be
        # used to generate a token allowing the developer to reply, and that
        # only works with the latest version.
        if self.addon.status != amo.STATUS_PUBLIC:
            template = u'reject_multiple_versions_disabled_addon'
            subject = (u'Mozilla Add-ons: %s%s has been disabled on '
                       u'addons.mozilla.org')
        else:
            template = u'reject_multiple_versions'
            subject = u'Mozilla Add-ons: Versions disabled for %s%s'
        self.notify_email(template, subject, version=latest_version)

        log.info(u'Making %s versions %s disabled' % (self.addon, u', '.join(
            unicode(v.pk) for v in self.data['versions'])))
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(self.request.user,
                                       self.addon,
                                       status,
                                       version=latest_version,
                                       post_review=True,
                                       content_review=self.content_review_only)
示例#16
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 == amo.ACTION_APPROVE:
            if is_rereview:
                approve_rereview(theme)
            theme.addon.update(status=amo.STATUS_PUBLIC)
            theme.approve = datetime.datetime.now()
            theme.save()

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

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

        elif action == amo.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.
            ActivityLog.create(
                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 in (amo.ACTION_REJECT, amo.ACTION_DUPLICATE,
                      amo.ACTION_APPROVE):
            score = ReviewerScore.award_points(
                theme_lock.reviewer, theme.addon, theme.addon.status)
        theme_lock.delete()

        return score
示例#17
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 == amo.ACTION_APPROVE:
            if is_rereview:
                approve_rereview(theme)
            theme.addon.update(status=amo.STATUS_PUBLIC)
            theme.approve = datetime.datetime.now()
            theme.save()

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

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

        elif action == amo.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.
            ActivityLog.create(
                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 in (amo.ACTION_REJECT, amo.ACTION_DUPLICATE,
                      amo.ACTION_APPROVE):
            score = ReviewerScore.award_points(
                theme_lock.reviewer, theme.addon, theme.addon.status)
        theme_lock.delete()

        return score
示例#18
0
    def reject_multiple_versions(self):
        """Reject a list of versions."""
        # self.version and self.files won't point to the versions we want to
        # modify in this action, so set them to None before finding the right
        # versions.
        status = self.addon.status
        latest_version = self.version
        self.version = None
        self.files = None
        action_id = (amo.LOG.REJECT_CONTENT if self.content_review_only
                     else amo.LOG.REJECT_VERSION)
        timestamp = datetime.now()
        for version in self.data['versions']:
            files = version.files.all()
            self.set_files(amo.STATUS_DISABLED, files, hide_disabled_file=True)
            self.log_action(action_id, version=version, files=files,
                            timestamp=timestamp)
        self.addon.update_status()
        self.data['version_numbers'] = u', '.join(
            six.text_type(v.version) for v in self.data['versions'])

        # Send the email to the developer. We need to pass the latest version
        # of the add-on instead of one of the versions we rejected, it will be
        # used to generate a token allowing the developer to reply, and that
        # only works with the latest version.
        if self.addon.status != amo.STATUS_APPROVED:
            template = u'reject_multiple_versions_disabled_addon'
            subject = (u'Mozilla Add-ons: %s%s has been disabled on '
                       u'addons.mozilla.org')
        else:
            template = u'reject_multiple_versions'
            subject = u'Mozilla Add-ons: Versions disabled for %s%s'
        self.notify_email(template, subject, version=latest_version)

        log.info(
            u'Making %s versions %s disabled' % (
                self.addon,
                u', '.join(
                    six.text_type(v.pk) for v in self.data['versions'])))
        log.info(u'Sending email for %s' % (self.addon))

        # Assign reviewer incentive scores.
        if self.request:
            ReviewerScore.award_points(
                self.request.user, self.addon, status, version=latest_version,
                post_review=True, content_review=self.content_review_only)
示例#19
0
    def reject_latest_version(self):
        """Reject the add-on latest version (potentially setting the add-on
        back to incomplete if it was awaiting its first review)."""
        # Safeguard to force implementation for unlisted add-ons to completely
        # override this method.
        assert self.version.channel == amo.RELEASE_CHANNEL_LISTED

        # Safeguard to make sure this action is not used for content review
        # (it should use reject_multiple_versions instead).
        assert not self.content_review

        # Hold onto the status before we change it.
        status = self.addon.status

        if self.set_addon_status:
            self.set_addon(status=amo.STATUS_NULL)
        self.set_files(amo.STATUS_DISABLED,
                       self.files,
                       hide_disabled_file=True)

        if self.human_review:
            # Clear needs human review flags, but only on the latest version:
            # it's the only version we can be certain that the reviewer looked
            # at.
            self.clear_specific_needs_human_review_flags(self.version)

            # Assign reviewer incentive scores.
            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       status,
                                       version=self.version)

        self.log_action(amo.LOG.REJECT_VERSION)
        template = u'%s_to_rejected' % self.review_type
        subject = u'Mozilla Add-ons: %s %s didn\'t pass review'
        self.notify_email(template, subject)

        self.log_sandbox_message()
        log.info(u'Sending email for %s' % (self.addon))
示例#20
0
    def confirm_auto_approved(self):
        """Confirm an auto-approval decision."""

        channel = self.version.channel
        if channel == amo.RELEASE_CHANNEL_LISTED:
            # When doing an approval in listed channel, the version we care
            # about is always current_version and *not* self.version.
            # This allows reviewers to confirm approval of a public add-on even
            # when their latest version is disabled.
            version = self.addon.current_version
        else:
            # For unlisted, we just use self.version.
            version = self.version
        # The confirm auto-approval action should not show the comment box,
        # so override the text in case the reviewer switched between actions
        # and accidently submitted some comments from another action.
        self.data['comments'] = ''
        if self.content_review_only:
            # If we're only doing a content review, then don't increment
            # the counter, just record the date of the content approval
            # and log it.
            AddonApprovalsCounter.approve_content_for_addon(addon=self.addon)
            self.log_action(amo.LOG.APPROVE_CONTENT, version=version)
        else:
            if channel == amo.RELEASE_CHANNEL_LISTED:
                version.autoapprovalsummary.update(confirmed=True)
                AddonApprovalsCounter.increment_for_addon(addon=self.addon)
            self.log_action(amo.LOG.CONFIRM_AUTO_APPROVED, version=version)

        # Assign reviewer incentive scores.
        if self.request:
            is_post_review = channel == amo.RELEASE_CHANNEL_LISTED
            ReviewerScore.award_points(
                self.request.user, self.addon, self.addon.status,
                version=version, post_review=is_post_review,
                content_review=self.content_review_only)
示例#21
0
    def generate_review_data(self):
        with freeze_time(self.last_week_begin):
            self.reviewer1 = user_factory(display_name='Volunteer A')
            self.reviewer2 = user_factory(display_name='Staff B')
            self.reviewer3 = user_factory(display_name=None)
            self.reviewer4 = user_factory(display_name='Staff Content D')
            self.reviewer5 = user_factory(display_name='Deleted')
            grant_permission(self.reviewer2, '', name='No Reviewer Incentives')
            grant_permission(self.reviewer4, '', name='No Reviewer Incentives')

            data = [
                (self.reviewer1, 178, amo.AUTO_APPROVED, False),
                (self.reviewer1, 95, amo.AUTO_APPROVED, False),
                (self.reviewer1, 123, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer1, 328, amo.AUTO_APPROVED, False),
                (self.reviewer1, 450, amo.AUTO_APPROVED, False),
                (self.reviewer1, 999, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer1, 131, amo.AUTO_APPROVED, False),
                (self.reviewer1, 74, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer1, 15, amo.AUTO_APPROVED, False),
                (self.reviewer2, 951, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer2, 8421, amo.AUTO_APPROVED, False),
                (self.reviewer2, 281, amo.AUTO_APPROVED, False),
                (self.reviewer2, 54, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer2, 91, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer2, 192, amo.AUTO_APPROVED, False),
                (self.reviewer2, 222, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer3, 178, amo.AUTO_APPROVED, True),
                (self.reviewer3, 95, amo.AUTO_APPROVED, True),
                (self.reviewer3, 123, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer3, 328, amo.AUTO_APPROVED, True),
                (self.reviewer3, 450, amo.AUTO_APPROVED, True),
                (self.reviewer3, 999, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer3, 131, amo.AUTO_APPROVED, True),
                (self.reviewer3, 74, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer3, 15, amo.AUTO_APPROVED, True),
                (self.reviewer3, 48, amo.AUTO_APPROVED, True),
                (self.reviewer3, 87, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer3, 265, amo.AUTO_APPROVED, True),
                (self.reviewer4, 951, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer4, 8421, amo.AUTO_APPROVED, True),
                (self.reviewer4, 281, amo.AUTO_APPROVED, True),
                (self.reviewer4, 54, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer4, 91, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer4, 192, amo.AUTO_APPROVED, True),
                (self.reviewer4, 222, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer4, 192, amo.AUTO_APPROVED, True),
                (self.reviewer4, 444, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer4, 749, amo.AUTO_APPROVED, True),
                (self.reviewer5, 523, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer5, 126, amo.AUTO_APPROVED, True),
                (self.reviewer5, 246, amo.AUTO_APPROVED, False),
                (self.reviewer5, 8740, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer5, 346, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer5, 985, amo.AUTO_APPROVED, False),
                (self.reviewer5, 123, amo.NOT_AUTO_APPROVED, True),
                (self.reviewer5, 93, amo.AUTO_APPROVED, True),
                (self.reviewer5, 22, amo.NOT_AUTO_APPROVED, False),
                (self.reviewer5, 165, amo.AUTO_APPROVED, True),
            ]
            for review_action in data:
                self.create_and_review_addon(review_action[0],
                                             review_action[1],
                                             review_action[2],
                                             review_action[3])

            self.reviewer5.delete()
            mail.outbox = []

            # Search plugin (submitted before auto-approval was implemented)
            search_plugin = addon_factory(type=4)
            ReviewerScore.award_points(self.reviewer3,
                                       search_plugin,
                                       amo.STATUS_APPROVED,
                                       version=search_plugin.versions.all()[0],
                                       post_review=False,
                                       content_review=True)

            # Dictionary (submitted before auto-approval was implemented)
            dictionary = addon_factory(type=3)
            ReviewerScore.award_points(self.reviewer3,
                                       dictionary,
                                       amo.STATUS_APPROVED,
                                       version=dictionary.versions.all()[0],
                                       post_review=False,
                                       content_review=True)
示例#22
0
    def reject_multiple_versions(self):
        """Reject a list of versions.
        Note: this is used in blocklist.utils.disable_addon_for_block for both
        listed and unlisted versions (human_review=False)."""
        # self.version and self.files won't point to the versions we want to
        # modify in this action, so set them to None before finding the right
        # versions.
        status = self.addon.status
        latest_version = self.version
        self.version = None
        self.files = None
        now = datetime.now()
        if self.data.get('delayed_rejection'):
            pending_rejection_deadline = now + timedelta(
                days=int(self.data['delayed_rejection_days']))
        else:
            pending_rejection_deadline = None
        if pending_rejection_deadline:
            action_id = (amo.LOG.REJECT_CONTENT_DELAYED if self.content_review
                         else amo.LOG.REJECT_VERSION_DELAYED)
            log.info('Marking %s versions %s for delayed rejection' %
                     (self.addon, ', '.join(
                         str(v.pk) for v in self.data['versions'])))
        else:
            action_id = (amo.LOG.REJECT_CONTENT
                         if self.content_review else amo.LOG.REJECT_VERSION)
            log.info('Making %s versions %s disabled' % (self.addon, ', '.join(
                str(v.pk) for v in self.data['versions'])))

        for version in self.data['versions']:
            files = version.files.all()
            if not pending_rejection_deadline:
                self.set_files(amo.STATUS_DISABLED,
                               files,
                               hide_disabled_file=True)
            self.log_action(action_id,
                            version=version,
                            files=files,
                            timestamp=now)
            if self.human_review:
                # Clear needs human review flags on rejected versions, we
                # consider that the reviewer looked at them before rejecting.
                self.clear_specific_needs_human_review_flags(version)
                # (Re)set pending_rejection. Could be reset to None if doing an
                # immediate rejection.
                VersionReviewerFlags.objects.update_or_create(
                    version=version,
                    defaults={'pending_rejection': pending_rejection_deadline})

        if pending_rejection_deadline:
            # A delayed rejection implies the next version should be manually
            # reviewed and the developers should be notified again once the
            # deadline is close.
            AddonReviewerFlags.objects.update_or_create(
                addon=self.addon,
                defaults={
                    'notified_about_expiring_delayed_rejections': False,
                    'auto_approval_disabled_until_next_approval': True
                })
            # The reviewer should be automatically subscribed to any new
            # versions posted to the same channel.
            ReviewerSubscription.objects.get_or_create(
                user=self.user,
                addon=self.addon,
                channel=latest_version.channel)
        else:
            # An immediate one might require the add-on status to change.
            self.addon.update_status()

        # Assign reviewer incentive scores and send email, if it's an human
        # reviewer: if it's not, it's coming from some automation where we
        # don't need to notify the developer (we should already have done that
        # before) and don't need to award points.
        if self.human_review:
            channel = latest_version.channel
            # Send the email to the developer. We need to pass the latest
            # version of the add-on instead of one of the versions we rejected,
            # it will be used to generate a token allowing the developer to
            # reply, and that only works with the latest version.
            self.data['version_numbers'] = u', '.join(
                str(v.version) for v in self.data['versions'])
            if pending_rejection_deadline:
                template = 'reject_multiple_versions_with_delay'
                subject = ('Mozilla Add-ons: %s%s will be disabled on '
                           'addons.mozilla.org')
            elif (self.addon.status != amo.STATUS_APPROVED
                  and channel == amo.RELEASE_CHANNEL_LISTED):
                template = 'reject_multiple_versions_disabled_addon'
                subject = ('Mozilla Add-ons: %s%s has been disabled on '
                           'addons.mozilla.org')
            else:
                template = 'reject_multiple_versions'
                subject = 'Mozilla Add-ons: Versions disabled for %s%s'
            log.info('Sending email for %s' % (self.addon))
            self.notify_email(template, subject, version=latest_version)

            ReviewerScore.award_points(self.user,
                                       self.addon,
                                       status,
                                       version=latest_version,
                                       post_review=True,
                                       content_review=self.content_review)