Example #1
0
    def sendGPGValidationRequest(self, key):
        """See ILoginToken."""
        separator = '\n    '
        formatted_uids = '    ' + separator.join(key.emails)

        assert self.tokentype in (LoginTokenType.VALIDATEGPG,
                                  LoginTokenType.VALIDATESIGNONLYGPG)

        # Craft the confirmation message that will be sent to the user.  There
        # are two chunks of text that will be concatenated together into a
        # single text/plain part.  The first chunk will be the clear text
        # instructions providing some extra help for those people who cannot
        # read the encrypted chunk that follows.  The encrypted chunk will
        # have the actual confirmation token in it, however the ability to
        # read this is highly dependent on the mail reader being used, and how
        # that MUA is configured.

        # Here are the instructions that need to be encrypted.
        template = get_email_template('validate-gpg.txt', app=MAIL_APP)
        key_type = '%s%s' % (key.keysize, key.algorithm.title)
        replacements = {
            'requester': self.requester.displayname,
            'requesteremail': self.requesteremail,
            'key_type': key_type,
            'fingerprint': key.fingerprint,
            'uids': formatted_uids,
            'token_url': canonical_url(self)
        }

        token_text = template % replacements
        salutation = 'Hello,\n\n'
        instructions = ''
        closing = "Thanks,\n\nThe Launchpad Team"

        # Encrypt this part's content if requested.
        if key.can_encrypt:
            gpghandler = getUtility(IGPGHandler)
            token_text = gpghandler.encryptContent(token_text.encode('utf-8'),
                                                   key)
            # In this case, we need to include some clear text instructions
            # for people who do not have an MUA that can decrypt the ASCII
            # armored text.
            instructions = get_email_template('gpg-cleartext-instructions.txt',
                                              app=MAIL_APP)

        # Concatenate the message parts and send it.
        text = salutation + instructions + token_text + closing
        from_name = 'Launchpad OpenPGP Key Confirmation'
        subject = 'Launchpad: Confirm your OpenPGP Key'
        self._send_email(from_name, subject, text)
Example #2
0
 def send(self):
     """Send a message to the user about the product's licence."""
     if not self.needs_notification(self.product):
         # The project has a common licence.
         return False
     maintainer = self.product.owner
     if maintainer.is_team:
         user_address = maintainer.getTeamAdminsEmailAddresses()
     else:
         user_address = format_address_for_person(maintainer)
     from_address = format_address("Launchpad",
                                   config.canonical.noreply_from_address)
     commercial_address = format_address('Commercial',
                                         '*****@*****.**')
     substitutions = dict(
         user_displayname=maintainer.displayname,
         user_name=maintainer.name,
         product_name=self.product.name,
         product_url=canonical_url(self.product),
         commercial_use_expiration=self.getCommercialUseMessage(),
     )
     # Email the user about licence policy.
     subject = ("Licence information for %(product_name)s "
                "in Launchpad" % substitutions)
     template = get_email_template(self.getTemplateName(), app='registry')
     message = template % substitutions
     simple_sendmail(from_address,
                     user_address,
                     subject,
                     message,
                     headers={'Reply-To': commercial_address})
     # Inform that Launchpad recognized the licence change.
     self._addLicenseChangeToReviewWhiteboard()
     return True
    def _sendFailureNotification(self, notify_owner, log):
        """Send a failure notification to the distribution's mirror admins and
        to the mirror owner, in case notify_owner is True.
        """
        template = get_email_template(
            'notify-mirror-owner.txt', app='registry')
        fromaddress = format_address(
            "Launchpad Mirror Prober", config.canonical.noreply_from_address)

        replacements = {
            'distro': self.distribution.title,
            'mirror_name': self.name,
            'mirror_url': canonical_url(self),
            'log_snippet': "\n".join(log.split('\n')[:20]),
            'logfile_url': self.last_probe_record.log_file.http_url}
        message = template % replacements
        subject = "Launchpad: Verification of %s failed" % self.name

        mirror_admin_address = get_contact_email_addresses(
            self.distribution.mirror_admin)
        simple_sendmail(fromaddress, mirror_admin_address, subject, message)

        if notify_owner:
            owner_address = get_contact_email_addresses(self.owner)
            if len(owner_address) > 0:
                simple_sendmail(fromaddress, owner_address, subject, message)
 def _getFailureEmailBody(self):
     """Send an email notification about the export failing."""
     template = get_email_template('poexport-failure.txt', 'translations')
     return template % {
         'person': self.person.displayname,
         'request_url': self.request_url,
     }
Example #5
0
def notify_message_held(message_approval, event):
    """Send a notification of a message hold to all team administrators."""
    message_details = getAdapter(message_approval, IHeldMessageDetails)
    team = message_approval.mailing_list.team
    from_address = format_address(team.displayname,
                                  config.canonical.noreply_from_address)
    subject = ('New mailing list message requiring approval for %s' %
               team.displayname)
    template = get_email_template('new-held-message.txt', app='registry')

    # Most of the replacements are the same for everyone.
    replacements = {
        'subject': message_details.subject,
        'author_name': message_details.author.displayname,
        'author_url': canonical_url(message_details.author),
        'date': message_details.date,
        'message_id': message_details.message_id,
        'review_url': '%s/+mailinglist-moderate' % canonical_url(team),
        'team': team.displayname,
    }

    # Don't wrap the paragraph with the url.
    def wrap_function(paragraph):
        return (paragraph.startswith('http:')
                or paragraph.startswith('https:'))

    # Send one message to every team administrator.
    person_set = getUtility(IPersonSet)
    for address in team.getTeamAdminsEmailAddresses():
        user = person_set.getByEmail(address)
        replacements['user'] = user.displayname
        body = MailWrapper(72).format(template % replacements,
                                      force_wrap=True,
                                      wrap_func=wrap_function)
        simple_sendmail(from_address, address, subject, body)
