Skip to content

Commit

Permalink
refactor: move dashboard to a separate ext
Browse files Browse the repository at this point in the history
  • Loading branch information
mutantsan committed Aug 14, 2024
1 parent e95b0fb commit bb522d7
Show file tree
Hide file tree
Showing 27 changed files with 179 additions and 91 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ The `ckanext-mailcraft` extension provides next features:
- A dashboard where you can view a list of all sent e-mails
- A function of stopping the sending of all emails sent through our mailer in order to debug the functionality.

Dashboard and config pages will be available only if you're using the `ckanext-admin-panel` extension. If not, only
mailer will be available.

To enable the extension, add `mailcraft` to the `ckan.plugins` setting in your CKAN.
If you want to enable the dashboard you should also add `mailcraft_dashboard` to the `ckan.plugins` setting.

## Usage
To use a mailer, you just have to import it.
Expand All @@ -29,6 +28,11 @@ To use a mailer, you just have to import it.
),
)

## Dashboard

The mailcraft dashboard requires the `ckanext-admin-panel` extension to be enabled. Otherwise, there most likely will be
an exception, cause we're heavily relying on the admin panel and some extra requirements of it.

## Requirements

Compatibility with core CKAN versions:
Expand Down
10 changes: 0 additions & 10 deletions ckanext/mailcraft/assets/script.js

This file was deleted.

5 changes: 0 additions & 5 deletions ckanext/mailcraft/assets/style.css

This file was deleted.

14 changes: 0 additions & 14 deletions ckanext/mailcraft/assets/webassets.yml

This file was deleted.

2 changes: 1 addition & 1 deletion ckanext/mailcraft/config_declaration.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 1
groups:
- annotation: ckanext-mailcraft
- annotation: mailcraft
options:
- key: ckanext.mailcraft.test_conn_on_startup
type: bool
Expand Down
8 changes: 7 additions & 1 deletion ckanext/mailcraft/mailer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations

from abc import ABC, abstractmethod

import logging
Expand Down Expand Up @@ -51,6 +52,7 @@ def mail_recipients(
body_html: str,
headers: Optional[dict[str, Any]] = None,
attachments: Optional[Iterable[Attachment]] = None,
to: Optional[list[str]] = None,
):
pass

Expand Down Expand Up @@ -88,16 +90,20 @@ def mail_recipients(
body_html: str,
headers: Optional[dict[str, Any]] = None,
attachments: Optional[Iterable[Attachment]] = None,
to: Optional[list[str]] = None,
):
headers = headers or {}
attachments = attachments or []

msg = EmailMessage()

msg["From"] = email_utils.formataddr((self.site_title, self.mail_from))
msg["To"] = msg["Bcc"] = ", ".join(recipients)
msg["Subject"] = subject
msg["Date"] = email_utils.formatdate(time())
if to:
msg["To"] = msg["Bcc"] = ", ".join(to)
else:
msg["To"] = msg["Bcc"] = ", ".join(recipients)

if not tk.config.get("ckan.hide_version"):
msg["X-Mailer"] = f"CKAN {tk.h.ckan_version()}"
Expand Down
55 changes: 0 additions & 55 deletions ckanext/mailcraft/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,77 +7,22 @@

import ckanext.mailcraft.config as mc_config
from ckanext.mailcraft.mailer import DefaultMailer
from ckanext.mailcraft.collection import MailCollection


@toolkit.blanket.blueprints
@toolkit.blanket.actions
@toolkit.blanket.auth_functions
@toolkit.blanket.validators
@toolkit.blanket.config_declarations
class MailcraftPlugin(plugins.SingletonPlugin):
plugins.implements(plugins.IConfigurer)
plugins.implements(plugins.IConfigurable)
plugins.implements(plugins.ISignal)

try:
from ckanext.collection.interfaces import ICollection, CollectionFactory
except ImportError:
pass
else:
plugins.implements(ICollection, inherit=True)

# ICollection

def get_collection_factories(self) -> dict[str, CollectionFactory]:
return {
"mailcraft-dashboard": MailCollection,
}

# IConfigurer

def update_config(self, config_):
toolkit.add_template_directory(config_, "templates")
toolkit.add_public_directory(config_, "public")
toolkit.add_resource("assets", "mailcraft")

# IConfigurable

def configure(self, config: CKANConfig) -> None:
if mc_config.is_startup_conn_test_enabled():
mailer = DefaultMailer()
mailer.test_conn()

# ISignal

def get_signal_subscriptions(self) -> types.SignalMapping:
return {
toolkit.signals.ckanext.signal("ap_main:collect_config_sections"): [
self.collect_config_sections_subs
],
toolkit.signals.ckanext.signal("ap_main:collect_config_schemas"): [
self.collect_config_schemas_subs
],
}

@staticmethod
def collect_config_sections_subs(sender: None):
return {
"name": "Mailcraft",
"configs": [
{
"name": "Global settings",
"blueprint": "mailcraft.config",
"info": "Global mailcraft configurations",
},
{
"name": "Dashboard",
"blueprint": "mailcraft.dashboard",
"info": "Mailcraft dashboard",
},
],
}

