mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[FR] Slack notifications & notification method updates (#4114)
* [FR] Slack notification Add slack sending method Fixes #3843 * Add global panel with notification methods * add plugin information * fix plugin lookup and link * Add settings content mixin * Add instructions for Slack setup * fix rendering of custom settings content
This commit is contained in:
parent
758f7886f1
commit
8b2e2a28d5
@ -192,6 +192,7 @@ class MethodStorageClass:
|
||||
for item in current_method:
|
||||
plugin = item.get_plugin(item)
|
||||
ref = f'{plugin.package_path}_{item.METHOD_NAME}' if plugin else item.METHOD_NAME
|
||||
item.plugin = plugin() if plugin else None
|
||||
filtered_list[ref] = item
|
||||
|
||||
storage.liste = list(filtered_list.values())
|
||||
|
@ -742,3 +742,24 @@ class PanelMixin:
|
||||
panels.append(panel)
|
||||
|
||||
return panels
|
||||
|
||||
|
||||
class SettingsContentMixin:
|
||||
"""Mixin which allows integration of custom HTML content into a plugins settings page.
|
||||
|
||||
The 'get_settings_content' method must return the HTML content to appear in the section
|
||||
"""
|
||||
|
||||
class MixinMeta:
|
||||
"""Meta for mixin."""
|
||||
|
||||
MIXIN_NAME = 'SettingsContent'
|
||||
|
||||
def __init__(self):
|
||||
"""Register mixin."""
|
||||
super().__init__()
|
||||
self.add_mixin('settingscontent', True, __class__)
|
||||
|
||||
def get_settings_content(self, view, request):
|
||||
"""This method *must* be implemented by the plugin class."""
|
||||
raise MixinNotImplementedError(f"{__class__} is missing the 'get_settings_content' method")
|
||||
|
@ -3,13 +3,15 @@
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import requests
|
||||
from allauth.account.models import EmailAddress
|
||||
|
||||
import common.models
|
||||
import InvenTree.helpers
|
||||
import InvenTree.tasks
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import BulkNotificationMethod, SettingsMixin
|
||||
from plugin import InvenTreePlugin, registry
|
||||
from plugin.mixins import (BulkNotificationMethod, SettingsContentMixin,
|
||||
SettingsMixin)
|
||||
|
||||
|
||||
class PlgMixin:
|
||||
@ -23,7 +25,7 @@ class PlgMixin:
|
||||
return CoreNotificationsPlugin
|
||||
|
||||
|
||||
class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
|
||||
class CoreNotificationsPlugin(SettingsContentMixin, SettingsMixin, InvenTreePlugin):
|
||||
"""Core notification methods for InvenTree."""
|
||||
|
||||
NAME = "CoreNotificationsPlugin"
|
||||
@ -39,8 +41,30 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'ENABLE_NOTIFICATION_SLACK': {
|
||||
'name': _('Enable slack notifications'),
|
||||
'description': _('Allow sending of slack channel messages for event notifications'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'NOTIFICATION_SLACK_URL': {
|
||||
'name': _('Slack incoming webhook url'),
|
||||
'description': _('URL that is used to send messages to a slack channel'),
|
||||
'protected': True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_settings_content(self, request):
|
||||
"""Custom settings content for the plugin."""
|
||||
return """
|
||||
<p>Setup for Slack:</p>
|
||||
<ol>
|
||||
<li>Create a new Slack app on <a href="https://api.slack.com/apps/new" target="_blank">this page</a></li>
|
||||
<li>Enable <i>Incoming Webhooks</i> for the channel you want the notifications posted to</li>
|
||||
<li>Set the webhook URL in the settings above</li>
|
||||
<li>Enable the plugin</li>
|
||||
"""
|
||||
|
||||
class EmailNotification(PlgMixin, BulkNotificationMethod):
|
||||
"""Notificationmethod for delivery via Email."""
|
||||
|
||||
@ -94,3 +118,53 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
|
||||
InvenTree.tasks.send_email(subject, '', targets, html_message=html_message)
|
||||
|
||||
return True
|
||||
|
||||
class SlackNotification(PlgMixin, BulkNotificationMethod):
|
||||
"""Notificationmethod for delivery via Slack channel messages."""
|
||||
|
||||
METHOD_NAME = 'slack'
|
||||
METHOD_ICON = 'fa-envelope'
|
||||
GLOBAL_SETTING = 'ENABLE_NOTIFICATION_SLACK'
|
||||
|
||||
def get_targets(self):
|
||||
"""Not used by this method."""
|
||||
return self.targets
|
||||
|
||||
def send_bulk(self):
|
||||
"""Send the notifications out via slack."""
|
||||
|
||||
instance = registry.plugins.get(self.get_plugin().NAME.lower())
|
||||
url = instance.get_setting('NOTIFICATION_SLACK_URL')
|
||||
|
||||
if not url:
|
||||
return False
|
||||
|
||||
ret = requests.post(url, json={
|
||||
'text': str(self.context['message']),
|
||||
'blocks': [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": str(self.context['name'])
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": str(self.context['message'])
|
||||
},
|
||||
"accessory": {
|
||||
"type": "button",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": str(_("Open link")), "emoji": True
|
||||
},
|
||||
"value": f'{self.category}_{self.obj.pk}',
|
||||
"url": self.context['link'],
|
||||
"action_id": "button-action"
|
||||
}
|
||||
}]
|
||||
})
|
||||
return ret.ok
|
||||
|
@ -8,8 +8,8 @@ from ..base.barcodes.mixins import BarcodeMixin
|
||||
from ..base.event.mixins import EventMixin
|
||||
from ..base.integration.mixins import (APICallMixin, AppMixin, NavigationMixin,
|
||||
PanelMixin, ScheduleMixin,
|
||||
SettingsMixin, UrlsMixin,
|
||||
ValidationMixin)
|
||||
SettingsContentMixin, SettingsMixin,
|
||||
UrlsMixin, ValidationMixin)
|
||||
from ..base.label.mixins import LabelPrintingMixin
|
||||
from ..base.locate.mixins import LocateMixin
|
||||
|
||||
@ -20,6 +20,7 @@ __all__ = [
|
||||
'LabelPrintingMixin',
|
||||
'NavigationMixin',
|
||||
'ScheduleMixin',
|
||||
'SettingsContentMixin',
|
||||
'SettingsMixin',
|
||||
'UrlsMixin',
|
||||
'PanelMixin',
|
||||
|
@ -29,6 +29,15 @@ def plugin_settings(plugin, *args, **kwargs):
|
||||
return registry.mixins_settings.get(plugin)
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def plugin_settings_content(context, plugin, *args, **kwargs):
|
||||
"""Get the settings content for the plugin."""
|
||||
plg = registry.get_plugin(plugin)
|
||||
if hasattr(plg, 'get_settings_content'):
|
||||
return plg.get_settings_content(context.request)
|
||||
return None
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def mixin_enabled(plugin, key, *args, **kwargs):
|
||||
"""Is the mixin registerd and configured in the plugin?"""
|
||||
@ -71,3 +80,16 @@ def plugin_errors(*args, **kwargs):
|
||||
def notification_settings_list(context, *args, **kwargs):
|
||||
"""List of all user notification settings."""
|
||||
return storage.get_usersettings(user=context.get('user', None))
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def notification_list(context, *args, **kwargs):
|
||||
"""List of all notification methods."""
|
||||
return [{
|
||||
'slug': a.METHOD_NAME,
|
||||
'icon': a.METHOD_ICON,
|
||||
'setting': a.GLOBAL_SETTING,
|
||||
'plugin': a.plugin,
|
||||
'description': a.__doc__,
|
||||
'name': a.__name__
|
||||
} for a in storage.liste]
|
||||
|
@ -0,0 +1,6 @@
|
||||
{% load plugin_extras %}
|
||||
|
||||
{% plugin_settings_content plugin_key as plugin_settings_content %}
|
||||
{% if plugin_settings_content %}
|
||||
{{ plugin_settings_content|safe }}
|
||||
{% endif %}
|
54
InvenTree/templates/InvenTree/settings/notifications.html
Normal file
54
InvenTree/templates/InvenTree/settings/notifications.html
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends "panel.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
{% load plugin_extras %}
|
||||
|
||||
{% block label %}global-notifications{% endblock label %}
|
||||
|
||||
{% block heading %}{% trans "Global Notification Settings" %}{% endblock heading %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class='row'>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<thead>
|
||||
<th></th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Slug" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% notification_list as methods %}
|
||||
{% for method in methods %}
|
||||
<tr>
|
||||
<td>{% if method.icon %}<span class="fas {{ method.icon }}"></span>{% endif %}</td>
|
||||
<td>
|
||||
{{ method.name }}
|
||||
{% if method.plugin %}
|
||||
<a class='sidebar-selector' id='select-plugin-{{method.plugin.slug}}' data-bs-parent="#sidebar">
|
||||
<span class='badge bg-dark badge-right rounded-pill'>{{ method.plugin.slug }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ method.slug }}</td>
|
||||
<td>{{ method.description }}</td>
|
||||
</tr>
|
||||
{% if method.setting %}
|
||||
<tr>
|
||||
<td colspan="1"></td>
|
||||
<td colspan="3">
|
||||
<table class='table table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key=method.setting plugin=method.plugin %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
@ -148,4 +148,6 @@
|
||||
{% include 'InvenTree/settings/mixins/urls.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'InvenTree/settings/mixins/settings_content.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
@ -32,6 +32,7 @@
|
||||
{% include "InvenTree/settings/global.html" %}
|
||||
{% include "InvenTree/settings/login.html" %}
|
||||
{% include "InvenTree/settings/barcode.html" %}
|
||||
{% include "InvenTree/settings/notifications.html" %}
|
||||
{% include "InvenTree/settings/label.html" %}
|
||||
{% include "InvenTree/settings/report.html" %}
|
||||
{% include "InvenTree/settings/part.html" %}
|
||||
|
@ -32,6 +32,8 @@
|
||||
{% include "sidebar_item.html" with label='login' text=text icon="fa-fingerprint" %}
|
||||
{% trans "Barcode Support" as text %}
|
||||
{% include "sidebar_item.html" with label='barcodes' text=text icon="fa-qrcode" %}
|
||||
{% trans "Notifications" as text %}
|
||||
{% include "sidebar_item.html" with label='global-notifications' text=text icon="fa-bell" %}
|
||||
{% trans "Pricing" as text %}
|
||||
{% include "sidebar_item.html" with label='pricing' text=text icon="fa-dollar-sign" %}
|
||||
{% trans "Label Printing" as text %}
|
||||
|
Loading…
Reference in New Issue
Block a user