Example #6
0
    def _sendFailureNotification(self, notify_owner, log):
        """Send a failure notification to the distribution's mirror admins and
        to the mirror owner, in case notify_owner is True.
        """
        template = get_email_template('notify-mirror-owner.txt',
                                      app='registry')
        fromaddress = format_address("Launchpad Mirror Prober",
                                     config.canonical.noreply_from_address)

        replacements = {
            'distro': self.distribution.title,
            'mirror_name': self.name,
            'mirror_url': canonical_url(self),
            'log_snippet': "\n".join(log.split('\n')[:20]),
            'logfile_url': self.last_probe_record.log_file.http_url
        }
        message = template % replacements
        subject = "Launchpad: Verification of %s failed" % self.name

        mirror_admin_address = get_contact_email_addresses(
            self.distribution.mirror_admin)
        simple_sendmail(fromaddress, mirror_admin_address, subject, message)

        if notify_owner:
            owner_address = get_contact_email_addresses(self.owner)
            if len(owner_address) > 0:
                simple_sendmail(fromaddress, owner_address, subject, message)
Example #7
0
 def _getTemplateParams(self, email, recipient):
     """See `BaseMailer`."""
     params = super(TeamMembershipMailer,
                    self)._getTemplateParams(email, recipient)
     params["recipient"] = recipient.displayname
     reason, _ = self._recipients.getReason(email)
     if reason.recipient_class is not None:
         params["recipient_class"] = reason.recipient_class
     params["member"] = self.member.unique_displayname
     params["membership_invitations_url"] = "%s/+invitation/%s" % (
         canonical_url(self.member), self.team.name)
     params["team"] = self.team.unique_displayname
     params["team_url"] = canonical_url(self.team)
     if self.membership is not None:
         params["membership_url"] = canonical_url(self.membership)
     if reason.recipient_class == "bulk" and self.reviewer == self.member:
         params["reviewer"] = "the user"
     elif self.reviewer is not None:
         params["reviewer"] = self.reviewer.unique_displayname
     if self.team.mailing_list is not None:
         template = get_email_template("team-list-subscribe-block.txt",
                                       app="registry")
         editemails_url = urlappend(
             canonical_url(getUtility(ILaunchpadRoot)),
             "~/+editmailinglists")
         list_instructions = template % {"editemails_url": editemails_url}
     else:
         list_instructions = ""
     params["list_instructions"] = list_instructions
     params.update(self.extra_params)
     return params
 def send(self):
     """Send a message to the user about the product's licence."""
     if not self.needs_notification(self.product):
         # The project has a common licence.
         return False
     maintainer = self.product.owner
     if maintainer.is_team:
         user_address = maintainer.getTeamAdminsEmailAddresses()
     else:
         user_address = format_address_for_person(maintainer)
     from_address = format_address(
         "Launchpad", config.canonical.noreply_from_address)
     commercial_address = format_address(
         'Commercial', '*****@*****.**')
     substitutions = dict(
         user_displayname=maintainer.displayname,
         user_name=maintainer.name,
         product_name=self.product.name,
         product_url=canonical_url(self.product),
         commercial_use_expiration=self.getCommercialUseMessage(),
         )
     # Email the user about licence policy.
     subject = (
         "Licence information for %(product_name)s "
         "in Launchpad" % substitutions)
     template = get_email_template(
         self.getTemplateName(), app='registry')
     message = template % substitutions
     simple_sendmail(
         from_address, user_address,
         subject, message, headers={'Reply-To': commercial_address})
     # Inform that Launchpad recognized the licence change.
     self._addLicenseChangeToReviewWhiteboard()
     return True
def notify_message_held(message_approval, event):
    """Send a notification of a message hold to all team administrators."""
    message_details = getAdapter(message_approval, IHeldMessageDetails)
    team = message_approval.mailing_list.team
    from_address = format_address(
        team.displayname, config.canonical.noreply_from_address)
    subject = (
        'New mailing list message requiring approval for %s'
        % team.displayname)
    template = get_email_template('new-held-message.txt', app='registry')

    # Most of the replacements are the same for everyone.
    replacements = {
        'subject': message_details.subject,
        'author_name': message_details.author.displayname,
        'author_url': canonical_url(message_details.author),
        'date': message_details.date,
        'message_id': message_details.message_id,
        'review_url': '%s/+mailinglist-moderate' % canonical_url(team),
        'team': team.displayname,
        }

    # Don't wrap the paragraph with the url.
    def wrap_function(paragraph):
        return (paragraph.startswith('http:') or
                paragraph.startswith('https:'))

    # Send one message to every team administrator.
    person_set = getUtility(IPersonSet)
    for address in team.getTeamAdminsEmailAddresses():
        user = person_set.getByEmail(address)
        replacements['user'] = user.displayname
        body = MailWrapper(72).format(
            template % replacements, force_wrap=True, wrap_func=wrap_function)
        simple_sendmail(from_address, address, subject, body)
Example #10
0
    def sendGPGValidationRequest(self, key):
        """See ILoginToken."""
        separator = '\n    '
        formatted_uids = '    ' + separator.join(key.emails)

        assert self.tokentype in (LoginTokenType.VALIDATEGPG,
                                  LoginTokenType.VALIDATESIGNONLYGPG)

        # Craft the confirmation message that will be sent to the user.  There
        # are two chunks of text that will be concatenated together into a
        # single text/plain part.  The first chunk will be the clear text
        # instructions providing some extra help for those people who cannot
        # read the encrypted chunk that follows.  The encrypted chunk will
        # have the actual confirmation token in it, however the ability to
        # read this is highly dependent on the mail reader being used, and how
        # that MUA is configured.

        # Here are the instructions that need to be encrypted.
        template = get_email_template('validate-gpg.txt', app=MAIL_APP)
        replacements = {'requester': self.requester.displayname,
                        'requesteremail': self.requesteremail,
                        'displayname': key.displayname,
                        'fingerprint': key.fingerprint,
                        'uids': formatted_uids,
                        'token_url': canonical_url(self)}

        token_text = template % replacements
        salutation = 'Hello,\n\n'
        instructions = ''
        closing = "Thanks,\n\nThe Launchpad Team"

        # Encrypt this part's content if requested.
        if key.can_encrypt:
            gpghandler = getUtility(IGPGHandler)
            token_text = gpghandler.encryptContent(token_text.encode('utf-8'),
                                                   key.fingerprint)
            # In this case, we need to include some clear text instructions
            # for people who do not have an MUA that can decrypt the ASCII
            # armored text.
            instructions = get_email_template(
                'gpg-cleartext-instructions.txt', app=MAIL_APP)

        # Concatenate the message parts and send it.
        text = salutation + instructions + token_text + closing
        from_name = 'Launchpad OpenPGP Key Confirmation'
        subject = 'Launchpad: Confirm your OpenPGP Key'
        self._send_email(from_name, subject, text)
Example #11
0
 def unsupported_language_warning(self):
     """Warning about the fact that the question is written in an
     unsupported language."""
     template = get_email_template(
         'question-unsupported-language-warning.txt', app='answers')
     return template % {
         'question_language': self.question.language.englishname,
         'target_name': self.question.target.displayname}
 def _getFailureEmailBody(self):
     """Send an email notification about the export failing."""
     template = get_email_template(
         'poexport-failure.txt', 'translations')
     return template % {
         'person': self.person.displayname,
         'request_url': self.request_url,
         }
 def _getSuccessEmailBody(self):
     """Send an email notification about the export working."""
     template = get_email_template('poexport-success.txt', 'translations')
     return template % {
         'person': self.person.displayname,
         'download_url': self.url,
         'request_url': self.request_url,
     }
 def test_signature_separator(self):
     # Email signatures are often separated from the body of a message by a
     # special separator so user agents can identify the signature for
     # special treatment (hiding, stripping when replying, colorizing,
     # etc.).  The bug notification messages follow the convention.
     names = ['bug-notification-verbose.txt', 'bug-notification.txt']
     for name in names:
         template = get_email_template(name, 'bugs')
         self.assertTrue(re.search('^-- $', template, re.MULTILINE))
Example #15
0
 def _getBody(self, email, recipient):
     """Return the complete body to use for this email."""
     template = get_email_template(self._template_name, app=self.app)
     params = self._getTemplateParams(email, recipient)
     body = template % params
     footer = self._getFooter(params)
     if footer is not None:
         body = append_footer(body, footer)
     return body
 def _getSuccessEmailBody(self):
     """Send an email notification about the export working."""
     template = get_email_template(
         'poexport-success.txt', 'translations')
     return template % {
         'person': self.person.displayname,
         'download_url': self.url,
         'request_url': self.request_url,
         }
Example #17
0
def notify_new_ppa_subscription(subscription, event):
    """Notification that a new PPA subscription can be activated."""
    non_active_subscribers = subscription.getNonActiveSubscribers()

    archive = subscription.archive

    # We don't send notification emails for some PPAs, particularly those that
    # are purchased via the Software Centre, so that its users do not have to
    # learn about Launchpad.
    if archive.suppress_subscription_notifications:
        return

    registrant_name = subscription.registrant.displayname
    ppa_displayname = archive.displayname
    ppa_reference = "ppa:%s/%s" % (
        archive.owner.name, archive.name)
    ppa_description = archive.description
    subject = 'PPA access granted for ' + ppa_displayname

    template = get_email_template('ppa-subscription-new.txt', app='soyuz')

    for person, preferred_email in non_active_subscribers:
        to_address = [preferred_email.email]
        root = getUtility(ILaunchpadRoot)
        recipient_subscriptions_url = "%s~/+archivesubscriptions" % (
            canonical_url(root))
        description_blurb = '.'
        if ppa_description is not None and ppa_description != '':
            description_blurb = (
                ' and has the following description:\n\n%s' % ppa_description)
        replacements = {
            'recipient_name': person.displayname,
            'registrant_name': registrant_name,
            'registrant_profile_url': canonical_url(subscription.registrant),
            'ppa_displayname': ppa_displayname,
            'ppa_reference': ppa_reference,
            'ppa_description_blurb': description_blurb,
            'recipient_subscriptions_url': recipient_subscriptions_url,
            }
        body = MailWrapper(72).format(template % replacements,
                                      force_wrap=True)

        from_address = format_address(
            registrant_name, config.canonical.noreply_from_address)

        headers = {
            'Sender': config.canonical.bounce_address,
            }

        # If the registrant has a preferred email, then use it for the
        # Reply-To.
        if subscription.registrant.preferredemail:
            headers['Reply-To'] = format_address(
                registrant_name,
                subscription.registrant.preferredemail.email)

        simple_sendmail(from_address, to_address, subject, body, headers)
Example #18
0
 def sendEmailToMaintainer(self, template_name, subject, from_address):
     """See `IProductNotificationJob`."""
     email_template = get_email_template(
         "%s.txt" % template_name, app='registry')
     for address in self.recipients.getEmails():
         body, headers = self.getBodyAndHeaders(
             email_template, address, self.reply_to)
         simple_sendmail(from_address, address, subject, body, headers)
     log.debug("%s has sent email to the maintainer of %s.",
         self.log_name, self.product.name)
    def _formatRemoteComment(self, message):
        """Format a comment for a remote bugtracker and return it."""
        comment_template = get_email_template(
            self.external_bugtracker.comment_template, 'bugs')

        return comment_template % {
            'launchpad_bug': self.bug_watch.bug.id,
            'comment_author': message.owner.displayname,
            'comment_body': message.text_contents,
        }
