seitime-frappe/frappe/email/__init__.py
Akhil Narang 8723a2b6ee
fix: ruff fixes
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2025-12-22 21:06:48 +05:30

246 lines
7.3 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
from typing import TYPE_CHECKING, Literal, Optional
import frappe
if TYPE_CHECKING:
from frappe.email.doctype.email_queue.email_queue import EmailQueue
def sendmail_to_system_managers(subject, content):
frappe.sendmail(recipients=get_system_managers(), subject=subject, content=content)
@frappe.whitelist()
def get_contact_list(txt, page_length=20, extra_filters: str | None = None) -> list[dict]:
"""Return email ids for a multiselect field."""
if extra_filters:
extra_filters = frappe.parse_json(extra_filters)
filters = [
["Contact Email", "email_id", "is", "set"],
]
if extra_filters:
filters.extend(extra_filters)
fields = ["first_name", "middle_name", "last_name", "company_name"]
contacts = frappe.get_list(
"Contact",
fields=["full_name", "`tabContact Email`.email_id"],
filters=filters,
or_filters=[[field, "like", f"%{txt}%"] for field in fields]
+ [["Contact Email", "email_id", "like", f"%{txt}%"]],
limit_page_length=page_length,
)
# The multiselect field will store the `label` as the selected value.
# The `value` is just used as a unique key to distinguish between the options.
# https://github.com/frappe/frappe/blob/6c6a89bcdd9454060a1333e23b855d0505c9ebc2/frappe/public/js/frappe/form/controls/autocomplete.js#L29-L35
return [
frappe._dict(
value=d.email_id,
label=d.email_id,
description=d.full_name,
)
for d in contacts
]
def get_system_managers():
return frappe.db.sql_list(
"""select parent FROM `tabHas Role`
WHERE role='System Manager'
AND parent!='Administrator'
AND parent IN (SELECT email FROM tabUser WHERE enabled=1)"""
)
@frappe.whitelist()
def relink(name, reference_doctype=None, reference_name=None):
frappe.db.sql(
"""update
`tabCommunication`
set
reference_doctype = %s,
reference_name = %s,
status = "Linked"
where
communication_type = "Communication" and
name = %s""",
(reference_doctype, reference_name, name),
)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_communication_doctype(doctype, txt, searchfield, start, page_len, filters):
user_perms = frappe.utils.user.UserPermissions(frappe.session.user)
user_perms.build_permissions()
can_read = user_perms.can_read
from frappe import _
from frappe.modules import load_doctype_module
com_doctypes = []
if len(txt) < 2:
for name in frappe.get_hooks("communication_doctypes"):
try:
module = load_doctype_module(name, suffix="_dashboard")
if hasattr(module, "get_data"):
for i in module.get_data()["transactions"]:
com_doctypes += i["items"]
except ImportError:
pass
else:
com_doctypes = [
d[0] for d in frappe.db.get_values("DocType", {"issingle": 0, "istable": 0, "hide_toolbar": 0})
]
results = []
txt_lower = txt.lower().replace("%", "")
for dt in list(set(com_doctypes)):
if dt in can_read:
if txt_lower in dt.lower() or txt_lower in _(dt).lower():
results.append([dt])
return results
def sendmail(
recipients=None,
sender="",
subject="No Subject",
message="No Message",
as_markdown=False,
delayed=True,
reference_doctype=None,
reference_name=None,
unsubscribe_method=None,
unsubscribe_params=None,
unsubscribe_message=None,
add_unsubscribe_link=1,
attachments=None,
content=None,
doctype=None,
name=None,
reply_to=None,
queue_separately=False,
cc=None,
bcc=None,
message_id=None,
in_reply_to=None,
send_after=None,
expose_recipients=None,
send_priority=1,
communication=None,
retry=1,
now=None,
read_receipt=None,
is_notification=False,
inline_images=None,
template=None,
args=None,
header=None,
print_letterhead=False,
with_container=False,
email_read_tracker_url=None,
x_priority: Literal[1, 3, 5] = 3,
email_headers=None,
) -> "EmailQueue" | None:
"""Send email using user's default **Email Account** or global default **Email Account**.
:param recipients: List of recipients.
:param sender: Email sender. Default is current user or default outgoing account.
:param subject: Email Subject.
:param message: (or `content`) Email Content.
:param as_markdown: Convert content markdown to HTML.
:param delayed: Send via scheduled email sender **Email Queue**. Don't send immediately. Default is true
:param send_priority: Priority for Email Queue, default 1.
:param reference_doctype: (or `doctype`) Append as communication to this DocType.
:param reference_name: (or `name`) Append as communication to this document name.
:param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe`
:param unsubscribe_params: Unsubscribe paramaters to be loaded on the unsubscribe_method [optional] (dict).
:param attachments: List of attachments.
:param reply_to: Reply-To Email Address.
:param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
:param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
:param send_after: Send after the given datetime.
:param expose_recipients: Display all recipients in the footer message - "This email was sent to"
:param communication: Communication link to be set in Email Queue record
:param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id
:param template: Name of html template from templates/emails folder
:param args: Arguments for rendering the template
:param header: Append header in email
:param with_container: Wraps email inside a styled container
:param x_priority: 1 = HIGHEST, 3 = NORMAL, 5 = LOWEST
:param email_headers: Additional headers to be added in the email, e.g. {"X-Custom-Header": "value"} or {"Custom-Header": "value"}. Automatically prepends "X-" to the header name if not present.
"""
from frappe.utils.jinja import get_email_from_template
if recipients is None:
recipients = []
if cc is None:
cc = []
if bcc is None:
bcc = []
text_content = None
if template:
message, text_content = get_email_from_template(template, args)
message = content or message
if as_markdown:
from frappe.utils import md_to_html
message = md_to_html(message)
if not delayed:
now = True
from frappe.email.doctype.email_queue.email_queue import QueueBuilder
builder = QueueBuilder(
recipients=recipients,
sender=sender,
subject=subject,
message=message,
text_content=text_content,
reference_doctype=doctype or reference_doctype,
reference_name=name or reference_name,
add_unsubscribe_link=add_unsubscribe_link,
unsubscribe_method=unsubscribe_method,
unsubscribe_params=unsubscribe_params,
unsubscribe_message=unsubscribe_message,
attachments=attachments,
reply_to=reply_to,
cc=cc,
bcc=bcc,
message_id=message_id,
in_reply_to=in_reply_to,
send_after=send_after,
expose_recipients=expose_recipients,
send_priority=send_priority,
queue_separately=queue_separately,
communication=communication,
read_receipt=read_receipt,
is_notification=is_notification,
inline_images=inline_images,
header=header,
print_letterhead=print_letterhead,
with_container=with_container,
email_read_tracker_url=email_read_tracker_url,
x_priority=x_priority,
email_headers=email_headers,
)
# build email queue and send the email if send_now is True.
q = builder.process(send_now=False)
if now:
frappe.db.after_commit.add(q.send)
return q