# 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.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}) ] return [[dt] for dt in com_doctypes if txt.lower().replace("%", "") in dt.lower() and dt in can_read] 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, ) -> Optional["EmailQueue"]: """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. return builder.process(send_now=now)