Example #20
0
 def sendEmailToMaintainer(self, template_name, subject, from_address):
     """See `IProductNotificationJob`."""
     email_template = get_email_template(
         "%s.txt" % template_name, app='registry')
     for address in self.recipients.getEmails():
         body, headers = self.getBodyAndHeaders(
             email_template, address, self.reply_to)
         simple_sendmail(from_address, address, subject, body, headers)
     log.debug("%s has sent email to the maintainer of %s.",
         self.log_name, self.product.name)
    def _formatRemoteComment(self, message):
        """Format a comment for a remote bugtracker and return it."""
        comment_template = get_email_template(
            self.external_bugtracker.comment_template, 'bugs')

        return comment_template % {
            'launchpad_bug': self.bug_watch.bug.id,
            'comment_author': message.owner.displayname,
            'comment_body': message.text_contents,
            }
Example #22
0
 def sendHelpEmail(self, to_address):
     """Send usage help to `to_address`."""
     # Get the help text (formatted as MoinMoin markup)
     help_text = get_email_template('help.txt', app='bugs')
     help_text = reformat_wiki_text(help_text)
     # Wrap text
     mailwrapper = MailWrapper(width=72)
     help_text = mailwrapper.format(help_text)
     simple_sendmail('*****@*****.**', to_address,
                     'Launchpad Bug Tracker Email Interface Help',
                     help_text)
 def _getUnicodeDecodeErrorEmailBody(self):
     """Send an email notification to admins about UnicodeDecodeError."""
     template = get_email_template(
         'poexport-failure-unicodedecodeerror.txt', 'translations')
     failed_requests = self._getFailedRequestsDescription()
     return template % {
         'person': self.person.displayname,
         'person_id': self.person.name,
         'request_url': self.request_url,
         'failed_requests': failed_requests,
     }
Example #24
0
 def getBody(self):
     """See QuestionNotification."""
     question = self.question
     template = get_email_template(
         'question-unsupported-languages-added.txt', app='answers')
     return template % {
         'target_name': question.target.displayname,
         'question_id': question.id,
         'question_url': canonical_url(question),
         'question_language': question.language.englishname,
         'comment': question.description}
Example #25
0
 def sendAdvertisementEmail(self, subject, content):
     """See ISignedCodeOfConduct."""
     assert self.owner.preferredemail
     template = get_email_template('signedcoc-acknowledge.txt',
                                   app='registry')
     fromaddress = format_address("Launchpad Code Of Conduct System",
                                  config.canonical.noreply_from_address)
     replacements = {'user': self.owner.displayname, 'content': content}
     message = template % replacements
     simple_sendmail(fromaddress, str(self.owner.preferredemail.email),
                     subject, message)
 def _getAdminFailureNotificationEmailBody(self):
     """Send an email notification about failed export to admins."""
     template = get_email_template(
         'poexport-failure-admin-notification.txt', 'translations')
     failed_requests = self._getFailedRequestsDescription()
     return template % {
         'person': self.person.displayname,
         'person_id': self.person.name,
         'request_url': self.request_url,
         'failure_message': self.failure,
         'failed_requests': failed_requests,
     }
Example #27
0
 def sendHelpEmail(self, to_address):
     """Send usage help to `to_address`."""
     # Get the help text (formatted as MoinMoin markup)
     help_text = get_email_template('help.txt', app='bugs')
     help_text = reformat_wiki_text(help_text)
     # Wrap text
     mailwrapper = MailWrapper(width=72)
     help_text = mailwrapper.format(help_text)
     simple_sendmail(
         '*****@*****.**', to_address,
         'Launchpad Bug Tracker Email Interface Help',
         help_text)
 def _getAdminFailureNotificationEmailBody(self):
     """Send an email notification about failed export to admins."""
     template = get_email_template(
         'poexport-failure-admin-notification.txt', 'translations')
     failed_requests = self._getFailedRequestsDescription()
     return template % {
         'person': self.person.displayname,
         'person_id': self.person.name,
         'request_url': self.request_url,
         'failure_message': self.failure,
         'failed_requests': failed_requests,
         }
 def _getUnicodeDecodeErrorEmailBody(self):
     """Send an email notification to admins about UnicodeDecodeError."""
     template = get_email_template(
         'poexport-failure-unicodedecodeerror.txt',
         'translations')
     failed_requests = self._getFailedRequestsDescription()
     return template % {
         'person': self.person.displayname,
         'person_id': self.person.name,
         'request_url': self.request_url,
         'failed_requests': failed_requests,
         }
def send_process_error_notification(to_address,
                                    subject,
                                    error_msg,
                                    original_msg,
                                    failing_command=None,
                                    max_return_size=MAX_RETURN_MESSAGE_SIZE):
    """Send a mail about an error occurring while using the email interface.

    Tells the user that an error was encountered while processing his
    request and attaches the original email which caused the error to
    happen.  The original message will be truncated to
    max_return_size bytes.

        :to_address: The address to send the notification to.
        :subject: The subject of the notification.
        :error_msg: The error message that explains the error.
        :original_msg: The original message sent by the user.
        :failing_command: The command that caused the error to happen.
        :max_return_size: The maximum size returned for the original message.
    """
    if isinstance(failing_command, list):
        failing_commands = failing_command
    elif failing_command is None:
        failing_commands = []
    else:
        failing_commands = [failing_command]
    failed_commands_information = ''
    if len(failing_commands) > 0:
        failed_commands_information = 'Failing command:'
        for failing_command in failing_commands:
            failed_commands_information += '\n    %s' % str(failing_command)

    body = get_email_template(
        'email-processing-error.txt', app='services/mail') % {
            'failed_command_information': failed_commands_information,
            'error_msg': error_msg
        }
    mailwrapper = MailWrapper(width=72)
    body = mailwrapper.format(body)
    error_part = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')

    msg = MIMEMultipart()
    msg['To'] = to_address
    msg['From'] = get_bugmail_error_address()
    msg['Subject'] = subject
    msg.attach(error_part)
    original_msg_str = str(original_msg)
    if len(original_msg_str) > max_return_size:
        truncated_msg_str = original_msg_str[:max_return_size]
        original_msg = message_from_string(truncated_msg_str)
    msg.attach(MIMEMessage(original_msg))
    sendmail(msg)
