diff --git a/doc/development/api/plugins.rst b/doc/development/api/plugins.rst
index 5b66470b73..256ceff5dd 100644
--- a/doc/development/api/plugins.rst
+++ b/doc/development/api/plugins.rst
@@ -74,6 +74,13 @@ A working example would be::
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
+The ``AppConfig`` class may implement a property ``compatiblity_errors``, that checks
+whether the pretix installation meets all requirements of the plugin. If so,
+it should contian ``None`` or an empty list, otherwise a list of strings containing
+human-readable error messages. We recommend using the ``django.utils.functional.cached_property``
+decorator, as it might get called a lot. You can also implement ``compatibility_warnings``,
+those will be displayed but not block the plugin execution.
+
Signals
-------
diff --git a/src/pretix/base/plugins.py b/src/pretix/base/plugins.py
index bbf760624b..e62909abb1 100644
--- a/src/pretix/base/plugins.py
+++ b/src/pretix/base/plugins.py
@@ -20,5 +20,6 @@ def get_all_plugins() -> "List[class]":
if hasattr(app, 'PretixPluginMeta'):
meta = app.PretixPluginMeta
meta.module = app.name
+ meta.app = app
plugins.append(meta)
return plugins
diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py
index 800d064414..28e650a081 100644
--- a/src/pretix/base/signals.py
+++ b/src/pretix/base/signals.py
@@ -39,8 +39,9 @@ class EventPluginSignal(django.dispatch.Signal):
# Only fire receivers from active plugins
if app.name in sender.get_plugins():
- response = receiver(signal=self, sender=sender, **named)
- responses.append((receiver, response))
+ if not hasattr(app, 'compatibility_errors') or not app.compatibility_errors:
+ response = receiver(signal=self, sender=sender, **named)
+ responses.append((receiver, response))
return responses
"""
diff --git a/src/pretix/control/static/pretixcontrol/less/forms.less b/src/pretix/control/static/pretixcontrol/less/forms.less
index ac55223475..1fca2e1633 100644
--- a/src/pretix/control/static/pretixcontrol/less/forms.less
+++ b/src/pretix/control/static/pretixcontrol/less/forms.less
@@ -14,6 +14,9 @@ td > .form-group > .checkbox {
.has-success .form-control {
border-color: #cccccc;
}
+.panel-body div.alert:last-child {
+ margin-bottom: 0;
+}
.form-horizontal [data-formset] .form-group {
width: 100%;
}
diff --git a/src/pretix/control/templates/pretixcontrol/event/plugins.html b/src/pretix/control/templates/pretixcontrol/event/plugins.html
index 97b54e5612..cb82f68439 100644
--- a/src/pretix/control/templates/pretixcontrol/event/plugins.html
+++ b/src/pretix/control/templates/pretixcontrol/event/plugins.html
@@ -12,18 +12,19 @@
{% endif %}
{% for plugin in plugins %}
-
+
{{ plugin.name }}
- {% if plugin.module in plugins_active %}
+ {% if plugin.app.compatibility_errors %}
+
+ {% elif plugin.module in plugins_active %}
{% else %}
-
{% endif %}
@@ -33,6 +34,26 @@
Version {{ v }} by {{ a }}
{% endblocktrans %}
{{ plugin.description }}
+ {% if plugin.app.compatibility_errors %}
+
+ {% trans "This plugin cannot be enabled for the following reasons:" %}
+
+ {% for e in plugin.app.compatibility_errors %}
+
{{ e }}
+ {% endfor %}
+
+
+ {% endif %}
+ {% if plugin.app.compatibility_warnings %}
+
+ {% trans "This plugin reports the following problems:" %}
+
+ {% for e in plugin.app.compatibility_warnings %}
+
{{ e }}
+ {% endfor %}
+
+
+ {% endif %}
{% endfor %}
diff --git a/src/pretix/plugins/banktransfer/__init__.py b/src/pretix/plugins/banktransfer/__init__.py
index a6ce5d9723..0a08759210 100644
--- a/src/pretix/plugins/banktransfer/__init__.py
+++ b/src/pretix/plugins/banktransfer/__init__.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from pretix.base.plugins import PluginType
@@ -18,5 +19,14 @@ class BankTransferApp(AppConfig):
def ready(self):
from . import signals # NOQA
+ @cached_property
+ def compatibility_warnings(self):
+ errs = []
+ try:
+ import chardet
+ except ImportError:
+ errs.append(_("Install the python package 'chardet' for better CSV import capabilities."))
+ return errs
+
default_app_config = 'pretix.plugins.banktransfer.BankTransferApp'
diff --git a/src/pretix/plugins/paypal/__init__.py b/src/pretix/plugins/paypal/__init__.py
index ee5b78be2b..5f79d58bba 100644
--- a/src/pretix/plugins/paypal/__init__.py
+++ b/src/pretix/plugins/paypal/__init__.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from pretix.base.plugins import PluginType
@@ -17,5 +18,13 @@ class PaypalApp(AppConfig):
def ready(self):
from . import signals # NOQA
+ @cached_property
+ def compatibility_errors(self):
+ errs = []
+ try:
+ import paypalrestsdk
+ except ImportError:
+ errs.append("Python package 'paypalrestsdk' is not installed.")
+ return errs
default_app_config = 'pretix.plugins.paypal.PaypalApp'
diff --git a/src/pretix/plugins/paypal/signals.py b/src/pretix/plugins/paypal/signals.py
index 28c49f2267..09b4924adc 100644
--- a/src/pretix/plugins/paypal/signals.py
+++ b/src/pretix/plugins/paypal/signals.py
@@ -2,9 +2,8 @@ from django.dispatch import receiver
from pretix.base.signals import register_payment_providers
-from .payment import Paypal
-
@receiver(register_payment_providers)
def register_payment_provider(sender, **kwargs):
+ from .payment import Paypal
return Paypal
diff --git a/src/pretix/plugins/stripe/__init__.py b/src/pretix/plugins/stripe/__init__.py
index 566aed5228..3240de1473 100644
--- a/src/pretix/plugins/stripe/__init__.py
+++ b/src/pretix/plugins/stripe/__init__.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from pretix.base.plugins import PluginType
@@ -18,5 +19,14 @@ class StripeApp(AppConfig):
def ready(self):
from . import signals # NOQA
+ @cached_property
+ def compatibility_errors(self):
+ errs = []
+ try:
+ import stripe
+ except ImportError:
+ errs.append("Python package 'stripe' is not installed.")
+ return errs
+
default_app_config = 'pretix.plugins.stripe.StripeApp'
diff --git a/src/pretix/plugins/stripe/signals.py b/src/pretix/plugins/stripe/signals.py
index 0fcbd20556..442398f132 100644
--- a/src/pretix/plugins/stripe/signals.py
+++ b/src/pretix/plugins/stripe/signals.py
@@ -5,17 +5,18 @@ from django.template.loader import get_template
from pretix.base.signals import register_payment_providers
-from .payment import Stripe
from pretix.presale.signals import html_head
@receiver(register_payment_providers)
def register_payment_provider(sender, **kwargs):
+ from .payment import Stripe
return Stripe
@receiver(html_head)
def html_head_presale(sender, request=None, **kwargs):
+ from .payment import Stripe
provider = Stripe(sender)
url = resolve(request.path_info)
if provider.is_enabled and "checkout.payment" in url.url_name: