Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/analytic_plot_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
# Django imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.db.models import Q, Case, Value, When
from django.db import models
from django.db.models.functions import Concat
Expand All @@ -18,6 +17,7 @@
from plane.db.models import Issue
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.analytics_plot import build_graph_plot
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception
from plane.utils.issue_filters import issue_filters

Expand Down Expand Up @@ -48,7 +48,7 @@ def send_export_email(email, slug, csv_buffer, rows):
"""Helper function to send export email."""
subject = "Your Export is ready"
html_content = render_to_string("emails/exports/analytics.html", {})
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

csv_buffer.seek(0)

Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/email_notification_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

# Django imports
from django.utils import timezone
from django.utils.html import strip_tags

# Module imports
from plane.db.models import EmailNotificationLog, Issue, User
from plane.license.utils.instance_value import get_email_configuration
from plane.settings.redis import redis_instance
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand Down Expand Up @@ -256,7 +256,7 @@ def send_email_notification(issue_id, notification_data, receiver_id, email_noti
"entity_type": "issue",
}
html_content = render_to_string("emails/notifications/issue-updates.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

try:
connection = get_connection(
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/forgot_password_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
# Third party imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Module imports
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand Down Expand Up @@ -41,7 +41,7 @@ def forgot_password(first_name, email, uidb64, token, current_site):

html_content = render_to_string("emails/auth/forgot_password.html", context)

text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

connection = get_connection(
host=EMAIL_HOST,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/magic_link_code_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
# Third party imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Module imports
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand All @@ -33,7 +33,7 @@ def magic_link(email, key, token):
context = {"code": token, "email": email}

html_content = render_to_string("emails/auth/magic_signin.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

connection = get_connection(
host=EMAIL_HOST,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/project_add_user_email_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
# Third party imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags


# Module imports
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception
from plane.db.models import ProjectMember
from plane.db.models import User
Expand Down Expand Up @@ -55,7 +55,7 @@ def project_add_user_email(current_site, project_member_id, invitor_id):

# Render the email template
html_content = render_to_string("emails/notifications/project_addition.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)
# Initialize the connection
connection = get_connection(
host=EMAIL_HOST,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/project_invitation_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
# Third party imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Module imports
from plane.db.models import Project, ProjectMemberInvite, User
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand All @@ -37,7 +37,7 @@ def project_invitation(email, project_id, token, current_site, invitor):

html_content = render_to_string("emails/invitations/project_invitation.html", context)

text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

project_member_invite.message = text_content
project_member_invite.save()
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/user_activation_email_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
# Django imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Third party imports
from celery import shared_task

# Module imports
from plane.db.models import User
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand All @@ -27,7 +27,7 @@ def user_activation_email(current_site, user_id):
# Send email to user
html_content = render_to_string("emails/user/user_activation.html", context)

text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)
# Configure email connection from the database
(
EMAIL_HOST,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/user_deactivation_email_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
# Django imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Third party imports
from celery import shared_task

# Module imports
from plane.db.models import User
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand All @@ -27,7 +27,7 @@ def user_deactivation_email(current_site, user_id):
# Send email to user
html_content = render_to_string("emails/user/user_deactivation.html", context)

text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)
# Configure email connection from the database
(
EMAIL_HOST,
Expand Down
6 changes: 3 additions & 3 deletions apps/api/plane/bgtasks/user_email_update_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
# Django imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Module imports
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand All @@ -32,7 +32,7 @@ def send_email_update_magic_code(email, token):
context = {"code": token, "email": email}

html_content = render_to_string("emails/auth/magic_signin.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

connection = get_connection(
host=EMAIL_HOST,
Expand Down Expand Up @@ -83,7 +83,7 @@ def send_email_update_confirmation(email):
context = {"email": email}

html_content = render_to_string("emails/user/email_updated.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

connection = get_connection(
host=EMAIL_HOST,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/webhook_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from django.core.mail import EmailMultiAlternatives, get_connection
from django.core.serializers.json import DjangoJSONEncoder
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.core.exceptions import ObjectDoesNotExist

# Module imports
Expand Down Expand Up @@ -47,6 +46,7 @@
IssueAssignee,
)
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception
from plane.settings.mongo import MongoConnection

Expand Down Expand Up @@ -218,7 +218,7 @@ def send_webhook_deactivation_email(webhook_id: str, receiver_id: str, current_s
"webhook_url": f"{current_site}/{str(webhook.workspace.slug)}/settings/webhooks/{str(webhook.id)}",
}
html_content = render_to_string("emails/notifications/webhook-deactivate.html", context)
text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

# Set the email connection
connection = get_connection(
Expand Down
4 changes: 2 additions & 2 deletions apps/api/plane/bgtasks/workspace_invitation_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
# Django imports
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.utils.html import strip_tags

# Module imports
from plane.db.models import User, Workspace, WorkspaceMemberInvite
from plane.license.utils.instance_value import get_email_configuration
from plane.utils.email import generate_plain_text_from_html
from plane.utils.exception_logger import log_exception


Expand Down Expand Up @@ -53,7 +53,7 @@ def workspace_invitation(email, workspace_id, token, current_site, inviter):

html_content = render_to_string("emails/invitations/workspace_invitation.html", context)

text_content = strip_tags(html_content)
text_content = generate_plain_text_from_html(html_content)

workspace_member_invite.message = text_content
workspace_member_invite.save()
Expand Down
42 changes: 42 additions & 0 deletions apps/api/plane/utils/email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
# SPDX-License-Identifier: LicenseRef-Plane-Commercial
#
# Licensed under the Plane Commercial License (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# https://plane.so/legals/eula
#
# DO NOT remove or modify this notice.
# NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.

# Python imports
import re

# Django imports
from django.utils.html import strip_tags


def generate_plain_text_from_html(html_content):
"""
Generate clean plain text from HTML email template.
Removes all HTML tags, CSS styles, and excessive whitespace.

Args:
html_content (str): The HTML content to convert to plain text

Returns:
str: Clean plain text without HTML tags, styles, or excessive whitespace
"""
# Remove style tags and their content
html_content = re.sub(r"<style[^>]*>.*?</style>", "", html_content, flags=re.DOTALL | re.IGNORECASE)

# Strip HTML tags
text_content = strip_tags(html_content)

# Remove excessive empty lines
text_content = re.sub(r"\n\s*\n\s*\n+", "\n\n", text_content)

# Ensure there's a leading and trailing whitespace
text_content = "\n\n" + text_content.lstrip().rstrip() + "\n\n"

return text_content
Loading