def get_template(archive, action):
    """Return the appropriate e-mail template."""
    template_name = 'upload-'
    if action in ('new', 'accepted', 'announcement'):
        template_name += action
    elif action == 'unapproved':
        template_name += 'accepted'
    elif action == 'rejected':
        template_name += 'rejection'
    if archive.is_ppa:
        template_name = 'ppa-%s' % template_name
    template_name += '.txt'
    return get_email_template(template_name, app='soyuz')
Example #32
0
    def sendProfileCreatedEmail(self, profile, comment):
        """See ILoginToken."""
        template = get_email_template('profile-created.txt', app=MAIL_APP)
        replacements = {'token_url': canonical_url(self),
                        'requester': self.requester.displayname,
                        'comment': comment,
                        'profile_url': canonical_url(profile)}
        message = template % replacements

        headers = {'Reply-To': self.requester.preferredemail.email}
        from_name = "Launchpad"
        subject = "Launchpad profile"
        self._send_email(from_name, subject, message, headers=headers)
Example #33
0
 def _getBody(self, email, recipient):
     """Return the complete body to use for this email."""
     template = get_email_template(self._getTemplateName(email, recipient),
                                   app=self.app)
     params = self._getTemplateParams(email, recipient)
     body = template % params
     if self._wrap:
         body = MailWrapper().format(body,
                                     force_wrap=self._force_wrap) + "\n"
     footer = self._getFooter(email, recipient, params)
     if footer is not None:
         body = append_footer(body, footer)
     return body
def get_template(archive, action):
    """Return the appropriate e-mail template."""
    template_name = 'upload-'
    if action in ('new', 'accepted', 'announcement'):
        template_name += action
    elif action == 'unapproved':
        template_name += 'accepted'
    elif action == 'rejected':
        template_name += 'rejection'
    if archive.is_ppa:
        template_name = 'ppa-%s' % template_name
    template_name += '.txt'
    return get_email_template(template_name, app='soyuz')
Example #35
0
    def sendClaimProfileEmail(self):
        """See ILoginToken."""
        template = get_email_template('claim-profile.txt', app=MAIL_APP)
        from_name = "Launchpad"
        profile = getUtility(IPersonSet).getByEmail(self.email)
        replacements = {'profile_name': (
                            "%s (%s)" % (profile.displayname, profile.name)),
                        'email': self.email,
                        'token_url': canonical_url(self)}
        message = template % replacements

        subject = "Launchpad: Claim Profile"
        self._send_email(from_name, subject, message)
Example #36
0
 def getBody(self):
     """See QuestionNotification."""
     question = self.question
     template = get_email_template(
         'question-added-notification.txt', app='answers')
     body = template % {
         'target_name': question.target.displayname,
         'question_id': question.id,
         'question_url': canonical_url(question),
         'comment': question.description}
     if self.unsupported_language:
         body += self.unsupported_language_warning
     return body
Example #37
0
 def sendEmailValidationRequest(self):
     """See ILoginToken."""
     template = get_email_template('validate-email.txt', app=MAIL_APP)
     replacements = {'token_url': canonical_url(self),
                     'requester': self.requester.displayname,
                     'requesteremail': self.requesteremail,
                     'toaddress': self.email}
     message = template % replacements
     subject = "Launchpad: Validate your email address"
     self._send_email("Launchpad Email Validator", subject, message)
     self.requester.security_field_changed(
         "A new email address is being added to your Launchpad account.",
         "<%s> will be activated for your account when you follow the "
         "instructions that were sent to <%s>." % (self.email, self.email))
Example #38
0
    def sendTeamEmailAddressValidationEmail(self, user):
        """See ILoginToken."""
        template = get_email_template('validate-teamemail.txt', app=MAIL_APP)

        from_name = "Launchpad Email Validator"
        subject = "Launchpad: Validate your team's contact email address"
        replacements = {'team': self.requester.displayname,
                        'requester': '%s (%s)' % (
                            user.displayname, user.name),
                        'toaddress': self.email,
                        'admin_email': config.canonical.admin_address,
                        'token_url': canonical_url(self)}
        message = template % replacements
        self._send_email(from_name, subject, message)
Example #39
0
 def getBody(self):
     """See QuestionNotification."""
     template = get_email_template(
         'question-linked-bug-status-updated.txt', app='coop/answersbugs')
     return template % {
         'bugtask_target_name': self.bugtask.target.displayname,
         'question_id': self.question.id,
         'question_title': self.question.title,
         'question_url': canonical_url(self.question),
         'bugtask_url': canonical_url(self.bugtask),
         'bug_id': self.bugtask.bug.id,
         'bugtask_title': self.bugtask.bug.title,
         'old_status': self.old_bugtask.status.title,
         'new_status': self.bugtask.status.title}
Example #40
0
def send_process_error_notification(
    to_address, subject, error_msg, original_msg, failing_command=None, max_return_size=MAX_RETURN_MESSAGE_SIZE
):
    """Send a mail about an error occurring while using the email interface.

    Tells the user that an error was encountered while processing his
    request and attaches the original email which caused the error to
    happen.  The original message will be truncated to
    max_return_size bytes.

        :to_address: The address to send the notification to.
        :subject: The subject of the notification.
        :error_msg: The error message that explains the error.
        :original_msg: The original message sent by the user.
        :failing_command: The command that caused the error to happen.
        :max_return_size: The maximum size returned for the original message.
    """
    if isinstance(failing_command, list):
        failing_commands = failing_command
    elif failing_command is None:
        failing_commands = []
    else:
        failing_commands = [failing_command]
    failed_commands_information = ""
    if len(failing_commands) > 0:
        failed_commands_information = "Failing command:"
        for failing_command in failing_commands:
            failed_commands_information += "\n    %s" % str(failing_command)

    body = get_email_template("email-processing-error.txt", app="services/mail") % {
        "failed_command_information": failed_commands_information,
        "error_msg": error_msg,
    }
    mailwrapper = MailWrapper(width=72)
    body = mailwrapper.format(body)
    error_part = MIMEText(body.encode("utf-8"), "plain", "utf-8")

    msg = MIMEMultipart()
    msg["To"] = to_address
    msg["From"] = get_bugmail_error_address()
    msg["Subject"] = subject
    msg.attach(error_part)
    original_msg_str = str(original_msg)
    if len(original_msg_str) > max_return_size:
        truncated_msg_str = original_msg_str[:max_return_size]
        original_msg = message_from_string(truncated_msg_str)
    msg.attach(MIMEMessage(original_msg))
    sendmail(msg)
