def prepare_password_changed_mail(user): """Return an e-mail notifying the user that their password changed. Args: user (django.contrib.auth.models.User): The user whose password changed. Returns: EmailMessage: The generated message. """ server_url = get_server_url() context = { 'api_token_url': AuthenticationPage.get_absolute_url(), 'has_api_tokens': user.webapi_tokens.exists(), 'server_url': server_url, 'user': user, } user_email = build_email_address_for_user(user) text_body = render_to_string('notifications/password_changed.txt', context) html_body = render_to_string('notifications/password_changed.html', context) return EmailMessage(subject='Password changed for user "%s" on %s' % (user.username, server_url), text_body=text_body, html_body=html_body, from_email=settings.SERVER_EMAIL, sender=settings.SERVER_EMAIL, to=user_email)
def prepare_password_changed_mail(user): """Return an e-mail notifying the user that their password changed. Args: user (django.contrib.auth.models.User): The user whose password changed. Returns: EmailMessage: The generated message. """ server_url = get_server_url() context = { 'api_token_url': AuthenticationPage.get_absolute_url(), 'has_api_tokens': user.webapi_tokens.exists(), 'server_url': server_url, 'user': user, } user_email = build_email_address_for_user(user) text_body = render_to_string('notifications/password_changed.txt', context) html_body = render_to_string('notifications/password_changed.html', context) return EmailMessage( subject='Password changed for user "%s" on %s' % (user.username, server_url), text_body=text_body, html_body=html_body, from_email=settings.DEFAULT_FROM_EMAIL, sender=settings.DEFAULT_FROM_EMAIL, to=(user_email,))
def prepare_webapi_token_mail(webapi_token, op): """Return an e-mail message notifying a user about a WebAPI token change. Args: webapi_token (reviewboard.notifications.models.WebAPIToken): The token that was created, updated, or deleted. op (unicode): The operation on the token. This is one of: * ``'created'`` * ``'updated'`` * ``'deleted'`` Returns: EmailMessage: The genereated e-mail. """ product_name = settings.PRODUCT_NAME if op == 'created': subject = 'New %s API token created' % product_name template_name = 'notifications/api_token_created' elif op == 'updated': subject = '%s API token updated' % product_name template_name = 'notifications/api_token_updated' elif op == 'deleted': subject = '%s API token deleted' % product_name template_name = 'notifications/api_token_deleted' else: raise ValueError('Unexpected op "%s" passed to mail_webapi_token.' % op) user = webapi_token.user user_email = build_email_address_for_user(user) context = { 'api_token': webapi_token, 'api_tokens_url': AuthenticationPage.get_absolute_url(), 'partial_token': '%s...' % webapi_token.token[:10], 'user': user, 'site_root_url': get_server_url(), 'PRODUCT_NAME': product_name, } text_message = render_to_string(template_name='%s.txt' % template_name, context=context) html_message = render_to_string(template_name='%s.html' % template_name, context=context) return EmailMessage(subject=subject, text_body=text_message, html_body=html_message, from_email=settings.DEFAULT_FROM_EMAIL, sender=settings.DEFAULT_FROM_EMAIL, to=[user_email])
def prepare_webapi_token_mail(webapi_token, op): """Return an e-mail message notifying a user about a WebAPI token change. Args: webapi_token (reviewboard.notifications.models.WebAPIToken): The token that was created, updated, or deleted. op (unicode): The operation on the token. This is one of: * ``'created'`` * ``'updated'`` * ``'deleted'`` Returns: EmailMessage: The genereated e-mail. """ if op == 'created': subject = 'New Review Board API token created' template_name = 'notifications/api_token_created' elif op == 'updated': subject = 'Review Board API token updated' template_name = 'notifications/api_token_updated' elif op == 'deleted': subject = 'Review Board API token deleted' template_name = 'notifications/api_token_deleted' else: raise ValueError('Unexpected op "%s" passed to mail_webapi_token.' % op) user = webapi_token.user user_email = build_email_address_for_user(user) context = { 'api_token': webapi_token, 'api_token_url': '%s#api-tokens' % build_server_url(reverse('user-preferences')), 'partial_token': '%s...' % webapi_token.token[:10], 'user': user, 'site_url': get_server_url(), } text_message = render_to_string('%s.txt' % template_name, context) html_message = render_to_string('%s.html' % template_name, context) return EmailMessage(subject=subject, text_body=text_message, html_body=html_message, from_email=settings.SERVER_EMAIL, sender=settings.SERVER_EMAIL, to=[user_email])
def prepare_webapi_token_mail(webapi_token, op): """Return an e-mail message notifying a user about a WebAPI token change. Args: webapi_token (reviewboard.notifications.models.WebAPIToken): The token that was created, updated, or deleted. op (unicode): The operation on the token. This is one of: * ``'created'`` * ``'updated'`` * ``'deleted'`` Returns: EmailMessage: The genereated e-mail. """ if op == 'created': subject = 'New Review Board API token created' template_name = 'notifications/api_token_created' elif op == 'updated': subject = 'Review Board API token updated' template_name = 'notifications/api_token_updated' elif op == 'deleted': subject = 'Review Board API token deleted' template_name = 'notifications/api_token_deleted' else: raise ValueError('Unexpected op "%s" passed to mail_webapi_token.' % op) user = webapi_token.user user_email = build_email_address_for_user(user) context = { 'api_token': webapi_token, 'api_tokens_url': AuthenticationPage.get_absolute_url(), 'partial_token': '%s...' % webapi_token.token[:10], 'user': user, 'site_root_url': get_server_url(), } text_message = render_to_string('%s.txt' % template_name, context) html_message = render_to_string('%s.html' % template_name, context) return EmailMessage( subject=subject, text_body=text_message, html_body=html_message, from_email=settings.DEFAULT_FROM_EMAIL, sender=settings.DEFAULT_FROM_EMAIL, to=[user_email])
def prepare_base_review_request_mail(user, review_request, subject, in_reply_to, to_field, cc_field, template_name_base, context=None, extra_headers=None): """Return a customized review request e-mail. This is intended to be called by one of the ``prepare_{type}_mail`` functions in this file. This method builds up a common context that all review request-related e-mails will use to render their templates, as well as handling user preferences regarding e-mail and add adding additional headers. Args: user (django.contrib.auth.models.User): The user who is sending the e-mail. review_request (reviewboard.reviews.models.review_request.ReviewRequest): The review request this e-mail is regarding. subject (unicode): The e-mail subject line. in_reply_to (unicode): The e-mail message ID this message is in response to or ``None``. to_field (set): The set of :py:class:`~django.contrib.auth.models.User` and :py:class`~reviewboard.reviews.models.group.Group`s to this e-mail will be sent to. cc_field (set): The set of :py:class:`~django.contrib.auth.models.User` and :py:class`~reviewboard.reviews.models.group.Group`s to be CC'ed on the e-mail. template_name_base (unicode): The name of the template to use to generate the e-mail without its extension. The plain-text version of the e-mail will append ``.txt`` to this and and the rich-text version of the e-mail will append ``.html``. context (dict, optional): Optional additional template rendering context. extra_headers (dict, optional): Optional additional headers to include. Returns: EmailMessage: The prepared e-mail message. """ user_email = build_email_address_for_user(user) to_field = recipients_to_addresses(to_field, review_request.id) cc_field = recipients_to_addresses(cc_field, review_request.id) - to_field if not user.should_send_own_updates(): to_field.discard(user_email) cc_field.discard(user_email) if not to_field and not cc_field: # This e-mail would have no recipients, so we won't send it. return None if not context: context = {} context.update({ 'user': user, 'site_url': get_server_url(), 'review_request': review_request, }) local_site = review_request.local_site if local_site: context['local_site_name'] = local_site.name text_body = render_to_string('%s.txt' % template_name_base, context) html_body = render_to_string('%s.html' % template_name_base, context) server_url = get_server_url(local_site=local_site) headers = MultiValueDict({ 'X-ReviewBoard-URL': [server_url], 'X-ReviewRequest-URL': [ build_server_url(review_request.get_absolute_url(), local_site=local_site) ], 'X-ReviewGroup': [ ', '.join( review_request.target_groups.values_list('name', flat=True)) ], }) if extra_headers: if not isinstance(extra_headers, MultiValueDict): extra_headers = MultiValueDict( (key, [value]) for key, value in six.iteritems(extra_headers)) headers.update(extra_headers) if review_request.repository: headers['X-ReviewRequest-Repository'] = review_request.repository.name latest_diffset = review_request.get_latest_diffset() if latest_diffset: modified_files = set() for filediff in latest_diffset.files.all(): if filediff.deleted or filediff.copied or filediff.moved: modified_files.add(filediff.source_file) if filediff.is_new or filediff.copied or filediff.moved: modified_files.add(filediff.dest_file) # The following code segment deals with the case where the client adds # a significant amount of files with large names. We limit the number # of headers; when more than 8192 characters are reached, we stop # adding filename headers. current_header_length = 0 for filename in modified_files: current_header_length += (HEADER_ADDITIONAL_CHARACTERS_LENGTH + len(filename)) if current_header_length > MAX_FILENAME_HEADERS_LENGTH: logging.warning( 'Unable to store all filenames in the ' 'X-ReviewBoard-Diff-For headers when sending e-mail for ' 'review request %s: The header size exceeds the limit of ' '%s. Remaining headers have been omitted.', review_request.display_id, MAX_FILENAME_HEADERS_LENGTH) break headers.appendlist('X-ReviewBoard-Diff-For', filename) if settings.DEFAULT_FROM_EMAIL: sender = build_email_address(full_name=user.get_full_name(), email=settings.DEFAULT_FROM_EMAIL) else: sender = None return EmailMessage(subject=subject.strip(), text_body=text_body.encode('utf-8'), html_body=html_body.encode('utf-8'), from_email=user_email, sender=sender, to=list(to_field), cc=list(cc_field), in_reply_to=in_reply_to, headers=headers)
def prepare_base_review_request_mail(user, review_request, subject, in_reply_to, to_field, cc_field, template_name_base, context=None, extra_headers=None): """Return a customized review request e-mail. This is intended to be called by one of the ``prepare_{type}_mail`` functions in this file. This method builds up a common context that all review request-related e-mails will use to render their templates, as well as handling user preferences regarding e-mail and add adding additional headers. Args: user (django.contrib.auth.models.User): The user who is sending the e-mail. review_request (reviewboard.reviews.models.review_request.ReviewRequest): The review request this e-mail is regarding. subject (unicode): The e-mail subject line. in_reply_to (unicode): The e-mail message ID this message is in response to or ``None``. to_field (set): The set of :py:class:`~django.contrib.auth.models.User` and :py:class`~reviewboard.reviews.models.group.Group`s to this e-mail will be sent to. cc_field (set): The set of :py:class:`~django.contrib.auth.models.User` and :py:class`~reviewboard.reviews.models.group.Group`s to be CC'ed on the e-mail. template_name_base (unicode): The name of the template to use to generate the e-mail without its extension. The plain-text version of the e-mail will append ``.txt`` to this and and the rich-text version of the e-mail will append ``.html``. context (dict, optional): Optional additional template rendering context. extra_headers (dict, optional): Optional additional headers to include. Returns: EmailMessage: The prepared e-mail message. """ user_email = build_email_address_for_user(user) to_field = recipients_to_addresses(to_field, review_request.id) cc_field = recipients_to_addresses(cc_field, review_request.id) - to_field if not user.should_send_own_updates(): to_field.discard(user_email) cc_field.discard(user_email) if not to_field and not cc_field: # This e-mail would have no recipients, so we won't send it. return None if not context: context = {} context.update({ 'user': user, 'site_url': _get_server_base_url(), 'review_request': review_request, }) local_site = review_request.local_site if local_site: context['local_site_name'] = local_site.name text_body = render_to_string('%s.txt' % template_name_base, context) html_body = render_to_string('%s.html' % template_name_base, context) server_url = get_server_url(local_site=local_site) headers = MultiValueDict({ 'X-ReviewBoard-URL': [server_url], 'X-ReviewRequest-URL': [ build_server_url(review_request.get_absolute_url(), local_site=local_site) ], 'X-ReviewGroup': [', '.join( review_request.target_groups.values_list('name', flat=True) )], }) if extra_headers: if not isinstance(extra_headers, MultiValueDict): extra_headers = MultiValueDict( (key, [value]) for key, value in six.iteritems(extra_headers) ) headers.update(extra_headers) if review_request.repository: headers['X-ReviewRequest-Repository'] = review_request.repository.name latest_diffset = review_request.get_latest_diffset() if latest_diffset: modified_files = set() for filediff in latest_diffset.files.all(): if not filediff.is_new: modified_files.add(filediff.source_file) if not filediff.deleted: modified_files.add(filediff.dest_file) # The following code segment deals with the case where the client adds # a significant amount of files with large names. We limit the number # of headers; when more than 8192 characters are reached, we stop # adding filename headers. current_header_length = 0 for filename in modified_files: current_header_length += (HEADER_ADDITIONAL_CHARACTERS_LENGTH + len(filename)) if current_header_length > MAX_FILENAME_HEADERS_LENGTH: logging.warning( 'Unable to store all filenames in the ' 'X-ReviewBoard-Diff-For headers when sending e-mail for ' 'review request %s: The header size exceeds the limit of ' '%s. Remaining headers have been omitted.', review_request.display_id, MAX_FILENAME_HEADERS_LENGTH) break headers.appendlist('X-ReviewBoard-Diff-For', filename) if settings.DEFAULT_FROM_EMAIL: sender = build_email_address(full_name=user.get_full_name(), email=settings.DEFAULT_FROM_EMAIL) else: sender = None return EmailMessage(subject=subject.strip(), text_body=text_body.encode('utf-8'), html_body=html_body.encode('utf-8'), from_email=user_email, sender=sender, to=list(to_field), cc=list(cc_field), in_reply_to=in_reply_to, headers=headers)