From 8436fe870de70851981b7295bd23f79b43a4dd32 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 2 Dec 2015 16:13:59 +0100 Subject: [PATCH 3/5] add orange contact everyone SMS backend (#9163) --- MANIFEST.in | 1 + passerelle/apps/orange/__init__.py | 0 passerelle/apps/orange/forms.py | 14 ++++++ passerelle/apps/orange/migrations/0001_initial.py | 31 ++++++++++++ passerelle/apps/orange/migrations/__init__.py | 0 passerelle/apps/orange/models.py | 59 +++++++++++++++++++++++ passerelle/apps/orange/orange.pem | 15 ++++++ passerelle/apps/orange/soap.py | 55 +++++++++++++++++++++ passerelle/apps/orange/urls.py | 13 +++++ passerelle/apps/orange/views.py | 37 ++++++++++++++ passerelle/settings.py | 2 + passerelle/urls.py | 10 ++++ 12 files changed, 237 insertions(+) create mode 100644 passerelle/apps/orange/__init__.py create mode 100644 passerelle/apps/orange/forms.py create mode 100644 passerelle/apps/orange/migrations/0001_initial.py create mode 100644 passerelle/apps/orange/migrations/__init__.py create mode 100644 passerelle/apps/orange/models.py create mode 100644 passerelle/apps/orange/orange.pem create mode 100644 passerelle/apps/orange/soap.py create mode 100644 passerelle/apps/orange/urls.py create mode 100644 passerelle/apps/orange/views.py diff --git a/MANIFEST.in b/MANIFEST.in index b600150..73a36f2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,7 @@ recursive-include passerelle/apps/*/static * # locales recursive-include passerelle/locale *.po *.mo +include passerelle/apps/orange/orange.pem include LICENSE include MANIFEST.in include VERSION diff --git a/passerelle/apps/orange/__init__.py b/passerelle/apps/orange/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/passerelle/apps/orange/forms.py b/passerelle/apps/orange/forms.py new file mode 100644 index 0000000..cf78909 --- /dev/null +++ b/passerelle/apps/orange/forms.py @@ -0,0 +1,14 @@ +from django.utils.text import slugify +from django import forms + +from .models import OrangeSMSGateway + +class OrangeSMSGatewayForm(forms.ModelForm): + class Meta: + model = OrangeSMSGateway + exclude = ('slug', 'users') + + def save(self, commit=True): + if not self.instance.slug: + self.instance.slug = slugify(self.instance.title) + return super(OrangeSMSGatewayForm, self).save(commit=commit) diff --git a/passerelle/apps/orange/migrations/0001_initial.py b/passerelle/apps/orange/migrations/0001_initial.py new file mode 100644 index 0000000..9061302 --- /dev/null +++ b/passerelle/apps/orange/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import passerelle.sms + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0002_auto_20151009_0326'), + ] + + operations = [ + migrations.CreateModel( + name='OrangeSMSGateway', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(max_length=50)), + ('slug', models.SlugField()), + ('description', models.TextField()), + ('keystore', models.FileField(help_text='Certificate and private key in PEM format', upload_to=b'orange', null=True, verbose_name='Keystore', blank=True)), + ('users', models.ManyToManyField(to='base.ApiUser', blank=True)), + ], + options={ + 'db_table': 'sms_orange', + 'verbose_name': 'Orange', + }, + bases=(models.Model, passerelle.sms.SMSGatewayMixin), + ), + ] diff --git a/passerelle/apps/orange/migrations/__init__.py b/passerelle/apps/orange/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/passerelle/apps/orange/models.py b/passerelle/apps/orange/models.py new file mode 100644 index 0000000..ef831eb --- /dev/null +++ b/passerelle/apps/orange/models.py @@ -0,0 +1,59 @@ +import re +import urllib +import urllib2 +import logging +import json + +from django.utils.translation import ugettext_lazy as _ +from django.db import models +from django.core.urlresolvers import reverse + +from passerelle.base.models import BaseResource +from passerelle.sms import SMSGatewayMixin + +from . import soap + +class OrangeError(Exception): + pass + + +class OrangeSMSGateway(BaseResource, SMSGatewayMixin): + keystore = models.FileField(upload_to='orange', + blank=True, null=True, + verbose_name=_('Keystore'), + help_text=_('Certificate and private key in PEM format')) + default_country_code = '33' + + class Meta: + verbose_name = 'Orange' + db_table = 'sms_orange' + + @classmethod + def get_verbose_name(cls): + return cls._meta.verbose_name + + @classmethod + def get_icon_class(cls): + return 'phone' + + def get_absolute_url(self): + return reverse('orange-view', kwargs={'slug': self.slug}) + + @classmethod + def get_add_url(cls): + return reverse('orange-add') + + def get_edit_url(self): + return reverse('orange-edit', kwargs={'slug': self.slug}) + + def get_delete_url(self): + return reverse('orange-delete', kwargs={'slug': self.slug}) + + def send(self, text, sender, destinations): + logger = logging.getLogger('passerelle.apps.orange') + """Send a SMS using the Orange provider""" + # unfortunately it lacks a batch API... + destinations = self.clean_numbers(destinations, self.default_country_code) + text = unicode(text).encode('utf-8') + + soap.ContactEveryoneSoap(instance=self).send_message(destinations, text) diff --git a/passerelle/apps/orange/orange.pem b/passerelle/apps/orange/orange.pem new file mode 100644 index 0000000..ad6eed6 --- /dev/null +++ b/passerelle/apps/orange/orange.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWzCCAcQCAQAwDQYJKoZIhvcNAQEEBQAwdjELMAkGA1UEBhMCRlIxDzANBgNV +BAgTBkZyYW5jZTENMAsGA1UEBxMEQ2FlbjEXMBUGA1UEChMORnJhbmNlIFRlbGVj +b20xDDAKBgNVBAsTA0RQUzEMMAoGA1UEAxMDRERQMRIwEAYJKoZIhvcNAQkBFgNE +SU0wHhcNMDYxMTA4MjAyNDUxWhcNMTYxMTA1MjAyNDUxWjB2MQswCQYDVQQGEwJG +UjEPMA0GA1UECBMGRnJhbmNlMQ0wCwYDVQQHEwRDYWVuMRcwFQYDVQQKEw5GcmFu +Y2UgVGVsZWNvbTEMMAoGA1UECxMDRFBTMQwwCgYDVQQDEwNERFAxEjAQBgkqhkiG +9w0BCQEWA0RJTTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxp5nnfYr4ZsV +QgIzYkz42Urc+0z49cm8JL5DQAcceIUpWYnCOSjDCEivvkYlBGEaSQhx6goLgpAk +6264BhFIa9tFJBz0VCbZ5erGANNFpi1zK9nglfGMkfgQmPXFcVF+hi9ztff+WHGR +SknuxzXAICG0/PfPy/LcpVC9E35IkG8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQAk +plMn6da0Yu2YZ7dSP9UBrWygN3iD93Krk5H9KcJCFKXRcZsKw/871J+fFxOFxe5u +l/wraMBF+oo9aMBIsrHwzPkPr6/T3+cYScJAcoP0vRqGjbhio1BvoSvH4lsfmJsF +L9cgc58xgDNwztKHqggDtiFCWVEBpYk2jbMnoy7/xg== +-----END CERTIFICATE----- diff --git a/passerelle/apps/orange/soap.py b/passerelle/apps/orange/soap.py new file mode 100644 index 0000000..0026e83 --- /dev/null +++ b/passerelle/apps/orange/soap.py @@ -0,0 +1,55 @@ +import datetime +import os.path + + +from passerelle.soap import Soap +from passerelle.xml_builder import XmlBuilder + + +class ContactEveryoneSoap(Soap): + WSDL_URL = ('https://www.api-contact-everyone.fr.orange-business.com/' + 'ContactEveryone/services/MultiDiffusionWS?wsdl') + ORANGE_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'orange.pem') + + url = WSDL_URL + + class ProfileListBuilder(XmlBuilder): + schema = ( + 'PROFILE_LIST', + ('?loop', 'recipients', + ('PROFILE', + ('DEST_NAME', 'name_{to}'), + ('DEST_FORENAME', 'forename_{to}'), + ('DEST_ID', 'ID_{to}'), + ('TERMINAL_GROUP', + ('TERMINAL', + ('TERMINAL_NAME', 'mobile1'), + ('TERMINAL_ADDR', '{to}'), + ('MEDIA_TYPE_GROUP', + ('MEDIA_TYPE', 'sms'))))))) + encoding = 'latin1' + + @property + def verify(self): + # Do not break if certificate is not updated + if datetime.datetime.now() < datetime.datetime(2016, 11, 5): + return self.ORANGE_CERTIFICATE + else: + return False + + def send_message(self, recipients, content): + client = self.get_client() + message = client.factory.create('WSMessage') + message.fullContenu = True + message.content = content + message.subject = content + message.resumeContent = content + message.strategy = 'sms' + send_profiles = self.ProfileListBuilder().string( + context={'recipients': [{'to': to} for to in recipients]}) + message.sendProfiles = send_profiles + # XXX: handle webfault and other problems + try: + client.service.sendMessage(message) + except Exception, e: + print e.__dict__ diff --git a/passerelle/apps/orange/urls.py b/passerelle/apps/orange/urls.py new file mode 100644 index 0000000..3ec725a --- /dev/null +++ b/passerelle/apps/orange/urls.py @@ -0,0 +1,13 @@ +from django.conf.urls import patterns, url +from views import * + +urlpatterns = patterns('', + url(r'^(?P[\w,-]+)/$', OrangeDetailView.as_view(), name='orange-view'), + url(r'^(?P[\w,-]+)/send$', OrangeSendView.as_view(), name='orange-send'), +) + +management_urlpatterns = patterns('', + url(r'^add$', OrangeCreateView.as_view(), name='orange-add'), + url(r'^(?P[\w,-]+)/edit$', OrangeUpdateView.as_view(), name='orange-edit'), + url(r'^(?P[\w,-]+)/delete$', OrangeDeleteView.as_view(), name='orange-delete'), +) diff --git a/passerelle/apps/orange/views.py b/passerelle/apps/orange/views.py new file mode 100644 index 0000000..78cb115 --- /dev/null +++ b/passerelle/apps/orange/views.py @@ -0,0 +1,37 @@ +from django.core.urlresolvers import reverse +from django.views.generic.edit import CreateView, UpdateView, DeleteView + +from passerelle.base.views import ResourceView +from passerelle.sms.views import SendView + +from .models import OrangeSMSGateway +from .forms import OrangeSMSGatewayForm + + +class OrangeDetailView(ResourceView): + model = OrangeSMSGateway + template_name = 'passerelle/manage/messages_service_view.html' + + +class OrangeCreateView(CreateView): + model = OrangeSMSGateway + template_name = 'passerelle/manage/service_form.html' + form_class = OrangeSMSGatewayForm + + +class OrangeUpdateView(UpdateView): + model = OrangeSMSGateway + template_name = 'passerelle/manage/service_form.html' + form_class = OrangeSMSGatewayForm + + +class OrangeDeleteView(DeleteView): + model = OrangeSMSGateway + template_name = 'passerelle/manage/service_confirm_delete.html' + + def get_success_url(self): + return reverse('manage-home') + + +class OrangeSendView(SendView): + model = OrangeSMSGateway diff --git a/passerelle/settings.py b/passerelle/settings.py index 5540376..88cd587 100644 --- a/passerelle/settings.py +++ b/passerelle/settings.py @@ -111,6 +111,7 @@ INSTALLED_APPS = ( 'bdp', 'base_adresse', 'csvdatasource', + 'orange', # backoffice templates and static 'gadjo', ) @@ -123,6 +124,7 @@ PASSERELLE_APP_CHOOSIT_ENABLED = True PASSERELLE_APP_OXYD_ENABLED = True PASSERELLE_APP_OVH_ENABLED = True PASSERELLE_APP_MOBYT_ENABLED = True +PASSERELLE_APP_ORANGE_ENABLED = True PASSERELLE_APP_BDP_ENABLED = False PASSERELLE_APP_GDC_ENABLED = False diff --git a/passerelle/urls.py b/passerelle/urls.py index 7aba345..1b71d95 100644 --- a/passerelle/urls.py +++ b/passerelle/urls.py @@ -22,6 +22,7 @@ import pastell.urls import concerto.urls import bdp.urls import base_adresse.urls +import orange.urls admin.autodiscover() @@ -131,6 +132,15 @@ urlpatterns += required( ) ) +urlpatterns += required( + app_enabled('orange'), + patterns('', + url(r'^orange/', include(orange.urls.urlpatterns)), + url(r'^manage/orange/', + decorated_includes(login_required, include(orange.urls.management_urlpatterns))), + ) +) + # add patterns from apps urlpatterns = register_apps_urls(urlpatterns) -- 2.1.4