예제 #1
0
 def setUp(self):
     # Run the tests as a logged-in user.
     super(TestAssignmentNotification, self).setUp(
         user='******')
     self.user = getUtility(ILaunchBag).user
     self.product = self.factory.makeProduct(owner=self.user,
                                             name='rebirth')
     self.bug = self.factory.makeBug(target=self.product)
     self.bug_task = self.bug.getBugTask(self.product)
     self.bug_task_before_modification = Snapshot(self.bug_task,
         providing=providedBy(self.bug_task))
     self.person_assigned_email = '*****@*****.**'
     self.person_assigned = self.factory.makePerson(
         name='assigned', displayname='Steve Rogers',
         email=self.person_assigned_email)
     self.team_member_email = '*****@*****.**'
     self.team_member = self.factory.makePerson(
         name='giantman', displayname='Hank Pym',
         email=self.team_member_email)
     self.team_assigned = self.factory.makeTeam(
         name='avengers', owner=self.user)
     self.team_assigned.addMember(self.team_member, self.user)
     # adding people to a team generates email
     transaction.commit()
     del stub.test_emails[:]
예제 #2
0
 def test_no_modification(self):
     # If there are no modifications, no delta is returned.
     repository = self.factory.makeGitRepository(name=u"foo")
     old_repository = Snapshot(repository, providing=providedBy(repository))
     delta = GitRepositoryDelta.construct(old_repository, repository,
                                          repository.owner)
     self.assertIsNone(delta)
예제 #3
0
    def request_action(self, action, data):
        changed = False
        recipe_before_modification = Snapshot(self.context,
                                              providing=providedBy(
                                                  self.context))

        recipe_text = data.pop('recipe_text')
        parser = RecipeParser(recipe_text)
        recipe = parser.parse()
        if self.context.builder_recipe != recipe:
            try:
                self.error_handler(self.context.setRecipeText, recipe_text)
                changed = True
            except ErrorHandled:
                return

        distros = data.pop('distroseries')
        if distros != self.context.distroseries:
            self.context.distroseries.clear()
            for distroseries_item in distros:
                self.context.distroseries.add(distroseries_item)
            changed = True

        if self.updateContextFromData(data, notify_modified=False):
            changed = True

        if changed:
            field_names = [
                form_field.__name__ for form_field in self.form_fields
            ]
            notify(
                ObjectModifiedEvent(self.context, recipe_before_modification,
                                    field_names))

        self.next_url = canonical_url(self.context)
예제 #4
0
    def execute(self, context, current_event):
        """See IEmailCommand."""
        if isinstance(context, CreateBugParams):
            # No one intentially reports a duplicate bug. Bug email commands
            # support CreateBugParams, so in this case, just return.
            return context, current_event
        self._ensureNumberOfArguments()
        [bug_id] = self.string_args

        if bug_id != 'no':
            try:
                bug = getUtility(IBugSet).getByNameOrID(bug_id)
            except NotFoundError:
                raise EmailProcessingError(
                    get_error_message('no-such-bug.txt',
                                      error_templates=error_templates,
                                      bug_id=bug_id))
        else:
            # 'no' is a special value for unmarking a bug as a duplicate.
            bug = None

        duplicate_field = IBug['duplicateof'].bind(context)
        try:
            duplicate_field.validate(bug)
        except ValidationError as error:
            raise EmailProcessingError(error.doc())

        context_snapshot = Snapshot(context, providing=providedBy(context))
        context.markAsDuplicate(bug)
        current_event = ObjectModifiedEvent(context, context_snapshot,
                                            'duplicateof')
        notify(current_event)
        return bug, current_event
예제 #5
0
 def linkBug(self, action, data):
     """Link to the requested bug. Publish an ObjectModifiedEvent and
     display a notification.
     """
     response = self.request.response
     target_unmodified = Snapshot(self.context,
                                  providing=providedBy(self.context))
     bug = data['bug']
     try:
         self.context.linkBug(bug, user=self.user)
     except Unauthorized:
         # XXX flacoste 2006-08-23 bug=57470: This should use proper _().
         self.setFieldError(
             'bug',
             'You are not allowed to link to private bug #%d.' % bug.id)
         return
     bug_props = {'bugid': bug.id, 'title': bug.title}
     response.addNotification(
         _(
             u'Added link to bug #$bugid: '
             u'\N{left double quotation mark}$title'
             u'\N{right double quotation mark}.',
             mapping=bug_props))
     notify(ObjectModifiedEvent(self.context, target_unmodified, ['bugs']))
     self.next_url = canonical_url(self.context)
예제 #6
0
    def test_workitems_added_notification_message(self):
        """ Test that we get a notification for setting work items on a new
        specification."""
        stub.test_emails = []
        spec = self.factory.makeSpecification()
        old_spec = Snapshot(spec, providing=providedBy(spec))
        new_work_item = {
            'title': u'A work item',
            'status': SpecificationWorkItemStatus.TODO,
            'assignee': None,
            'milestone': None,
            'sequence': 0
        }

        login_person(spec.owner)
        spec.updateWorkItems([new_work_item])
        # For API requests, lazr.restful does the notify() call, for this test
        # we need to call ourselves.
        transaction.commit()
        notify(ObjectModifiedEvent(
            spec, old_spec, edited_fields=['workitems_text']))
        transaction.commit()

        self.assertEqual(1, len(stub.test_emails))
        rationale = 'Work items set to:\nWork items:\n%s: %s' % (
            new_work_item['title'],
            new_work_item['status'].name)
        [email] = stub.test_emails
        # Actual message is part 2 of the e-mail.
        msg = email[2]
        self.assertIn(rationale, msg)
예제 #7
0
    def updateContextFromData(self, data, context=None, notify_modified=True):
        """Update the context object based on form data.

        If no context is given, the view's context is used.

        If any changes were made, ObjectModifiedEvent will be
        emitted.

        This method should be called by an action method of the form.

        Returns True if there were any changes to apply.
        """
        if context is None:
            context = self.context
        if notify_modified:
            context_before_modification = Snapshot(
                context, providing=providedBy(context))

        was_changed = form.applyChanges(context, self.form_fields, data,
                                        self.adapters)
        if was_changed and notify_modified:
            field_names = [
                form_field.__name__ for form_field in self.form_fields
            ]
            notify(
                ObjectModifiedEvent(context, context_before_modification,
                                    field_names))
        return was_changed
예제 #8
0
 def remove_action(self, action, data):
     """Update the bug."""
     bug = self.context.bug
     bug_before_modification = Snapshot(bug, providing=providedBy(bug))
     bug.markAsDuplicate(None)
     notify(
         ObjectModifiedEvent(bug, bug_before_modification, 'duplicateof'))
     return self._duplicate_action_result()
예제 #9
0
 def test_add_rule(self):
     repository = self.factory.makeGitRepository()
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         repository.addRule("refs/heads/*", repository.owner)
     self.assertDeltaDescriptionEqual(
         [], ["Added protected ref: refs/heads/*"], snapshot, repository)
예제 #10
0
 def test_no_job_created_if_no_delta(self):
     """Ensure None is returned if no change has been made."""
     merge_proposal, person = self.makeProposalWithSubscriber()
     old_merge_proposal = Snapshot(
         merge_proposal, providing=providedBy(merge_proposal))
     event = ObjectModifiedEvent(
         merge_proposal, old_merge_proposal, [], merge_proposal.registrant)
     merge_proposal_modified(merge_proposal, event)
     self.assertIs(None, self.getProposalUpdatedEmailJob(merge_proposal))
 def setUp(self):
     # Run the tests as a logged-in user.
     super(TestModificationNotification, self).setUp(
         user='******')
     self.user = getUtility(ILaunchBag).user
     self.product = self.factory.makeProduct(owner=self.user)
     self.bug = self.factory.makeBug(target=self.product)
     self.bug_task = self.bug.getBugTask(self.product)
     self.bug_task_before_modification = Snapshot(self.bug_task,
         providing=providedBy(self.bug_task))
예제 #12
0
 def test_remove_rule(self):
     repository = self.factory.makeGitRepository()
     rule = self.factory.makeGitRule(
         repository=repository, ref_pattern="refs/heads/*")
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         rule.destroySelf(repository.owner)
     self.assertDeltaDescriptionEqual(
         [], ["Removed protected ref: refs/heads/*"], snapshot, repository)
예제 #13
0
    def match(self, matchee):
        snapshot = Snapshot(matchee, providing=self.interface)
        mismatches = []
        for attribute in self.attr_list:
            if hasattr(snapshot, attribute):
                mismatches.append(WasSnapshotted(matchee, attribute))

        if len(mismatches) == 0:
            return None
        else:
            return MismatchesAll(mismatches)
예제 #14
0
 def test_no_job_created_if_work_in_progress(self):
     """Ensure None is returned if no change has been made."""
     merge_proposal, person = self.makeProposalWithSubscriber(
         needs_review=False)
     old_merge_proposal = Snapshot(
         merge_proposal, providing=providedBy(merge_proposal))
     merge_proposal.commit_message = 'new commit message'
     merge_proposal.description = 'change description'
     event = ObjectModifiedEvent(
         merge_proposal, old_merge_proposal, [], merge_proposal.registrant)
     merge_proposal_modified(merge_proposal, event)
     self.assertIs(None, self.getProposalUpdatedEmailJob(merge_proposal))