Example #41
0
    def sendMergeRequestEmail(self):
        """See ILoginToken."""
        template = get_email_template('request-merge.txt', app=MAIL_APP)
        from_name = "Launchpad Account Merge"

        dupe = getUtility(IPersonSet).getByEmail(
            self.email, filter_status=False)
        replacements = {'dupename': "%s (%s)" % (dupe.displayname, dupe.name),
                        'requester': self.requester.name,
                        'requesteremail': self.requesteremail,
                        'toaddress': self.email,
                        'token_url': canonical_url(self)}
        message = template % replacements

        subject = "Launchpad: Merge of Accounts Requested"
        self._send_email(from_name, subject, message)
Example #42
0
 def sendClaimTeamEmail(self):
     """See `ILoginToken`."""
     template = get_email_template('claim-team.txt', app=MAIL_APP)
     from_name = "Launchpad"
     profile = getUtility(IPersonSet).getByEmail(
                                         self.email,
                                         filter_status=False)
     replacements = {'profile_name': (
                         "%s (%s)" % (profile.displayname, profile.name)),
                     'requester_name': (
                         "%s (%s)" % (self.requester.displayname,
                                      self.requester.name)),
                     'email': self.email,
                     'token_url': canonical_url(self)}
     message = template % replacements
     subject = "Launchpad: Claim existing team"
     self._send_email(from_name, subject, message)
Example #43
0
    def getBody(self):
        """See QuestionNotification."""
        body = self.metadata_changes_text
        replacements = dict(
            question_id=self.question.id,
            target_name=self.question.target.displayname,
            question_url=canonical_url(self.question))

        if self.new_message:
            if body:
                body += '\n\n'
            body += self.getNewMessageText()
            replacements['new_message_id'] = list(
                self.question.messages).index(self.new_message)

        replacements['body'] = body

        template = get_email_template(self.body_template, app='answers')
        return template % replacements
    def _handleUnpushedBranch(self, productseries):
        """Branch has never been scanned.  Notify owner.

        This means that as far as the Launchpad database knows, there is
        no actual bzr branch behind this `IBranch` yet.
        """
        branch = productseries.translations_branch
        self.logger.info("Notifying %s of unpushed branch %s." % (
            branch.owner.name, branch.bzr_identity))

        template = get_email_template('unpushed-branch.txt', 'translations')
        text = template % {
            'productseries': productseries.title,
            'branch_url': branch.bzr_identity,
        }
        recipients = get_contact_email_addresses(branch.owner)
        sender = format_address(
            "Launchpad Translations", config.canonical.noreply_from_address)
        subject = "Launchpad: translations branch has not been set up."
        self._sendMail(sender, recipients, subject, text)
def notify_mailinglist_activated(mailinglist, event):
    """Notification that a mailing list is available.

    All active members of a team and its subteams receive notification when
    the team's mailing list is available.
    """
    # We will use the setting of the date_activated field as a hint
    # that this list is new, and that noboby has subscribed yet.  See
    # `MailingList.transitionToStatus()` for the details.
    old_date = event.object_before_modification.date_activated
    new_date = event.object.date_activated
    list_looks_new = old_date is None and new_date is not None

    if not (list_looks_new and mailinglist.is_usable):
        return

    team = mailinglist.team
    from_address = format_address(
        team.displayname, config.canonical.noreply_from_address)
    headers = {}
    subject = "New Mailing List for %s" % team.displayname
    template = get_email_template('new-mailing-list.txt', app='registry')
    editemails_url = '%s/+editemails'

    for person in team.allmembers:
        if person.is_team or person.preferredemail is None:
            # This is either a team or a person without a preferred email, so
            # don't send a notification.
            continue
        to_address = [str(person.preferredemail.email)]
        replacements = {
            'user': person.displayname,
            'team_displayname': team.displayname,
            'team_name': team.name,
            'team_url': canonical_url(team),
            'subscribe_url': editemails_url % canonical_url(person),
            }
        body = MailWrapper(72).format(template % replacements,
                                      force_wrap=True)
        simple_sendmail(from_address, to_address, subject, body, headers)
    def sendSelfRenewalNotification(self):
        """See `ITeamMembership`."""
        team = self.team
        member = self.person
        assert team.renewal_policy == TeamMembershipRenewalPolicy.ONDEMAND

        from_addr = format_address(
            team.displayname, config.canonical.noreply_from_address)
        replacements = {'member_name': member.unique_displayname,
                        'team_name': team.unique_displayname,
                        'team_url': canonical_url(team),
                        'dateexpires': self.dateexpires.strftime('%Y-%m-%d')}
        subject = '%s extended their membership' % member.name
        template = get_email_template(
            'membership-member-renewed.txt', app='registry')
        admins_addrs = self.team.getTeamAdminsEmailAddresses()
        for address in admins_addrs:
            recipient = getUtility(IPersonSet).getByEmail(address)
            replacements['recipient_name'] = recipient.displayname
            msg = MailWrapper().format(
                template % replacements, force_wrap=True)
            simple_sendmail(from_addr, address, subject, msg)