@staticmethod
def collect_config_schemas_subs(sender: None):
return ["ckanext.mailcraft:config_schema.yaml"]
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
GlobalAction,
)

from ckanext.mailcraft.model import Email
from ckanext.mailcraft_dashboard.model import Email


class MailCollection(ApCollection):
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
94 changes: 94 additions & 0 deletions ckanext/mailcraft_dashboard/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

import logging
from datetime import datetime
from typing import Any

from sqlalchemy import Column, DateTime, Integer, Text
from sqlalchemy.orm import Query
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.mutable import MutableDict
from typing_extensions import Self

import ckan.model as model
from ckan.plugins import toolkit as tk

from ckanext.mailcraft.types import EmailData

log = logging.getLogger(__name__)


class Email(tk.BaseModel):
__tablename__ = "mailcraft_mail"

class State:
failed = "failed"
success = "success"
stopped = "stopped"

id = Column(Integer, primary_key=True)

subject = Column(Text)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow)
sender = Column(Text)
recipient = Column(Text)
message = Column(Text)
state = Column(Text, nullable=False, default=State.success)
extras = Column("extras", MutableDict.as_mutable(JSONB))

@classmethod
def all(cls) -> list[dict[str, Any]]:
query: Query = model.Session.query.query(cls).order_by(cls.timestamp.desc())

return [mail.dictize({}) for mail in query.all()]

@classmethod
def save_mail(
cls,
email_data: EmailData,
body_html: str,
state: str,
) -> Email:
mail = cls(
subject=email_data["Subject"],
timestamp=email_data["Date"],
sender=email_data["From"],
recipient=email_data["To"],
message=body_html,
state=state,
extras=email_data,
)

model.Session.add(mail)
model.Session.commit()

return mail

def dictize(self, context):
return {
"id": self.id,
"subject": self.subject,
"timestamp": self.timestamp,
"sender": self.sender,
"recipient": self.recipient,
"message": self.message,
"state": self.state,
"extras": self.extras or {},
}

@classmethod
def clear_emails(cls) -> int:
rows_deleted = model.Session.query(cls).delete()
model.Session.commit()

return rows_deleted

@classmethod
def get(cls, mail_id: str) -> Self | None:
query: Query = model.Session.query(cls).filter(cls.id == mail_id)

return query.one_or_none()

def delete(self) -> None:
model.Session().autoflush = False
model.Session.delete(self)
66 changes: 66 additions & 0 deletions ckanext/mailcraft_dashboard/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import annotations

import ckan.types as types
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

from ckanext.collection.interfaces import ICollection, CollectionFactory

from ckanext.mailcraft_dashboard.collection import MailCollection


@toolkit.blanket.blueprints
@toolkit.blanket.actions
@toolkit.blanket.auth_functions
@toolkit.blanket.validators
@toolkit.blanket.config_declarations
class MailcraftDashboardPlugin(plugins.SingletonPlugin):
plugins.implements(plugins.IConfigurer)
plugins.implements(plugins.ISignal)
plugins.implements(ICollection, inherit=True)

# ICollection

def get_collection_factories(self) -> dict[str, CollectionFactory]:
return {
"mailcraft-dashboard": MailCollection,
}

# IConfigurer

def update_config(self, config_):
toolkit.add_template_directory(config_, "templates")

# ISignal

def get_signal_subscriptions(self) -> types.SignalMapping:
return {
toolkit.signals.ckanext.signal("ap_main:collect_config_sections"): [
self.collect_config_sections_subs
],
toolkit.signals.ckanext.signal("ap_main:collect_config_schemas"): [
self.collect_config_schemas_subs
],
}

@staticmethod
def collect_config_sections_subs(sender: None):
return {
"name": "Mailcraft",
"configs": [
{
"name": "Global settings",
"blueprint": "mailcraft.config",
"info": "Global mailcraft configurations",
},
{
"name": "Dashboard",
"blueprint": "mailcraft.dashboard",
"info": "Mailcraft dashboard",
},
],
}

@staticmethod
def collect_config_schemas_subs(sender: None):
return ["ckanext.mailcraft:config_schema.yaml"]
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = ckanext-mailcraft
version = 0.1.0
version = 0.2.0
description = Custom extandable mailer for CKAN
long_description = file: README.md
long_description_content_type = text/markdown
Expand All @@ -20,11 +20,13 @@ keywords = CKAN
packages = find:
namespace_packages = ckanext
install_requires =
ckanext-collection>=0.1.21,<1.0.0
include_package_data = True

[options.entry_points]
ckan.plugins =
mailcraft = ckanext.mailcraft.plugin:MailcraftPlugin
mailcraft_dashboard = ckanext.mailcraft_dashboard.plugin:MailcraftDashboardPlugin

babel.extractors =
ckan = ckan.lib.extract:extract_ckan
Expand Down

0 comments on commit bb522d7

Please sign in to comment.