From 6e7f034f3300e03c29b3a5efe05454caee03fce2 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Jan 2025 17:55:25 +0530 Subject: [PATCH] feat: Mail Group Member --- .../doctype/mail_agent_job/mail_agent_job.py | 43 ++++++++++ mail/mail/doctype/mail_alias/mail_alias.json | 6 +- mail/mail/doctype/mail_group/mail_group.json | 7 +- .../doctype/mail_group_member/__init__.py | 0 .../mail_group_member/mail_group_member.js | 8 ++ .../mail_group_member/mail_group_member.json | 84 +++++++++++++++++++ .../mail_group_member/mail_group_member.py | 37 ++++++++ .../test_mail_group_member.py | 29 +++++++ 8 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 mail/mail/doctype/mail_group_member/__init__.py create mode 100644 mail/mail/doctype/mail_group_member/mail_group_member.js create mode 100644 mail/mail/doctype/mail_group_member/mail_group_member.json create mode 100644 mail/mail/doctype/mail_group_member/mail_group_member.py create mode 100644 mail/mail/doctype/mail_group_member/test_mail_group_member.py diff --git a/mail/mail/doctype/mail_agent_job/mail_agent_job.py b/mail/mail/doctype/mail_agent_job/mail_agent_job.py index 51b83e7c..30f83702 100644 --- a/mail/mail/doctype/mail_agent_job/mail_agent_job.py +++ b/mail/mail/doctype/mail_agent_job/mail_agent_job.py @@ -381,3 +381,46 @@ def delete_alias_from_agents(account: str, alias: str, agents: list[str] | None agent_job.endpoint = f"/api/principal/{account}" agent_job.request_data = request_data agent_job.insert() + + +def create_group_member_on_agents(group: str, member: str, agents: list[str] | None = None) -> None: + primary_agents = agents or frappe.db.get_all( + "Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name" + ) + + if not primary_agents: + return + + request_data = json.dumps([{"action": "addItem", "field": "members", "value": member}]) + for agent in primary_agents: + agent_job = frappe.new_doc("Mail Agent Job") + agent_job.agent = agent + agent_job.method = "PATCH" + agent_job.endpoint = f"/api/principal/{group}" + agent_job.request_data = request_data + agent_job.insert() + + +def patch_group_member_on_agents( + new_group: str, old_group: str, member: str, agents: list[str] | None = None +) -> None: + delete_account_from_agents(old_group, member, agents) + create_alias_on_agents(new_group, member, agents) + + +def delete_group_member_from_agents(group: str, member: str, agents: list[str] | None = None) -> None: + primary_agents = agents or frappe.db.get_all( + "Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name" + ) + + if not primary_agents: + return + + request_data = json.dumps([{"action": "removeItem", "field": "members", "value": member}]) + for agent in primary_agents: + agent_job = frappe.new_doc("Mail Agent Job") + agent_job.agent = agent + agent_job.method = "PATCH" + agent_job.endpoint = f"/api/principal/{group}" + agent_job.request_data = request_data + agent_job.insert() diff --git a/mail/mail/doctype/mail_alias/mail_alias.json b/mail/mail/doctype/mail_alias/mail_alias.json index aee0bc5b..484e4029 100644 --- a/mail/mail/doctype/mail_alias/mail_alias.json +++ b/mail/mail/doctype/mail_alias/mail_alias.json @@ -58,6 +58,8 @@ { "fieldname": "alias_for_type", "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Alias For (Type)", "options": "\nMail Account\nMail Group", "reqd": 1 @@ -65,6 +67,8 @@ { "fieldname": "alias_for_name", "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Alias For (Name)", "options": "alias_for_type", "reqd": 1 @@ -76,7 +80,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-01-06 14:29:12.825375", + "modified": "2025-01-06 17:51:26.806575", "modified_by": "Administrator", "module": "Mail", "name": "Mail Alias", diff --git a/mail/mail/doctype/mail_group/mail_group.json b/mail/mail/doctype/mail_group/mail_group.json index f8559fb1..5953ed81 100644 --- a/mail/mail/doctype/mail_group/mail_group.json +++ b/mail/mail/doctype/mail_group/mail_group.json @@ -65,9 +65,14 @@ "group": "Reference", "link_doctype": "Mail Alias", "link_fieldname": "alias_for_name" + }, + { + "group": "Reference", + "link_doctype": "Mail Group Member", + "link_fieldname": "mail_group" } ], - "modified": "2025-01-06 15:29:16.465956", + "modified": "2025-01-06 17:24:17.818529", "modified_by": "Administrator", "module": "Mail", "name": "Mail Group", diff --git a/mail/mail/doctype/mail_group_member/__init__.py b/mail/mail/doctype/mail_group_member/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mail/mail/doctype/mail_group_member/mail_group_member.js b/mail/mail/doctype/mail_group_member/mail_group_member.js new file mode 100644 index 00000000..38112135 --- /dev/null +++ b/mail/mail/doctype/mail_group_member/mail_group_member.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Mail Group Member", { +// refresh(frm) { + +// }, +// }); diff --git a/mail/mail/doctype/mail_group_member/mail_group_member.json b/mail/mail/doctype/mail_group_member/mail_group_member.json new file mode 100644 index 00000000..74a60edf --- /dev/null +++ b/mail/mail/doctype/mail_group_member/mail_group_member.json @@ -0,0 +1,84 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "hash", + "creation": "2025-01-06 15:47:23.657133", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_hw88", + "mail_group", + "column_break_upc0", + "member_type", + "member_name" + ], + "fields": [ + { + "fieldname": "section_break_hw88", + "fieldtype": "Section Break" + }, + { + "fieldname": "mail_group", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Mail Group", + "no_copy": 1, + "options": "Mail Group", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "member_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Member (Type)", + "no_copy": 1, + "options": "\nMail Account\nMail Group", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "column_break_upc0", + "fieldtype": "Column Break" + }, + { + "fieldname": "member_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Member (Name)", + "no_copy": 1, + "options": "member_type", + "reqd": 1, + "set_only_once": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-01-06 17:50:56.129594", + "modified_by": "Administrator", + "module": "Mail", + "name": "Mail Group Member", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/mail/mail/doctype/mail_group_member/mail_group_member.py b/mail/mail/doctype/mail_group_member/mail_group_member.py new file mode 100644 index 00000000..1e788547 --- /dev/null +++ b/mail/mail/doctype/mail_group_member/mail_group_member.py @@ -0,0 +1,37 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document + +from mail.mail.doctype.mail_agent_job.mail_agent_job import ( + create_group_member_on_agents, + delete_group_member_from_agents, +) + + +class MailGroupMember(Document): + def validate(self) -> None: + self.validate_member_name() + + def validate_member_name(self) -> None: + if self.mail_group == self.member_name: + if self.member_type == "Mail Group": + frappe.throw(_("Mail Group cannot be a member of itself")) + else: + frappe.throw(_("Member cannot be the same as the Mail Group")) + + def after_insert(self) -> None: + create_group_member_on_agents(self.mail_group, self.member_name) + + def on_trash(self) -> None: + delete_group_member_from_agents(self.mail_group, self.member_name) + + +def on_doctype_update() -> None: + frappe.db.add_unique( + "Mail Group Member", + ["mail_group", "member_name"], + constraint_name="unique_mail_group_member", + ) diff --git a/mail/mail/doctype/mail_group_member/test_mail_group_member.py b/mail/mail/doctype/mail_group_member/test_mail_group_member.py new file mode 100644 index 00000000..9bfc7f9d --- /dev/null +++ b/mail/mail/doctype/mail_group_member/test_mail_group_member.py @@ -0,0 +1,29 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase, UnitTestCase + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + +class UnitTestMailGroupMember(UnitTestCase): + """ + Unit tests for MailGroupMember. + Use this class for testing individual functions and methods. + """ + + pass + + +class IntegrationTestMailGroupMember(IntegrationTestCase): + """ + Integration tests for MailGroupMember. + Use this class for testing interactions between multiple components. + """ + + pass