From 8f573fd9c2ec4bb5cf96ba802a6dd024507c6105 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Fri, 7 Aug 2020 01:07:15 +0200
Subject: [PATCH] sms: add a button to test sending sms (#45333)

---
 passerelle/apps/mobyt/models.py               |  2 -
 passerelle/apps/orange/models.py              |  2 -
 passerelle/apps/ovh/models.py                 |  2 -
 passerelle/apps/oxyd/models.py                |  2 -
 passerelle/base/__init__.py                   | 19 +++++++++-
 passerelle/sms/forms.py                       |  8 ++++
 passerelle/sms/models.py                      |  2 +
 .../manage/messages_service_test_send.html    | 18 +++++++++
 .../manage/messages_service_view.html         | 13 +++++++
 passerelle/sms/urls.py                        |  8 ++++
 passerelle/sms/views.py                       | 38 +++++++++++++++++++
 .../manage/messages_service_view.html         | 12 ------
 12 files changed, 105 insertions(+), 21 deletions(-)
 create mode 100644 passerelle/sms/forms.py
 create mode 100644 passerelle/sms/templates/passerelle/manage/messages_service_test_send.html
 create mode 100644 passerelle/sms/templates/passerelle/manage/messages_service_view.html
 create mode 100644 passerelle/sms/urls.py
 create mode 100644 passerelle/sms/views.py
 delete mode 100644 passerelle/templates/passerelle/manage/messages_service_view.html

diff --git a/passerelle/apps/mobyt/models.py b/passerelle/apps/mobyt/models.py
index 93b9bf78..70dc072e 100644
--- a/passerelle/apps/mobyt/models.py
+++ b/passerelle/apps/mobyt/models.py
@@ -14,18 +14,16 @@ class MobytSMSGateway(SMSResource):
         ('ll', _('sms low-cost')),
         ('n', _('sms top')),
     )
     username = models.CharField(verbose_name=_('Username'), max_length=64)
     password = models.CharField(verbose_name=_('Password'), max_length=64)
     quality = models.CharField(max_length=4, choices=MESSAGES_QUALITIES, default='l',
                                verbose_name=_('Message quality'))
 