Example #47
0
def new_import(code_import, event):
    """Email the vcs-imports team about a new code import."""
    if (event.user is None
        or IUnauthenticatedPrincipal.providedBy(event.user)):
        # If there is no logged in user, then we are most likely in a
        # test.
        return
    user = IPerson(event.user)
    subject = 'New code import: %s/%s' % (
        code_import.branch.target.name, code_import.branch.name)
    if code_import.rcs_type == RevisionControlSystems.CVS:
        location = '%s, %s' % (code_import.cvs_root, code_import.cvs_module)
    else:
        location = code_import.url
    rcs_type_map = {
        RevisionControlSystems.CVS: 'CVS',
        RevisionControlSystems.SVN: 'subversion',
        RevisionControlSystems.BZR_SVN: 'subversion',
        RevisionControlSystems.GIT: 'git',
        RevisionControlSystems.BZR: 'bazaar',
        }
    body = get_email_template('new-code-import.txt', app='code') % {
        'person': code_import.registrant.displayname,
        'branch': canonical_url(code_import.branch),
        'rcs_type': rcs_type_map[code_import.rcs_type],
        'location': location,
        }

    from_address = format_address(
        user.displayname, user.preferredemail.email)

    vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports
    headers = {'X-Launchpad-Branch': code_import.branch.unique_name,
               'X-Launchpad-Message-Rationale':
                   'Operator @%s' % vcs_imports.name,
               'X-Launchpad-Notification-Type': 'code-import',
               }
    for address in get_contact_email_addresses(vcs_imports):
        simple_sendmail(from_address, address, subject, body, headers)
    def sendCancellationEmail(self, token):
        """Send an email to the person whose subscription was cancelled."""
        if token.archive.suppress_subscription_notifications:
            # Don't send an email if they should be suppresed for the
            # archive
            return
        send_to_person = token.person
        ppa_name = token.archive.displayname
        ppa_owner_url = canonical_url(token.archive.owner)
        subject = "PPA access cancelled for %s" % ppa_name
        template = get_email_template(
            "ppa-subscription-cancelled.txt", app='soyuz')

        assert not send_to_person.is_team, (
            "Token.person is a team, it should always be individuals.")

        if send_to_person.preferredemail is None:
            # The person has no preferred email set, so we don't
            # email them.
            return

        to_address = [send_to_person.preferredemail.email]
        replacements = {
            'recipient_name': send_to_person.displayname,
            'ppa_name': ppa_name,
            'ppa_owner_url': ppa_owner_url,
            }
        body = MailWrapper(72).format(
            template % replacements, force_wrap=True)

        from_address = format_address(
            ppa_name,
            config.canonical.noreply_from_address)

        headers = {
            'Sender': config.canonical.bounce_address,
            }

        simple_sendmail(from_address, to_address, subject, body, headers)
    def run(self):
        self.logger.info("Starting verification of POFile stats at id %d"
            % self.start_at_id)
        loop = Verifier(self.transaction, self.logger, self.start_at_id)

        # Since the script can run for a long time, our deployment
        # process might remove the Launchpad tree the script was run
        # from, thus the script failing to find the email template
        # if it was attempted after DBLoopTuner run is completed.
        # See bug #811447 for OOPS we used to get then.
        template = get_email_template(
            'pofile-stats.txt', 'translations')

        # Each iteration of our loop collects all statistics first, before
        # modifying any rows in the database.  With any locks on the database
        # acquired only at the very end of the iteration, we can afford to
        # make relatively long, low-overhead iterations without disrupting
        # application response times.
        iteration_duration = (
            config.rosetta_pofile_stats.looptuner_iteration_duration)
        DBLoopTuner(loop, iteration_duration).run()

        if loop.total_incorrect > 0 or loop.total_exceptions > 0:
            # Not all statistics were correct, or there were failures while
            # checking them.  Email the admins.
            message = template % {
                'exceptions': loop.total_exceptions,
                'errors': loop.total_incorrect,
                'total': loop.total_checked}
            simple_sendmail(
                from_addr=config.canonical.noreply_from_address,
                to_addrs=[config.launchpad.errors_address],
                subject="POFile statistics errors",
                body=MailWrapper().format(message))
            self.transaction.commit()

        self.logger.info("Done.")