예제 #15
0
 def test_change_rule(self):
     repository = self.factory.makeGitRepository()
     rule = self.factory.makeGitRule(
         repository=repository, ref_pattern="refs/heads/foo")
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         with notify_modified(rule, ["ref_pattern"]):
             rule.ref_pattern = "refs/heads/bar"
     self.assertDeltaDescriptionEqual(
         [], ["Changed protected ref: refs/heads/foo => refs/heads/bar"],
         snapshot, repository)
예제 #16
0
 def test_job_created_if_work_in_progress_merged(self):
     # If work in progress is merged, then that is email worthy.
     merge_proposal, person = self.makeProposalWithSubscriber(
         needs_review=False)
     old_merge_proposal = Snapshot(
         merge_proposal, providing=providedBy(merge_proposal))
     merge_proposal.setStatus(BranchMergeProposalStatus.MERGED)
     event = ObjectModifiedEvent(
         merge_proposal, old_merge_proposal, [], merge_proposal.registrant)
     merge_proposal_modified(merge_proposal, event)
     job = self.getProposalUpdatedEmailJob(merge_proposal)
     self.assertIsNot(None, job, 'Job was not created.')
예제 #17
0
 def test_what_changed_works_with_fieldnames(self):
     # When what_changed is passed an ObjectModifiedEvent with a list
     # of fieldnames in its edited_fields property, it will deal with
     # those fields appropriately.
     bug = self.factory.makeBug()
     bug_before_modification = Snapshot(bug, providing=IBug)
     with person_logged_in(bug.owner):
         bug.setPrivate(True, bug.owner)
     event = ObjectModifiedEvent(bug, bug_before_modification, ['private'])
     expected_changes = {'private': ['False', 'True']}
     changes = what_changed(event)
     self.assertEqual(expected_changes, changes)
예제 #18
0
 def test_no_job_created_if_only_preview_diff_changed(self):
     """Ensure None is returned if only the preview diff has changed."""
     merge_proposal, person = self.makeProposalWithSubscriber()
     old_merge_proposal = Snapshot(
         merge_proposal, providing=providedBy(merge_proposal))
     merge_proposal.updatePreviewDiff(
         ''.join(unified_diff('', 'Fake diff')),
         unicode(self.factory.getUniqueString('revid')),
         unicode(self.factory.getUniqueString('revid')))
     event = ObjectModifiedEvent(
         merge_proposal, old_merge_proposal, [], merge_proposal.registrant)
     merge_proposal_modified(merge_proposal, event)
     self.assertIs(None, self.getProposalUpdatedEmailJob(merge_proposal))
예제 #19
0
 def test_move_rule(self):
     repository = self.factory.makeGitRepository()
     rule = self.factory.makeGitRule(
         repository=repository, ref_pattern="refs/heads/*")
     self.factory.makeGitRule(
         repository=repository, ref_pattern="refs/heads/stable/*")
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         repository.moveRule(rule, 1, repository.owner)
     self.assertDeltaDescriptionEqual(
         [], ["Moved rule for protected ref refs/heads/*: position 0 => 1"],
         snapshot, repository)
예제 #20
0
 def test_remove_grant(self):
     repository = self.factory.makeGitRepository()
     grant = self.factory.makeGitRuleGrant(
         repository=repository, ref_pattern="refs/heads/*",
         grantee=GitGranteeType.REPOSITORY_OWNER, can_push=True)
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         grant.destroySelf(repository.owner)
     self.assertDeltaDescriptionEqual(
         [],
         ["Removed access for repository owner to refs/heads/*: push"],
         snapshot, repository)
 def test_duplicate_edit_notifications(self):
     # Bug edits for a duplicate are sent to duplicate subscribers only.
     bug_before_modification = Snapshot(self.dupe_bug,
                                        providing=providedBy(self.dupe_bug))
     self.dupe_bug.description = 'A changed description'
     notify(
         ObjectModifiedEvent(self.dupe_bug,
                             bug_before_modification, ['description'],
                             user=self.dupe_bug.owner))
     latest_notification = BugNotification.selectFirst(orderBy='-id')
     recipients = set(recipient.person
                      for recipient in latest_notification.recipients)
     self.assertEqual(self.dupe_subscribers, recipients)
