Skip to content

Commit

Permalink
feat: Mail Group
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r committed Jan 6, 2025
1 parent 75528ee commit 18fe0e0
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 44 deletions.
136 changes: 98 additions & 38 deletions mail/mail/doctype/mail_agent_job/mail_agent_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,44 +69,6 @@ def execute(self) -> None:
self.db_update()


def create_domain_on_agents(domain_name: str, agents: list[str] | None = None) -> None:
"""Creates a domain on all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

principal = Principal(name=domain_name, type="domain").__dict__
for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "POST"
agent_job.endpoint = "/api/principal"
agent_job.request_json = principal
agent_job.insert()


def delete_domain_from_agents(domain_name: str, agents: list[str] | None = None) -> None:
"""Deletes a domain from all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "DELETE"
agent_job.endpoint = f"/api/principal/{domain_name}"
agent_job.insert()


def create_dkim_key_on_agents(
domain_name: str, rsa_private_key: str, ed25519_private_key: str, agents: list[str] | None = None
) -> None:
Expand Down Expand Up @@ -187,6 +149,44 @@ def delete_dkim_key_from_agents(domain_name: str, agents: list[str] | None = Non
agent_job.insert()


def create_domain_on_agents(domain_name: str, agents: list[str] | None = None) -> None:
"""Creates a domain on all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

principal = Principal(name=domain_name, type="domain").__dict__
for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "POST"
agent_job.endpoint = "/api/principal"
agent_job.request_json = principal
agent_job.insert()


def delete_domain_from_agents(domain_name: str, agents: list[str] | None = None) -> None:
"""Deletes a domain from all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "DELETE"
agent_job.endpoint = f"/api/principal/{domain_name}"
agent_job.insert()


def create_account_on_agents(
email: str, display_name: str, secret: str, agents: list[str] | None = None
) -> None:
Expand Down Expand Up @@ -274,6 +274,66 @@ def delete_account_from_agents(email: str, agents: list[str] | None = None) -> N
agent_job.insert()


def create_group_on_agents(email: str, display_name: str, agents: list[str] | None = None) -> None:
"""Creates a group on all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

principal = Principal(
name=email,
type="group",
description=display_name,
emails=[email],
enabledPermissions=["email-send", "email-receive"],
).__dict__
for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "POST"
agent_job.endpoint = "/api/principal"
agent_job.request_json = principal
agent_job.insert()


def patch_group_on_agents(email: str, display_name: str, agents: list[str] | None = None) -> None:
"""Patches a group on all primary agents."""

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": "set",
"field": "description",
"value": display_name,
}
]
)
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/{email}"
agent_job.request_data = request_data
agent_job.insert()


def delete_group_from_agents(email: str, agents: list[str] | None = None) -> None:
"""Deletes a group from all primary agents."""

delete_account_from_agents(email, agents)


def create_alias_on_agents(account: str, alias: str, agents: list[str] | None = None) -> None:
"""Creates an alias on all primary agents."""

Expand Down
4 changes: 2 additions & 2 deletions mail/mail/doctype/mail_alias/mail_alias.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"fieldname": "alias_for_type",
"fieldtype": "Select",
"label": "Alias For (Type)",
"options": "\nMail Account",
"options": "\nMail Account\nMail Group",
"reqd": 1
},
{
Expand All @@ -76,7 +76,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-01-06 12:54:35.804872",
"modified": "2025-01-06 14:29:12.825375",
"modified_by": "Administrator",
"module": "Mail",
"name": "Mail Alias",
Expand Down
18 changes: 14 additions & 4 deletions mail/mail/doctype/mail_domain/mail_domain.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@
"links": [
{
"group": "Reference",
"link_doctype": "Mailbox",
"link_doctype": "Mail Account",
"link_fieldname": "domain_name"
},
{
"group": "Reference",
"link_doctype": "Mail Group",
"link_fieldname": "domain_name"
},
{
Expand All @@ -106,17 +111,22 @@
"link_fieldname": "domain_name"
},
{
"group": "Reference",
"group": "Mail",
"link_doctype": "Incoming Mail",
"link_fieldname": "domain_name"
},
{
"group": "Reference",
"group": "Mail",
"link_doctype": "Outgoing Mail",
"link_fieldname": "domain_name"
},
{
"group": "Report",
"link_doctype": "DMARC Report",
"link_fieldname": "domain_name"
}
],
"modified": "2025-01-05 12:07:54.486802",
"modified": "2025-01-06 15:35:35.811831",
"modified_by": "Administrator",
"module": "Mail",
"name": "Mail Domain",
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions mail/mail/doctype/mail_group/mail_group.js
Original file line number Diff line number Diff line change
@@ -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", {
// refresh(frm) {

// },
// });
94 changes: 94 additions & 0 deletions mail/mail/doctype/mail_group/mail_group.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"actions": [],
"creation": "2025-01-06 14:23:03.332903",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"section_break_uwhi",
"enabled",
"column_break_zfbl",
"domain_name",
"email",
"display_name"
],
"fields": [
{
"fieldname": "section_break_uwhi",
"fieldtype": "Section Break"
},
{
"default": "1",
"depends_on": "eval: !doc.__islocal",
"fieldname": "enabled",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Enabled",
"search_index": 1
},
{
"fieldname": "domain_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Domain Name",
"no_copy": 1,
"options": "Mail Domain",
"reqd": 1,
"search_index": 1,
"set_only_once": 1
},
{
"fieldname": "email",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Email",
"no_copy": 1,
"options": "Email",
"reqd": 1,
"set_only_once": 1,
"unique": 1
},
{
"fieldname": "column_break_zfbl",
"fieldtype": "Column Break"
},
{
"fieldname": "display_name",
"fieldtype": "Data",
"label": "Display Name"
}
],
"index_web_pages_for_search": 1,
"links": [
{
"group": "Reference",
"link_doctype": "Mail Alias",
"link_fieldname": "alias_for_name"
}
],
"modified": "2025-01-06 15:29:16.465956",
"modified_by": "Administrator",
"module": "Mail",
"name": "Mail Group",
"naming_rule": "Set by user",
"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
}
54 changes: 54 additions & 0 deletions mail/mail/doctype/mail_group/mail_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

import frappe
from frappe.model.document import Document

from mail.mail.doctype.mail_agent_job.mail_agent_job import (
create_group_on_agents,
delete_group_from_agents,
patch_group_on_agents,
)
from mail.utils.validation import (
is_valid_email_for_domain,
validate_domain_is_enabled_and_verified,
)


class MailGroup(Document):
def autoname(self) -> None:
self.email = self.email.strip().lower()
self.name = self.email

def validate(self) -> None:
self.validate_domain()
self.validate_email()
self.validate_display_name()

def on_update(self) -> None:
if self.has_value_changed("email"):
create_group_on_agents(self.email, self.display_name)
return

has_value_changed = self.has_value_changed("display_name")
if has_value_changed:
patch_group_on_agents(self.email, self.display_name)

def on_trash(self) -> None:
delete_group_from_agents(self.email)

def validate_domain(self) -> None:
"""Validates the domain."""

validate_domain_is_enabled_and_verified(self.domain_name)

def validate_email(self) -> None:
"""Validates the email address."""

is_valid_email_for_domain(self.email, self.domain_name, raise_exception=True)

def validate_display_name(self) -> None:
"""Validates the display name."""

if self.is_new() and not self.display_name:
self.display_name = frappe.db.get_value("User", self.user, "full_name")
Loading

0 comments on commit 18fe0e0

Please sign in to comment.