diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 7564de029d..8aba7e0566 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -218,6 +218,24 @@ def download_vcard(contact: str): frappe.response["type"] = "binary" +@frappe.whitelist() +def download_vcards(contacts: str): + """Download vCard for the contact""" + from frappe.utils.data import now + + vcards = [] + for contact_id in frappe.parse_json(contacts): + contact = frappe.get_doc("Contact", contact_id) + vcard = contact.get_vcard() + vcards.append(vcard.serialize()) + + timestamp = now()[:19] # remove milliseconds + + frappe.response["filename"] = f"{timestamp} Contacts.vcf" + frappe.response["filecontent"] = "\n".join(vcards).encode("utf-8") + frappe.response["type"] = "binary" + + def get_default_contact(doctype, name): """Return default contact for the given doctype, name.""" out = frappe.db.sql( diff --git a/frappe/contacts/doctype/contact/contact_list.js b/frappe/contacts/doctype/contact/contact_list.js index 2b3cd8a062..07b4aeb570 100644 --- a/frappe/contacts/doctype/contact/contact_list.js +++ b/frappe/contacts/doctype/contact/contact_list.js @@ -1,3 +1,11 @@ frappe.listview_settings["Contact"] = { add_fields: ["image"], + onload: function (listview) { + listview.page.add_action_item(__("Download vCards"), function () { + const contacts = listview.get_checked_items(); + open_url_post("/api/method/frappe.contacts.doctype.contact.contact.download_vcards", { + contacts: contacts.map((c) => c.name), + }); + }); + }, };