def notify_invitation_to_join_team(event):
    """Notify team admins that the team has been invited to join another team.

    The notification will include a link to a page in which any team admin can
    accept the invitation.

    XXX: Guilherme Salgado 2007-05-08:
    At some point we may want to extend this functionality to allow invites
    to be sent to users as well, but for now we only use it for teams.
    """
    member = event.member
    assert member.is_team
    team = event.team
    membership = getUtility(ITeamMembershipSet).getByPersonAndTeam(
        member, team)
    assert membership is not None

    reviewer = membership.proposed_by
    admin_addrs = member.getTeamAdminsEmailAddresses()
    from_addr = format_address(
        team.displayname, config.canonical.noreply_from_address)
    subject = 'Invitation for %s to join' % member.name
    templatename = 'membership-invitation.txt'
    template = get_email_template(templatename, app='registry')
    replacements = {
        'reviewer': '%s (%s)' % (reviewer.displayname, reviewer.name),
        'member': '%s (%s)' % (member.displayname, member.name),
        'team': '%s (%s)' % (team.displayname, team.name),
        'team_url': canonical_url(team),
        'membership_invitations_url':
            "%s/+invitation/%s" % (canonical_url(member), team.name)}
    for address in admin_addrs:
        recipient = getUtility(IPersonSet).getByEmail(address)
        replacements['recipient_name'] = recipient.displayname
        msg = MailWrapper().format(template % replacements, force_wrap=True)
        simple_sendmail(from_addr, address, subject, msg)
    def sendExpirationWarningEmail(self):
        """See `ITeamMembership`."""
        if self.dateexpires is None:
            raise AssertionError(
                '%s in team %s has no membership expiration date.' %
                (self.person.name, self.team.name))
        if self.dateexpires < datetime.now(pytz.timezone('UTC')):
            # The membership has reached expiration. Silently return because
            # there is nothing to do. The member will have received emails
            # from previous calls by flag-expired-memberships.py
            return
        member = self.person
        team = self.team
        if member.is_team:
            recipient = member.teamowner
            templatename = 'membership-expiration-warning-bulk.txt'
            subject = '%s will expire soon from %s' % (member.name, team.name)
        else:
            recipient = member
            templatename = 'membership-expiration-warning-personal.txt'
            subject = 'Your membership in %s is about to expire' % team.name

        if team.renewal_policy == TeamMembershipRenewalPolicy.ONDEMAND:
            how_to_renew = (
                "If you want, you can renew this membership at\n"
                "<%s/+expiringmembership/%s>"
                % (canonical_url(member), team.name))
        elif not self.canChangeExpirationDate(recipient):
            admins_names = []
            admins = team.getDirectAdministrators()
            assert admins.count() >= 1
            if admins.count() == 1:
                admin = admins[0]
                how_to_renew = (
                    "To prevent this membership from expiring, you should "
                    "contact the\nteam's administrator, %s.\n<%s>"
                    % (admin.unique_displayname, canonical_url(admin)))
            else:
                for admin in admins:
                    admins_names.append(
                        "%s <%s>" % (admin.unique_displayname,
                                        canonical_url(admin)))

                how_to_renew = (
                    "To prevent this membership from expiring, you should "
                    "get in touch\nwith one of the team's administrators:\n")
                how_to_renew += "\n".join(admins_names)
        else:
            how_to_renew = (
                "To stay a member of this team you should extend your "
                "membership at\n<%s/+member/%s>"
                % (canonical_url(team), member.name))

        to_addrs = get_contact_email_addresses(recipient)
        if len(to_addrs) == 0:
            # The user does not have a preferred email address, he was
            # probably suspended.
            return
        formatter = DurationFormatterAPI(
            self.dateexpires - datetime.now(pytz.timezone('UTC')))
        replacements = {
            'recipient_name': recipient.displayname,
            'member_name': member.unique_displayname,
            'team_url': canonical_url(team),
            'how_to_renew': how_to_renew,
            'team_name': team.unique_displayname,
            'expiration_date': self.dateexpires.strftime('%Y-%m-%d'),
            'approximate_duration': formatter.approximateduration()}

        msg = get_email_template(templatename, app='registry') % replacements
        from_addr = format_address(
            team.displayname, config.canonical.noreply_from_address)
        simple_sendmail(from_addr, to_addrs, subject, msg)
    def importBugComments(self):
        """Import all the comments from the remote bug."""
        with self.transaction:
            local_bug_id = self.bug_watch.bug.id
            remote_bug_id = self.bug_watch.remotebug

        # Construct a list of the comment IDs we want to import; i.e.
        # those which we haven't already imported.
        all_comment_ids = self.external_bugtracker.getCommentIds(
            remote_bug_id)

        with self.transaction:
            comment_ids_to_import = [
                comment_id for comment_id in all_comment_ids
                if not self.bug_watch.hasComment(comment_id)]

        self.external_bugtracker.fetchComments(
            remote_bug_id, comment_ids_to_import)

        with self.transaction:
            previous_imported_comments = (
                self.bug_watch.getImportedBugMessages())
            is_initial_import = previous_imported_comments.count() == 0
            imported_comments = []

            for comment_id in comment_ids_to_import:
                displayname, email = (
                    self.external_bugtracker.getPosterForComment(
                        remote_bug_id, comment_id))

                if displayname is None and email is None:
                    # If we don't have a displayname or an email address
                    # then we can't create a Launchpad Person as the author
                    # of this comment. We raise an OOPS and continue.
                    self.warning(
                        "Unable to import remote comment author. No email "
                        "address or display name found.",
                        get_remote_system_oops_properties(
                            self.external_bugtracker),
                        sys.exc_info())
                    continue

                poster = self.bug_watch.bugtracker.ensurePersonForSelf(
                    displayname, email, PersonCreationRationale.BUGIMPORT,
                    "when importing comments for %s." % self.bug_watch.title)

                comment_message = (
                    self.external_bugtracker.getMessageForComment(
                        remote_bug_id, comment_id, poster))

                bug_message = self.bug_watch.addComment(
                    comment_id, comment_message)
                imported_comments.append(bug_message)

            if len(imported_comments) > 0:
                self.bug_watch_updater = (
                    getUtility(ILaunchpadCelebrities).bug_watch_updater)
                if is_initial_import:
                    notification_text = get_email_template(
                        'bugwatch-initial-comment-import.txt', 'bugs') % dict(
                            num_of_comments=len(imported_comments),
                            bug_watch_url=self.bug_watch.url)
                    comment_text_template = get_email_template(
                        'bugwatch-comment.txt', 'bugs')

                    for bug_message in imported_comments:
                        comment = bug_message.message
                        notification_text += comment_text_template % dict(
                            comment_date=comment.datecreated.isoformat(),
                            commenter=comment.owner.displayname,
                            comment_text=comment.text_contents,
                            comment_reply_url=canonical_url(comment))
                    notification_message = getUtility(IMessageSet).fromText(
                        subject=self.bug_watch.bug.followup_subject(),
                        content=notification_text,
                        owner=self.bug_watch_updater)
                    self.bug_watch.bug.addCommentNotification(
                        notification_message)
                else:
                    for bug_message in imported_comments:
                        notify(ObjectCreatedEvent(
                            bug_message,
                            user=self.bug_watch_updater))

            self.logger.info("Imported %(count)i comments for remote bug "
                "%(remotebug)s on %(bugtracker_url)s into Launchpad bug "
                "%(bug_id)s." %
                {'count': len(imported_comments),
                 'remotebug': remote_bug_id,
                 'bugtracker_url': self.external_bugtracker.baseurl,
                 'bug_id': local_bug_id})