-    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
-
     TEST_DEFAULTS = {
         'create_kwargs': {
             'username': 'john',
             'password': 'doe',
         },
         'test_vectors': [
             {
                 'response': '',
diff --git a/passerelle/apps/orange/models.py b/passerelle/apps/orange/models.py
index fb6da58c..614558dc 100644
--- a/passerelle/apps/orange/models.py
+++ b/passerelle/apps/orange/models.py
@@ -43,18 +43,16 @@ def get_json(response):
         raise OrangeError('Orange returned Invalid JSON content: %s' % response.text)
 
 
 class OrangeSMSGateway(SMSResource):
     username = models.CharField(verbose_name=_('Identifier'), max_length=64)
     password = models.CharField(verbose_name=_('Password'), max_length=64)
     groupname = models.CharField(verbose_name=_('Group'), max_length=64)
 
-    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
-
     class Meta:
         verbose_name = _('Orange')
         db_table = 'sms_orange'
 
     def get_access_token(self):
         headers = {'content-type': 'application/x-www-form-urlencoded'}
         params = {'username': self.username, 'password': self.password}
         response = self.requests.post(URL_TOKEN, data=params, headers=headers)
diff --git a/passerelle/apps/ovh/models.py b/passerelle/apps/ovh/models.py
index 6a2f70a5..eb4ad7f7 100644
--- a/passerelle/apps/ovh/models.py
+++ b/passerelle/apps/ovh/models.py
@@ -65,18 +65,16 @@ class OVHSMSGateway(SMSResource):
         ),
     )
     msg_class = models.IntegerField(choices=MESSAGES_CLASSES, default=1,
                                     verbose_name=_('Message class'))
     credit_threshold_alert = models.PositiveIntegerField(verbose_name=_('Credit alert threshold'),
                                                          default=100)
     credit_left = models.PositiveIntegerField(verbose_name=_('Credit left'), default=0)
 
-    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
-
     TEST_DEFAULTS = {
         'create_kwargs': {
             'account': '1234',
             'username': 'john',
             'password': 'doe',
         },
         'test_vectors': [
             {
diff --git a/passerelle/apps/oxyd/models.py b/passerelle/apps/oxyd/models.py
index 69babc9f..46cb6afa 100644
--- a/passerelle/apps/oxyd/models.py
+++ b/passerelle/apps/oxyd/models.py
@@ -7,18 +7,16 @@ from django.utils.translation import ugettext_lazy as _
 from passerelle.utils.jsonresponse import APIError
 from passerelle.sms.models import SMSResource
 
 
 class OxydSMSGateway(SMSResource):
     username = models.CharField(verbose_name=_('Username'), max_length=64)
     password = models.CharField(verbose_name=_('Password'), max_length=64)
 
-    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
-
     TEST_DEFAULTS = {
         'create_kwargs': {
             'username': 'john',
             'password': 'doe',
         },
         'test_vectors': [
             {
                 'response': '',
diff --git a/passerelle/base/__init__.py b/passerelle/base/__init__.py
index 9982d5cc..4506e0a7 100644
--- a/passerelle/base/__init__.py
+++ b/passerelle/base/__init__.py
@@ -34,38 +34,55 @@ class ConnectorAppMixin(object):
         except ImportError:
             return None
 
 
 class ConnectorAppConfig(ConnectorAppMixin, django.apps.AppConfig):
     pass
 
 
+class ConnectorSMSMixin(ConnectorAppMixin):
+    def get_management_urls(self):
+        urlpatterns = super(ConnectorSMSMixin, self).get_management_urls() or []
+        urlpatterns += import_string('passerelle.sms.urls.management_urlpatterns')
+        return urlpatterns
+
+class ConnectorSMSConfig(ConnectorSMSMixin, django.apps.AppConfig):
+    pass
+
+
 class AppConfig(django.apps.AppConfig):
     name = 'passerelle.base'
 
     def ready(self):
         # once all applications are ready, go through them and mark them as
         # connectors if they have a get_connector_model() method or a model
         # that inherits from BaseResource.
         from .models import BaseResource
+        from passerelle.sms.models import SMSResource
         for app in apps.get_app_configs():
             connector_model = None
             if hasattr(app, 'get_connector_model'):
                 connector_model = app.get_connector_model()
             else:
                 for model in app.get_models():
                     if issubclass(model, BaseResource):
                         connector_model = model
                         app._connector_model = model
+                        if issubclass(model, SMSResource):
+                            app._is_sms_connector = True
                         break
+
             if not connector_model:
                 continue
             if app.__class__ is django.apps.AppConfig:
                 # switch class if it's an application without a custom
                 # appconfig.
-                app.__class__ = ConnectorAppConfig
+                if getattr(app, "_is_sms_connector", False):
+                    app.__class__ = ConnectorSMSConfig
+                else:
+                    app.__class__ = ConnectorAppConfig
             else:
                 # add mixin to base classes if it's an application with a
                 # custom appconfig.
                 app.__class__.__bases__ = (ConnectorAppMixin,) + app.__class__.__bases__
 
 default_app_config = 'passerelle.base.AppConfig'
diff --git a/passerelle/sms/forms.py b/passerelle/sms/forms.py
new file mode 100644
index 00000000..f0baa355
--- /dev/null
+++ b/passerelle/sms/forms.py
@@ -0,0 +1,8 @@
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+
+
+class SmsTestSendForm(forms.Form):
+    number = forms.CharField(label=_('To'), max_length=12)
+    sender = forms.CharField(label=_('From'), max_length=12)
+    message = forms.CharField(label=_('Message'), max_length=128)
diff --git a/passerelle/sms/models.py b/passerelle/sms/models.py
index 7651d98d..8afd2349 100644
--- a/passerelle/sms/models.py
+++ b/passerelle/sms/models.py
@@ -34,16 +34,18 @@ class SMSResource(BaseResource):
 
     default_country_code = models.CharField(verbose_name=_('Default country code'), max_length=3,
                                             default=u'33')
     default_trunk_prefix = models.CharField(verbose_name=_('Default trunk prefix'), max_length=2,
                                             default=u'0')  # Yeah France first !
     # FIXME: add regexp field, to check destination and from format
     max_message_length = models.IntegerField(_('Maximum message length'), default=160)
 
+    manager_view_template_name = 'passerelle/manage/messages_service_view.html'
+
     def clean_numbers(self, destinations):
         numbers = []
         for dest in destinations:
             # most gateways needs the number prefixed by the country code, this is
             # really unfortunate.
             dest = dest.strip()
             number = ''.join(re.findall('[0-9]', dest))
             if dest.startswith('+'):
diff --git a/passerelle/sms/templates/passerelle/manage/messages_service_test_send.html b/passerelle/sms/templates/passerelle/manage/messages_service_test_send.html
new file mode 100644
index 00000000..95fa2d37
--- /dev/null
+++ b/passerelle/sms/templates/passerelle/manage/messages_service_test_send.html
@@ -0,0 +1,18 @@
+{% extends "passerelle/base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+<h2>{% trans "Test Nico" %}</h2>
+{% endblock %}
+
+{% block content %}
+
+<form method="post" enctype="multipart/form-data">
+  {% csrf_token %}
+  {{ form.as_p }}
+  <div class="buttons">
+    <button>{% trans "Send SMS" %}</button>
+    <a class="cancel" href="{% url 'manage-home' %}">{% trans 'Cancel' %}</a>
+  </div>
+</form>
+{% endblock %}
diff --git a/passerelle/sms/templates/passerelle/manage/messages_service_view.html b/passerelle/sms/templates/passerelle/manage/messages_service_view.html
new file mode 100644
index 00000000..1c21acc6
--- /dev/null
+++ b/passerelle/sms/templates/passerelle/manage/messages_service_view.html
@@ -0,0 +1,13 @@
+{% extends "passerelle/manage/service_view.html" %}
+{% load i18n passerelle %}
+
+{% block endpoints %}
+{{ block.super }}
+<div>
+  <a rel="popup" href="/manage{{ object.get_absolute_url }}test-send/">
+    {% trans 'Send a test message' %}
+  </a>
+</div>
+{% endblock %}
+
+{% debug %}
diff --git a/passerelle/sms/urls.py b/passerelle/sms/urls.py
new file mode 100644
index 00000000..c58df515
--- /dev/null
+++ b/passerelle/sms/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import url
+
+from . import views
+
+management_urlpatterns = [
+    url(r'^(?P<slug>[\w,-]+)/test-send/$',
+        views.SmsTestSendView.as_view(), name='sms-test-send'),
+]
diff --git a/passerelle/sms/views.py b/passerelle/sms/views.py
new file mode 100644
index 00000000..b7fa49bb
--- /dev/null
+++ b/passerelle/sms/views.py
@@ -0,0 +1,38 @@
+from django.apps import apps
+from django.contrib import messages
+from django.utils.translation import ugettext_lazy as _
+from django.views.generic import FormView
+
+from passerelle.utils.jsonresponse import APIError
+
+from .forms import SmsTestSendForm
+
+
+class SmsTestSendView(FormView):
+    form_class = SmsTestSendForm
+    template_name = 'passerelle/manage/messages_service_test_send.html'
+
+    def get_instance(self):
+        name = self.kwargs['connector']
+        slug = self.kwargs['slug']
+        model_name = '%sSMSGateway' % name.capitalize()
+        model = apps.get_model(name, model_name)
+        return model.objects.get(slug=slug)
+
+    def get_success_url(self):
+        instance = self.get_instance()
+        return instance.get_absolute_url()
+
+    def form_valid(self, form):
+        instance = self.get_instance()
+        number = form.cleaned_data['number']
+        sender = form.cleaned_data['sender']
+        message = form.cleaned_data['message']
+        try:
+            number = instance.clean_numbers([number])[0]
+            instance.send_msg(text=message, sender=sender, destinations=[number])
+        except APIError as exc:
+            messages.error(self.request, _('Sending SMS fails: %s' % exc))
+        else:
+            messages.success(self.request, _('An SMS was just sent'))
+        return super(SmsTestSendView, self).form_valid(form)
diff --git a/passerelle/templates/passerelle/manage/messages_service_view.html b/passerelle/templates/passerelle/manage/messages_service_view.html
deleted file mode 100644
index 9c4113dc..00000000
--- a/passerelle/templates/passerelle/manage/messages_service_view.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "passerelle/manage/service_view.html" %}
-{% load i18n passerelle %}
-
-{% block description %}
-{% if object.description %}{{object.description|linebreaks}}{% endif %}
-{% endblock %}
-
-{% block endpoints %}
-<ul>
- <li>{% trans 'Sending a message:' %} <a href="send" >{{ site_base_uri }}{{ object.get_absolute_url }}send</a> (POST)</li>
-</ul>
-{% endblock %}
-- 
2.27.0