예제 #22
0
 def test_what_changed_works_with_field_instances(self):
     # Sometimes something will pass what_changed an
     # ObjectModifiedEvent where the edited_fields list contains
     # field instances. what_changed handles that correctly, too.
     bug = self.factory.makeBug()
     bug_before_modification = Snapshot(bug, providing=IBug)
     with person_logged_in(bug.owner):
         bug.setPrivate(True, bug.owner)
     event = ObjectModifiedEvent(bug, bug_before_modification,
                                 [IBug['private']])
     expected_changes = {'private': ['False', 'True']}
     changes = what_changed(event)
     self.assertEqual(expected_changes, changes)
예제 #23
0
    def test_modifyPOTemplate_makes_job(self):
        """Creating a Packaging should make a TranslationMergeJob."""
        potemplate = self.factory.makePOTemplate()
        finder = JobFinder(None, None, TranslationTemplateChangeJob,
                           potemplate)
        self.assertEqual([], finder.find())
        with person_logged_in(potemplate.owner):
            snapshot = Snapshot(potemplate, providing=IPOTemplate)
            potemplate.name = self.factory.getUniqueString()
            notify(ObjectModifiedEvent(potemplate, snapshot, ["name"]))

        (job, ) = finder.find()
        self.assertIsInstance(job, TranslationTemplateChangeJob)
예제 #24
0
    def expireBugTasks(self, transaction_manager):
        """Expire old, unassigned, Incomplete BugTasks.

        Only BugTasks for projects that use Malone are updated. This method
        will login as the bug_watch_updater celebrity and logout after the
        expiration is done.
        """
        message_template = ('[Expired for %s because there has been no '
                            'activity for %d days.]')
        self.log.info('Expiring unattended, INCOMPLETE bugtasks older than '
                      '%d days for projects that use Launchpad Bugs.' %
                      self.days_before_expiration)
        self._login()
        try:
            expired_count = 0
            bugtask_set = getUtility(IBugTaskSet)
            incomplete_bugtasks = bugtask_set.findExpirableBugTasks(
                self.days_before_expiration,
                user=self.janitor,
                target=self.target,
                limit=self.limit)
            self.log.info('Found %d bugtasks to expire.' %
                          incomplete_bugtasks.count())
            for bugtask in incomplete_bugtasks:
                # We don't expire bugtasks with conjoined masters.
                if bugtask.conjoined_master:
                    continue

                bugtask_before_modification = Snapshot(
                    bugtask, providing=providedBy(bugtask))
                bugtask.transitionToStatus(BugTaskStatus.EXPIRED, self.janitor)
                content = message_template % (bugtask.bugtargetdisplayname,
                                              self.days_before_expiration)
                bugtask.bug.newMessage(owner=self.janitor,
                                       subject=bugtask.bug.followup_subject(),
                                       content=content)
                notify(
                    ObjectModifiedEvent(bugtask,
                                        bugtask_before_modification,
                                        ['status'],
                                        user=self.janitor))
                # We commit after each expiration because emails are sent
                # immediately in zopeless. This minimize the risk of
                # duplicate expiration emails being sent in case an error
                # occurs later on.
                transaction_manager.commit()
                expired_count += 1
            self.log.info('Expired %d bugtasks.' % expired_count)
        finally:
            self._logout()
        self.log.info('Finished expiration run.')
 def test_notifications_for_question_subscribers(self):
     # Ensure that notifications are sent to subscribers of a
     # question linked to the expired bug.
     bugtask = self.bug.default_bugtask
     bugtask_before_modification = Snapshot(bugtask, providing=IBugTask)
     bugtask.transitionToStatus(BugTaskStatus.EXPIRED, self.product.owner)
     bug_modified = ObjectModifiedEvent(bugtask,
                                        bugtask_before_modification,
                                        ["status"])
     notify(bug_modified)
     recipients = [
         job.metadata['recipient_set'] for job in pop_questionemailjobs()
     ]
     self.assertContentEqual(['ASKER_SUBSCRIBER'], recipients)
예제 #26
0
 def change_action(self, action, data):
     """Update the bug."""
     data = dict(data)
     # We handle duplicate changes by hand instead of leaving it to
     # the usual machinery because we must use bug.markAsDuplicate().
     bug = self.context.bug
     bug_before_modification = Snapshot(bug, providing=providedBy(bug))
     duplicateof = data.pop('duplicateof')
     bug.markAsDuplicate(duplicateof)
     notify(
         ObjectModifiedEvent(bug, bug_before_modification, 'duplicateof'))
     # Apply other changes.
     self.updateBugFromData(data)
     return self._duplicate_action_result()
예제 #27
0
 def makeProposalUpdatedEmailJob(self):
     """Fixture method providing a mailer for a modified merge proposal"""
     merge_proposal, subscriber = self.makeProposalWithSubscriber()
     old_merge_proposal = Snapshot(
         merge_proposal, providing=providedBy(merge_proposal))
     merge_proposal.requestReview()
     merge_proposal.commit_message = 'new commit message'
     merge_proposal.description = 'change description'
     event = ObjectModifiedEvent(
         merge_proposal, old_merge_proposal, [], merge_proposal.registrant)
     merge_proposal_modified(merge_proposal, event)
     job = self.getProposalUpdatedEmailJob(merge_proposal)
     self.assertIsNot(None, job, 'Job was not created.')
     return job, subscriber
예제 #28
0
    def execute(self, context, current_event):
        """See `IEmailCommand`.

        Much of this method was lifted from
        `EditEmailCommand.execute`.
        """
        # Parse args.
        self._ensureNumberOfArguments()
        [security_flag] = self.string_args
        if security_flag == 'yes':
            security_related = True
        elif security_flag == 'no':
            security_related = False
        else:
            raise EmailProcessingError(get_error_message(
                'security-parameter-mismatch.txt',
                error_templates=error_templates),
                                       stop_processing=True)

        if isinstance(context, CreateBugParams):
            if security_related:
                context.information_type = InformationType.PRIVATESECURITY
            return context, current_event

        # Take a snapshot.
        edited = False
        edited_fields = set()
        if IObjectModifiedEvent.providedBy(current_event):
            context_snapshot = current_event.object_before_modification
            edited_fields.update(current_event.edited_fields)
        else:
            context_snapshot = Snapshot(context, providing=providedBy(context))

        # Apply requested changes.
        user = getUtility(ILaunchBag).user
        if security_related:
            if context.setPrivate(True, user):
                edited = True
                edited_fields.add('private')
        if context.security_related != security_related:
            context.setSecurityRelated(security_related, user)
            edited = True
            edited_fields.add('security_related')

        # Update the current event.
        if edited and not IObjectCreatedEvent.providedBy(current_event):
            current_event = ObjectModifiedEvent(context, context_snapshot,
                                                list(edited_fields))

        return context, current_event
예제 #29
0
 def test_change_basic_properties(self):
     repository = self.factory.makeGitRepository(name="foo")
     transaction.commit()
     snapshot = Snapshot(repository, providing=providedBy(repository))
     with person_logged_in(repository.owner):
         repository.setName("bar", repository.owner)
     expected = [
         "Name: foo => bar",
         "Git identity: lp:~{person}/{project}/+git/foo => "
         "lp:~{person}/{project}/+git/bar".format(
             person=repository.owner.name, project=repository.target.name),
         ]
     self.assertDeltaDescriptionEqual(
         expected, expected, snapshot, repository)
예제 #30
0
    def change_action(self, action, data):
        """Update the bug."""
        data = dict(data)
        bug = self.context.bug
        information_type = data.pop('information_type')
        changed_fields = ['information_type']
        # When the user first submits the form, validate change is True and
        # so we check that the bug does not become invisible. If the user
        # confirms they really want to make the change, validate change is
        # False and we process the change as normal.
        if self.request.is_ajax:
            validate_change = data.get('validate_change', False)
            if (validate_change and
                information_type in PRIVATE_INFORMATION_TYPES and
                self._bug_will_be_invisible(information_type)):
                self.request.response.setStatus(400, "Bug Visibility")
                return ''

        user_will_be_subscribed = (
            information_type in PRIVATE_INFORMATION_TYPES and
            bug.getSubscribersForPerson(self.user).is_empty())
        bug_before_modification = Snapshot(bug, providing=providedBy(bug))
        changed = bug.transitionToInformationType(
            information_type, self.user)
        if changed:
            self._handlePrivacyChanged(user_will_be_subscribed)
            notify(
                ObjectModifiedEvent(
                    bug, bug_before_modification, changed_fields,
                    user=self.user))
        if self.request.is_ajax:
            # Avoid circular imports
            from lp.bugs.browser.bugtask import (
                can_add_package_task_to_bug,
                can_add_project_task_to_bug,
            )
            if changed:
                result_data = self._getSubscriptionDetails()
                result_data['can_add_project_task'] = (
                    can_add_project_task_to_bug(bug))
                result_data['can_add_package_task'] = (
                    can_add_package_task_to_bug(bug))
                self.request.response.setHeader(
                    'content-type', 'application/json')
                return dumps(
                    result_data, cls=ResourceJSONEncoder,
                    media_type=EntryResource.JSON_TYPE)
            else:
                return ''