0003-add-orange-contact-everyone-SMS-backend-9163.patch
MANIFEST.in | ||
---|---|---|
5 | 5 |
# locales |
6 | 6 |
recursive-include passerelle/locale *.po *.mo |
7 | 7 | |
8 |
include passerelle/apps/orange/orange.pem |
|
8 | 9 |
include LICENSE |
9 | 10 |
include MANIFEST.in |
10 | 11 |
include VERSION |
passerelle/apps/orange/forms.py | ||
---|---|---|
1 |
from django.utils.text import slugify |
|
2 |
from django import forms |
|
3 | ||
4 |
from .models import OrangeSMSGateway |
|
5 | ||
6 |
class OrangeSMSGatewayForm(forms.ModelForm): |
|
7 |
class Meta: |
|
8 |
model = OrangeSMSGateway |
|
9 |
exclude = ('slug', 'users') |
|
10 | ||
11 |
def save(self, commit=True): |
|
12 |
if not self.instance.slug: |
|
13 |
self.instance.slug = slugify(self.instance.title) |
|
14 |
return super(OrangeSMSGatewayForm, self).save(commit=commit) |
passerelle/apps/orange/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 |
import passerelle.sms |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('base', '0002_auto_20151009_0326'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.CreateModel( |
|
16 |
name='OrangeSMSGateway', |
|
17 |
fields=[ |
|
18 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
19 |
('title', models.CharField(max_length=50)), |
|
20 |
('slug', models.SlugField()), |
|
21 |
('description', models.TextField()), |
|
22 |
('keystore', models.FileField(help_text='Certificate and private key in PEM format', upload_to=b'orange', null=True, verbose_name='Keystore', blank=True)), |
|
23 |
('users', models.ManyToManyField(to='base.ApiUser', blank=True)), |
|
24 |
], |
|
25 |
options={ |
|
26 |
'db_table': 'sms_orange', |
|
27 |
'verbose_name': 'Orange', |
|
28 |
}, |
|
29 |
bases=(models.Model, passerelle.sms.SMSGatewayMixin), |
|
30 |
), |
|
31 |
] |
passerelle/apps/orange/models.py | ||
---|---|---|
1 |
import re |
|
2 |
import urllib |
|
3 |
import urllib2 |
|
4 |
import logging |
|
5 |
import json |
|
6 | ||
7 |
from django.utils.translation import ugettext_lazy as _ |
|
8 |
from django.db import models |
|
9 |
from django.core.urlresolvers import reverse |
|
10 | ||
11 |
from passerelle.base.models import BaseResource |
|
12 |
from passerelle.sms import SMSGatewayMixin |
|
13 | ||
14 |
from . import soap |
|
15 | ||
16 |
class OrangeError(Exception): |
|
17 |
pass |
|
18 | ||
19 | ||
20 |
class OrangeSMSGateway(BaseResource, SMSGatewayMixin): |
|
21 |
keystore = models.FileField(upload_to='orange', |
|
22 |
blank=True, null=True, |
|
23 |
verbose_name=_('Keystore'), |
|
24 |
help_text=_('Certificate and private key in PEM format')) |
|
25 |
default_country_code = '33' |
|
26 | ||
27 |
class Meta: |
|
28 |
verbose_name = 'Orange' |
|
29 |
db_table = 'sms_orange' |
|
30 | ||
31 |
@classmethod |
|
32 |
def get_verbose_name(cls): |
|
33 |
return cls._meta.verbose_name |
|
34 | ||
35 |
@classmethod |
|
36 |
def get_icon_class(cls): |
|
37 |
return 'phone' |
|
38 | ||
39 |
def get_absolute_url(self): |
|
40 |
return reverse('orange-view', kwargs={'slug': self.slug}) |
|
41 | ||
42 |
@classmethod |
|
43 |
def get_add_url(cls): |
|
44 |
return reverse('orange-add') |
|
45 | ||
46 |
def get_edit_url(self): |
|
47 |
return reverse('orange-edit', kwargs={'slug': self.slug}) |
|
48 | ||
49 |
def get_delete_url(self): |
|
50 |
return reverse('orange-delete', kwargs={'slug': self.slug}) |
|
51 | ||
52 |
def send(self, text, sender, destinations): |
|
53 |
logger = logging.getLogger('passerelle.apps.orange') |
|
54 |
"""Send a SMS using the Orange provider""" |
|
55 |
# unfortunately it lacks a batch API... |
|
56 |
destinations = self.clean_numbers(destinations, self.default_country_code) |
|
57 |
text = unicode(text).encode('utf-8') |
|
58 | ||
59 |
soap.ContactEveryoneSoap(instance=self).send_message(destinations, text) |
passerelle/apps/orange/orange.pem | ||
---|---|---|
1 |
-----BEGIN CERTIFICATE----- |
|
2 |
MIICWzCCAcQCAQAwDQYJKoZIhvcNAQEEBQAwdjELMAkGA1UEBhMCRlIxDzANBgNV |
|
3 |
BAgTBkZyYW5jZTENMAsGA1UEBxMEQ2FlbjEXMBUGA1UEChMORnJhbmNlIFRlbGVj |
|
4 |
b20xDDAKBgNVBAsTA0RQUzEMMAoGA1UEAxMDRERQMRIwEAYJKoZIhvcNAQkBFgNE |
|
5 |
SU0wHhcNMDYxMTA4MjAyNDUxWhcNMTYxMTA1MjAyNDUxWjB2MQswCQYDVQQGEwJG |
|
6 |
UjEPMA0GA1UECBMGRnJhbmNlMQ0wCwYDVQQHEwRDYWVuMRcwFQYDVQQKEw5GcmFu |
|
7 |
Y2UgVGVsZWNvbTEMMAoGA1UECxMDRFBTMQwwCgYDVQQDEwNERFAxEjAQBgkqhkiG |
|
8 |
9w0BCQEWA0RJTTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxp5nnfYr4ZsV |
|
9 |
QgIzYkz42Urc+0z49cm8JL5DQAcceIUpWYnCOSjDCEivvkYlBGEaSQhx6goLgpAk |
|
10 |
6264BhFIa9tFJBz0VCbZ5erGANNFpi1zK9nglfGMkfgQmPXFcVF+hi9ztff+WHGR |
|
11 |
SknuxzXAICG0/PfPy/LcpVC9E35IkG8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQAk |
|
12 |
plMn6da0Yu2YZ7dSP9UBrWygN3iD93Krk5H9KcJCFKXRcZsKw/871J+fFxOFxe5u |
|
13 |
l/wraMBF+oo9aMBIsrHwzPkPr6/T3+cYScJAcoP0vRqGjbhio1BvoSvH4lsfmJsF |
|
14 |
L9cgc58xgDNwztKHqggDtiFCWVEBpYk2jbMnoy7/xg== |
|
15 |
-----END CERTIFICATE----- |
passerelle/apps/orange/soap.py | ||
---|---|---|
1 |
import datetime |
|
2 |
import os.path |
|
3 | ||
4 | ||
5 |
from passerelle.soap import Soap |
|
6 |
from passerelle.xml_builder import XmlBuilder |
|
7 | ||
8 | ||
9 |
class ContactEveryoneSoap(Soap): |
|
10 |
WSDL_URL = ('https://www.api-contact-everyone.fr.orange-business.com/' |
|
11 |
'ContactEveryone/services/MultiDiffusionWS?wsdl') |
|
12 |
ORANGE_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'orange.pem') |
|
13 | ||
14 |
url = WSDL_URL |
|
15 | ||
16 |
class ProfileListBuilder(XmlBuilder): |
|
17 |
schema = ( |
|
18 |
'PROFILE_LIST', |
|
19 |
('?loop', 'recipients', |
|
20 |
('PROFILE', |
|
21 |
('DEST_NAME', 'name_{to}'), |
|
22 |
('DEST_FORENAME', 'forename_{to}'), |
|
23 |
('DEST_ID', 'ID_{to}'), |
|
24 |
('TERMINAL_GROUP', |
|
25 |
('TERMINAL', |
|
26 |
('TERMINAL_NAME', 'mobile1'), |
|
27 |
('TERMINAL_ADDR', '{to}'), |
|
28 |
('MEDIA_TYPE_GROUP', |
|
29 |
('MEDIA_TYPE', 'sms'))))))) |
|
30 |
encoding = 'latin1' |
|
31 | ||
32 |
@property |
|
33 |
def verify(self): |
|
34 |
# Do not break if certificate is not updated |
|
35 |
if datetime.datetime.now() < datetime.datetime(2016, 11, 5): |
|
36 |
return self.ORANGE_CERTIFICATE |
|
37 |
else: |
|
38 |
return False |
|
39 | ||
40 |
def send_message(self, recipients, content): |
|
41 |
client = self.get_client() |
|
42 |
message = client.factory.create('WSMessage') |
|
43 |
message.fullContenu = True |
|
44 |
message.content = content |
|
45 |
message.subject = content |
|
46 |
message.resumeContent = content |
|
47 |
message.strategy = 'sms' |
|
48 |
send_profiles = self.ProfileListBuilder().string( |
|
49 |
context={'recipients': [{'to': to} for to in recipients]}) |
|
50 |
message.sendProfiles = send_profiles |
|
51 |
# XXX: handle webfault and other problems |
|
52 |
try: |
|
53 |
client.service.sendMessage(message) |
|
54 |
except Exception, e: |
|
55 |
print e.__dict__ |
passerelle/apps/orange/urls.py | ||
---|---|---|
1 |
from django.conf.urls import patterns, url |
|
2 |
from views import * |
|
3 | ||
4 |
urlpatterns = patterns('', |
|
5 |
url(r'^(?P<slug>[\w,-]+)/$', OrangeDetailView.as_view(), name='orange-view'), |
|
6 |
url(r'^(?P<slug>[\w,-]+)/send$', OrangeSendView.as_view(), name='orange-send'), |
|
7 |
) |
|
8 | ||
9 |
management_urlpatterns = patterns('', |
|
10 |
url(r'^add$', OrangeCreateView.as_view(), name='orange-add'), |
|
11 |
url(r'^(?P<slug>[\w,-]+)/edit$', OrangeUpdateView.as_view(), name='orange-edit'), |
|
12 |
url(r'^(?P<slug>[\w,-]+)/delete$', OrangeDeleteView.as_view(), name='orange-delete'), |
|
13 |
) |
passerelle/apps/orange/views.py | ||
---|---|---|
1 |
from django.core.urlresolvers import reverse |
|
2 |
from django.views.generic.edit import CreateView, UpdateView, DeleteView |
|
3 | ||
4 |
from passerelle.base.views import ResourceView |
|
5 |
from passerelle.sms.views import SendView |
|
6 | ||
7 |
from .models import OrangeSMSGateway |
|
8 |
from .forms import OrangeSMSGatewayForm |
|
9 | ||
10 | ||
11 |
class OrangeDetailView(ResourceView): |
|
12 |
model = OrangeSMSGateway |
|
13 |
template_name = 'passerelle/manage/messages_service_view.html' |
|
14 | ||
15 | ||
16 |
class OrangeCreateView(CreateView): |
|
17 |
model = OrangeSMSGateway |
|
18 |
template_name = 'passerelle/manage/service_form.html' |
|
19 |
form_class = OrangeSMSGatewayForm |
|
20 | ||
21 | ||
22 |
class OrangeUpdateView(UpdateView): |
|
23 |
model = OrangeSMSGateway |
|
24 |
template_name = 'passerelle/manage/service_form.html' |
|
25 |
form_class = OrangeSMSGatewayForm |
|
26 | ||
27 | ||
28 |
class OrangeDeleteView(DeleteView): |
|
29 |
model = OrangeSMSGateway |
|
30 |
template_name = 'passerelle/manage/service_confirm_delete.html' |
|
31 | ||
32 |
def get_success_url(self): |
|
33 |
return reverse('manage-home') |
|
34 | ||
35 | ||
36 |
class OrangeSendView(SendView): |
|
37 |
model = OrangeSMSGateway |
passerelle/settings.py | ||
---|---|---|
111 | 111 |
'bdp', |
112 | 112 |
'base_adresse', |
113 | 113 |
'csvdatasource', |
114 |
'orange', |
|
114 | 115 |
# backoffice templates and static |
115 | 116 |
'gadjo', |
116 | 117 |
) |
... | ... | |
123 | 124 |
PASSERELLE_APP_OXYD_ENABLED = True |
124 | 125 |
PASSERELLE_APP_OVH_ENABLED = True |
125 | 126 |
PASSERELLE_APP_MOBYT_ENABLED = True |
127 |
PASSERELLE_APP_ORANGE_ENABLED = True |
|
126 | 128 | |
127 | 129 |
PASSERELLE_APP_BDP_ENABLED = False |
128 | 130 |
PASSERELLE_APP_GDC_ENABLED = False |
passerelle/urls.py | ||
---|---|---|
22 | 22 |
import concerto.urls |
23 | 23 |
import bdp.urls |
24 | 24 |
import base_adresse.urls |
25 |
import orange.urls |
|
25 | 26 | |
26 | 27 |
admin.autodiscover() |
27 | 28 | |
... | ... | |
131 | 132 |
) |
132 | 133 |
) |
133 | 134 | |
135 |
urlpatterns += required( |
|
136 |
app_enabled('orange'), |
|
137 |
patterns('', |
|
138 |
url(r'^orange/', include(orange.urls.urlpatterns)), |
|
139 |
url(r'^manage/orange/', |
|
140 |
decorated_includes(login_required, include(orange.urls.management_urlpatterns))), |
|
141 |
) |
|
142 |
) |
|
143 | ||
134 | 144 |
# add patterns from apps |
135 | 145 |
urlpatterns = register_apps_urls(urlpatterns) |
136 | 146 | |
137 |
- |