Commit bf37b4a0 by Bill DeRusha

Email notification

parent 56ca0e90
...@@ -2882,3 +2882,5 @@ THEME_CACHE_TIMEOUT = 30 * 60 ...@@ -2882,3 +2882,5 @@ THEME_CACHE_TIMEOUT = 30 * 60
# API access management # API access management
API_ACCESS_MANAGER_EMAIL = 'api-access@example.com' API_ACCESS_MANAGER_EMAIL = 'api-access@example.com'
API_ACCESS_FROM_EMAIL = 'api-requests@example.com' API_ACCESS_FROM_EMAIL = 'api-requests@example.com'
API_DOCUMENTATION_URL = 'http://edx.readthedocs.org/projects/edx-platform-api/en/latest/overview.html'
AUTH_DOCUMENTATION_URL = 'http://edx.readthedocs.org/projects/edx-platform-api/en/latest/authentication.html'
## mako
Dear ${name},
Your request for access to the ${platform_name} Course Catalog API has been approved. Sign in at ${api_management_url} using the same account you used to request API access to generate your API client credentials.
Notes:
- ${platform_name} recommends that you bookmark the ${api_management_url} page for easy access.
- For more information about how to use the API client credentials to authenticate your API request, see the OAuth 2.0 documentation at ${authentication_docs_url}.
- For questions about using this API, see the documentation at ${api_docs_url} or contact ${support_email_address}.
Thanks!
The ${platform_name} API team
## mako
Dear ${name},
Your request for access to the ${platform_name} Course Catalog API has been denied. If you think this is an error, or for other questions about using this API, contact ${support_email_address}.
- The ${platform_name} API team
## mako
We have received the following request to use the Course Catalog API. Go to ${approval_url} to approve the user.
Company name: ${api_request.company_name}
Company contact: ${api_request.user.username}
Company URL: ${api_request.website}
Address: ${api_request.company_address}
Reason for API usage: ${api_request.reason}
## mako
We have received the following request to use the Course Discovery API. Please go to ${approval_url} to approve the user.
Company name: ${company_name}
Company contact: ${username}
Company URL: ${url}
Address: ${company_address}
Reason for API usage: ${reason}
...@@ -14,7 +14,7 @@ from django.utils.translation import ugettext as _ ...@@ -14,7 +14,7 @@ from django.utils.translation import ugettext as _
<h2 class="api-access-request-subheading">${_("API Access")}</h2> <h2 class="api-access-request-subheading">${_("API Access")}</h2>
<p class="api-tos-body">${_("To access the APIs, you will need to create an {platform_name} user account for your application (not for personal use). This account will provide you with access to our API request page at {request_url}. On that page, you must complete the API request form including a description of your proposed uses for the APIs. Any account and registration information that you provide to {platform_name} must be accurate and up to date, and you agree to inform us promptly of any changes. {platform_name} will review your API request form and, upon approval in {platform_name}'s sole discretion, will provide you with instructions for obtaining your API shared secret and client ID.").format(platform_name=settings.PLATFORM_NAME, request_url=reverse('api-request'))}</p> <p class="api-tos-body">${_("To access the APIs, you will need to create an {platform_name} user account for your application (not for personal use). This account will provide you with access to our API request page at {request_url}. On that page, you must complete the API request form including a description of your proposed uses for the APIs. Any account and registration information that you provide to {platform_name} must be accurate and up to date, and you agree to inform us promptly of any changes. {platform_name} will review your API request form and, upon approval in {platform_name}'s sole discretion, will provide you with instructions for obtaining your API shared secret and client ID.").format(platform_name=settings.PLATFORM_NAME, request_url=reverse('api_admin:api-request'))}</p>
<h2 class="api-access-request-subheading">${_("Permissible Use")}</h2> <h2 class="api-access-request-subheading">${_("Permissible Use")}</h2>
......
...@@ -110,6 +110,9 @@ urlpatterns = ( ...@@ -110,6 +110,9 @@ urlpatterns = (
# TODO Namespace these! # TODO Namespace these!
url(r'^course_modes/', include('course_modes.urls')), url(r'^course_modes/', include('course_modes.urls')),
url(r'^verify_student/', include('verify_student.urls')), url(r'^verify_student/', include('verify_student.urls')),
# URLs for API access management
url(r'^api-admin/', include('openedx.core.djangoapps.api_admin.urls', namespace='api_admin')),
) )
urlpatterns += ( urlpatterns += (
...@@ -1000,8 +1003,3 @@ if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'): ...@@ -1000,8 +1003,3 @@ if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'):
name='submit_financial_assistance_request' name='submit_financial_assistance_request'
) )
) )
# URLs for API access management
urlpatterns += (
url(r'^api-admin/', include('openedx.core.djangoapps.api_admin.urls')),
)
...@@ -12,6 +12,8 @@ class ApiAccessRequestAdmin(admin.ModelAdmin): ...@@ -12,6 +12,8 @@ class ApiAccessRequestAdmin(admin.ModelAdmin):
list_filter = ('status',) list_filter = ('status',)
search_fields = ('user__email',) search_fields = ('user__email',)
raw_id_fields = ('user',) raw_id_fields = ('user',)
readonly_fields = ('user', 'website', 'reason', 'company_name', 'company_address', 'contacted', )
exclude = ('site',)
admin.site.register(ApiAccessConfig, ConfigurationModelAdmin) admin.site.register(ApiAccessConfig, ConfigurationModelAdmin)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sites', '0001_initial'),
('api_admin', '0003_auto_20160404_1618'),
]
operations = [
migrations.AddField(
model_name='apiaccessrequest',
name='contacted',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='apiaccessrequest',
name='site',
field=models.ForeignKey(default=1, to='sites.Site'),
preserve_default=False,
),
migrations.AddField(
model_name='historicalapiaccessrequest',
name='contacted',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='historicalapiaccessrequest',
name='site',
field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='sites.Site', null=True),
),
]
"""Models for API management.""" """Models for API management."""
import logging import logging
from smtplib import SMTPException
from urlparse import urlunsplit
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django_extensions.db.models import TimeStampedModel from django_extensions.db.models import TimeStampedModel
from edxmako.shortcuts import render_to_string
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from config_models.models import ConfigurationModel from config_models.models import ConfigurationModel
...@@ -35,6 +44,8 @@ class ApiAccessRequest(TimeStampedModel): ...@@ -35,6 +44,8 @@ class ApiAccessRequest(TimeStampedModel):
reason = models.TextField(help_text=_('The reason this user wants to access the API.')) reason = models.TextField(help_text=_('The reason this user wants to access the API.'))
company_name = models.CharField(max_length=255, default='') company_name = models.CharField(max_length=255, default='')
company_address = models.CharField(max_length=255, default='') company_address = models.CharField(max_length=255, default='')
site = models.ForeignKey(Site)
contacted = models.BooleanField(default=False)
history = HistoricalRecords() history = HistoricalRecords()
...@@ -88,3 +99,83 @@ class ApiAccessConfig(ConfigurationModel): ...@@ -88,3 +99,83 @@ class ApiAccessConfig(ConfigurationModel):
def __unicode__(self): def __unicode__(self):
return u'ApiAccessConfig [enabled={}]'.format(self.enabled) return u'ApiAccessConfig [enabled={}]'.format(self.enabled)
@receiver(post_save, sender=ApiAccessRequest, dispatch_uid="api_access_request_post_save_email")
def send_request_email(sender, instance, created, **kwargs): # pylint: disable=unused-argument
""" Send request email after new record created. """
if created:
_send_new_pending_email(instance)
@receiver(pre_save, sender=ApiAccessRequest, dispatch_uid="api_access_request_pre_save_email")
def send_decision_email(sender, instance, **kwargs): # pylint: disable=unused-argument
""" Send decision email after status changed. """
if instance.id and not instance.contacted:
old_instance = ApiAccessRequest.objects.get(pk=instance.id)
if instance.status != old_instance.status:
_send_decision_email(instance)
def _send_new_pending_email(instance):
""" Send an email to settings.API_ACCESS_MANAGER_EMAIL with the contents of this API access request. """
context = {
'approval_url': urlunsplit(
(
'https' if settings.HTTPS == 'on' else 'http',
instance.site.domain,
reverse('admin:api_admin_apiaccessrequest_change', args=(instance.id,)),
'',
'',
)
),
'api_request': instance
}
message = render_to_string('api_admin/api_access_request_email_new_request.txt', context)
try:
send_mail(
_('API access request from {company}').format(company=instance.company_name),
message,
settings.API_ACCESS_FROM_EMAIL,
[settings.API_ACCESS_MANAGER_EMAIL],
fail_silently=False
)
except SMTPException:
log.exception('Error sending API user notification email for request [%s].', instance.id)
def _send_decision_email(instance):
""" Send an email to requesting user with the decision made about their request. """
context = {
'name': instance.user.username,
'api_management_url': urlunsplit(
(
'https' if settings.HTTPS == 'on' else 'http',
instance.site.domain,
reverse('api_admin:api-status'),
'',
'',
)
),
'authentication_docs_url': settings.AUTH_DOCUMENTATION_URL,
'api_docs_url': settings.API_DOCUMENTATION_URL,
'support_email_address': settings.API_ACCESS_FROM_EMAIL,
'platform_name': settings.PLATFORM_NAME
}
message = render_to_string(
'api_admin/api_access_request_email_{status}.txt'.format(status=instance.status),
context
)
try:
send_mail(
_('API access request'),
message,
settings.API_ACCESS_FROM_EMAIL,
[instance.user.email],
fail_silently=False
)
instance.contacted = True
except SMTPException:
log.exception('Error sending API user notification email for request [%s].', instance.id)
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import factory import factory
from factory.django import DjangoModelFactory from factory.django import DjangoModelFactory
from microsite_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest from openedx.core.djangoapps.api_admin.models import ApiAccessRequest
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -12,3 +13,4 @@ class ApiAccessRequestFactory(DjangoModelFactory): ...@@ -12,3 +13,4 @@ class ApiAccessRequestFactory(DjangoModelFactory):
model = ApiAccessRequest model = ApiAccessRequest
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
site = factory.SubFactory(SiteFactory)
# pylint: disable=missing-docstring # pylint: disable=missing-docstring
from smtplib import SMTPException
import ddt import ddt
from django.conf import settings
from django.db import IntegrityError from django.db import IntegrityError
from django.test import TestCase from django.test import TestCase
import mock
import unittest
from microsite_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest, ApiAccessConfig from openedx.core.djangoapps.api_admin.models import ApiAccessRequest, ApiAccessConfig
from openedx.core.djangoapps.api_admin.models import log as model_log
from openedx.core.djangoapps.api_admin.tests.factories import ApiAccessRequestFactory from openedx.core.djangoapps.api_admin.tests.factories import ApiAccessRequestFactory
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
@ddt.ddt @ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class ApiAccessRequestTests(TestCase): class ApiAccessRequestTests(TestCase):
def setUp(self): def setUp(self):
...@@ -69,3 +77,71 @@ class ApiAccessConfigTests(TestCase): ...@@ -69,3 +77,71 @@ class ApiAccessConfigTests(TestCase):
unicode(ApiAccessConfig(enabled=False)), unicode(ApiAccessConfig(enabled=False)),
u'ApiAccessConfig [enabled=False]' u'ApiAccessConfig [enabled=False]'
) )
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class ApiAccessRequestSignalTests(TestCase):
def setUp(self):
super(ApiAccessRequestSignalTests, self).setUp()
self.user = UserFactory()
self.api_access_request = ApiAccessRequest(user=self.user, site=SiteFactory())
self.send_new_pending_email_function = 'openedx.core.djangoapps.api_admin.models._send_new_pending_email'
self.send_decision_email_function = 'openedx.core.djangoapps.api_admin.models._send_decision_email'
def test_save_signal_success_new_email(self):
""" Verify that initial save sends new email and no decision email. """
with mock.patch(self.send_new_pending_email_function) as mock_new_email:
with mock.patch(self.send_decision_email_function) as mock_decision_email:
self.api_access_request.save()
mock_new_email.assert_called_once_with(self.api_access_request)
self.assertFalse(mock_decision_email.called)
def test_save_signal_success_decision_email(self):
""" Verify that updating request status sends decision email and no new email. """
self.api_access_request.save()
with mock.patch(self.send_new_pending_email_function) as mock_new_email:
with mock.patch(self.send_decision_email_function) as mock_decision_email:
self.api_access_request.approve()
mock_decision_email.assert_called_once_with(self.api_access_request)
self.assertFalse(mock_new_email.called)
def test_save_signal_success_no_emails(self):
""" Verify that updating request status again sends no emails. """
self.api_access_request.save()
self.api_access_request.approve()
with mock.patch(self.send_new_pending_email_function) as mock_new_email:
with mock.patch(self.send_decision_email_function) as mock_decision_email:
self.api_access_request.deny()
self.assertFalse(mock_decision_email.called)
self.assertFalse(mock_new_email.called)
def test_save_signal_failure_email(self):
""" Verify that saving still functions even on email errors. """
self.assertIsNone(self.api_access_request.id)
mail_function = 'openedx.core.djangoapps.api_admin.models.send_mail'
with mock.patch(mail_function, side_effect=SMTPException):
with mock.patch.object(model_log, 'exception') as mock_model_log_exception:
self.api_access_request.save()
# Verify that initial save logs email errors properly
mock_model_log_exception.assert_called_once_with(
'Error sending API user notification email for request [%s].', self.api_access_request.id
)
# Verify object saved
self.assertIsNotNone(self.api_access_request.id)
with mock.patch(mail_function, side_effect=SMTPException):
with mock.patch.object(model_log, 'exception') as mock_model_log_exception:
self.api_access_request.approve()
# Verify that updating request status logs email errors properly
mock_model_log_exception.assert_called_once_with(
'Error sending API user notification email for request [%s].', self.api_access_request.id
)
# Verify object saved
self.assertEqual(self.api_access_request.status, ApiAccessRequest.APPROVED)
#pylint: disable=missing-docstring #pylint: disable=missing-docstring
from smtplib import SMTPException
import unittest import unittest
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
import mock
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest, ApiAccessConfig from openedx.core.djangoapps.api_admin.models import ApiAccessRequest, ApiAccessConfig
from openedx.core.djangoapps.api_admin.tests.factories import ApiAccessRequestFactory from openedx.core.djangoapps.api_admin.tests.factories import ApiAccessRequestFactory
from openedx.core.djangoapps.api_admin.tests.utils import VALID_DATA from openedx.core.djangoapps.api_admin.tests.utils import VALID_DATA
from openedx.core.djangoapps.api_admin.views import log as view_log
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -26,7 +23,7 @@ class ApiRequestViewTest(ApiAdminTest): ...@@ -26,7 +23,7 @@ class ApiRequestViewTest(ApiAdminTest):
def setUp(self): def setUp(self):
super(ApiRequestViewTest, self).setUp() super(ApiRequestViewTest, self).setUp()
self.url = reverse('api-request') self.url = reverse('api_admin:api-request')
password = 'abc123' password = 'abc123'
self.user = UserFactory(password=password) self.user = UserFactory(password=password)
self.client.login(username=self.user.username, password=password) self.client.login(username=self.user.username, password=password)
...@@ -49,14 +46,14 @@ class ApiRequestViewTest(ApiAdminTest): ...@@ -49,14 +46,14 @@ class ApiRequestViewTest(ApiAdminTest):
""" """
ApiAccessRequestFactory(user=self.user) ApiAccessRequestFactory(user=self.user)
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertRedirects(response, reverse('api-status')) self.assertRedirects(response, reverse('api_admin:api-status'))
def _assert_post_success(self, response): def _assert_post_success(self, response):
""" """
Assert that a successful POST has been made, that the response Assert that a successful POST has been made, that the response
redirects correctly, and that the correct object has been created. redirects correctly, and that the correct object has been created.
""" """
self.assertRedirects(response, reverse('api-status')) self.assertRedirects(response, reverse('api_admin:api-status'))
api_request = ApiAccessRequest.objects.get(user=self.user) api_request = ApiAccessRequest.objects.get(user=self.user)
self.assertEqual(api_request.status, ApiAccessRequest.PENDING) self.assertEqual(api_request.status, ApiAccessRequest.PENDING)
return api_request return api_request
...@@ -64,38 +61,14 @@ class ApiRequestViewTest(ApiAdminTest): ...@@ -64,38 +61,14 @@ class ApiRequestViewTest(ApiAdminTest):
def test_post_valid(self): def test_post_valid(self):
"""Verify that a logged-in user can create an API request.""" """Verify that a logged-in user can create an API request."""
self.assertFalse(ApiAccessRequest.objects.all().exists()) self.assertFalse(ApiAccessRequest.objects.all().exists())
with mock.patch('openedx.core.djangoapps.api_admin.views.send_mail') as mock_send_mail: response = self.client.post(self.url, VALID_DATA)
response = self.client.post(self.url, VALID_DATA)
mock_send_mail.assert_called_once_with(
'API access request from ' + VALID_DATA['company_name'],
mock.ANY,
settings.API_ACCESS_FROM_EMAIL,
[settings.API_ACCESS_MANAGER_EMAIL],
fail_silently=False
)
self._assert_post_success(response) self._assert_post_success(response)
def test_failed_email(self):
"""
Verify that an access request is still created if sending email
fails for some reason, and that the necessary information is
logged.
"""
mail_function = 'openedx.core.djangoapps.api_admin.views.send_mail'
with mock.patch(mail_function, side_effect=SMTPException):
with mock.patch.object(view_log, 'exception') as mock_view_log_exception:
response = self.client.post(self.url, VALID_DATA)
api_request = self._assert_post_success(response)
mock_view_log_exception.assert_called_once_with(
'Error sending API request email for request [%s].', api_request.id # pylint: disable=no-member
)
def test_post_anonymous(self): def test_post_anonymous(self):
"""Verify that users must be logged in to create an access request.""" """Verify that users must be logged in to create an access request."""
self.client.logout() self.client.logout()
with mock.patch('openedx.core.djangoapps.api_admin.views.send_mail') as mock_send_mail: response = self.client.post(self.url, VALID_DATA)
response = self.client.post(self.url, VALID_DATA)
mock_send_mail.assert_not_called()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertFalse(ApiAccessRequest.objects.all().exists()) self.assertFalse(ApiAccessRequest.objects.all().exists())
...@@ -120,7 +93,7 @@ class ApiRequestStatusViewTest(ApiAdminTest): ...@@ -120,7 +93,7 @@ class ApiRequestStatusViewTest(ApiAdminTest):
password = 'abc123' password = 'abc123'
self.user = UserFactory(password=password) self.user = UserFactory(password=password)
self.client.login(username=self.user.username, password=password) self.client.login(username=self.user.username, password=password)
self.url = reverse('api-status') self.url = reverse('api_admin:api-status')
def test_get_without_request(self): def test_get_without_request(self):
""" """
...@@ -128,7 +101,7 @@ class ApiRequestStatusViewTest(ApiAdminTest): ...@@ -128,7 +101,7 @@ class ApiRequestStatusViewTest(ApiAdminTest):
redirected to the API request form. redirected to the API request form.
""" """
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertRedirects(response, reverse('api-request')) self.assertRedirects(response, reverse('api_admin:api-request'))
def test_get_with_request(self): def test_get_with_request(self):
""" """
...@@ -157,7 +130,7 @@ class ApiTosViewTest(ApiAdminTest): ...@@ -157,7 +130,7 @@ class ApiTosViewTest(ApiAdminTest):
def test_get_api_tos(self): def test_get_api_tos(self):
"""Verify that the terms of service can be read.""" """Verify that the terms of service can be read."""
url = reverse('api-tos') url = reverse('api_admin:api-tos')
response = self.client.get(url) response = self.client.get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn('Terms of Service', response.content) self.assertIn('Terms of Service', response.content)
"""Views for API management.""" """Views for API management."""
import logging import logging
from smtplib import SMTPException
from django.conf import settings from django.conf import settings
from django.core.mail import send_mail from django.contrib.sites.shortcuts import get_current_site
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import View from django.views.generic import View
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView from django.views.generic.edit import CreateView
from edxmako.shortcuts import render_to_response, render_to_string from edxmako.shortcuts import render_to_response
from openedx.core.djangoapps.api_admin.forms import ApiAccessRequestForm from openedx.core.djangoapps.api_admin.forms import ApiAccessRequestForm
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest from openedx.core.djangoapps.api_admin.models import ApiAccessRequest
...@@ -22,7 +21,7 @@ class ApiRequestView(CreateView): ...@@ -22,7 +21,7 @@ class ApiRequestView(CreateView):
"""Form view for requesting API access.""" """Form view for requesting API access."""
form_class = ApiAccessRequestForm form_class = ApiAccessRequestForm
template_name = 'api_admin/api_access_request_form.html' template_name = 'api_admin/api_access_request_form.html'
success_url = reverse_lazy('api-status') success_url = reverse_lazy('api_admin:api-status')
def get(self, request): def get(self, request):
""" """
...@@ -30,41 +29,13 @@ class ApiRequestView(CreateView): ...@@ -30,41 +29,13 @@ class ApiRequestView(CreateView):
them to the client creation page. them to the client creation page.
""" """
if ApiAccessRequest.api_access_status(request.user) is not None: if ApiAccessRequest.api_access_status(request.user) is not None:
return redirect(reverse('api-status')) return redirect(reverse('api_admin:api-status'))
return super(ApiRequestView, self).get(request) return super(ApiRequestView, self).get(request)
def send_email(self, api_request):
"""
Send an email to settings.API_ACCESS_MANAGER_EMAIL with the
contents of this API access request.
"""
context = {
'approval_url': self.request.build_absolute_uri(
reverse('admin:api_admin_apiaccessrequest_change', args=(api_request.id,))
),
'company_name': api_request.company_name,
'username': api_request.user.username,
'url': api_request.website,
'company_address': api_request.company_address,
'reason': api_request.reason,
}
message = render_to_string('api_admin/email.txt', context)
try:
send_mail(
_('API access request from {company}').format(company=api_request.company_name),
message,
settings.API_ACCESS_FROM_EMAIL,
[settings.API_ACCESS_MANAGER_EMAIL],
fail_silently=False
)
except SMTPException:
log.exception('Error sending API request email for request [%s].', api_request.id)
def form_valid(self, form): def form_valid(self, form):
form.instance.user = self.request.user form.instance.user = self.request.user
result = super(ApiRequestView, self).form_valid(form) form.instance.site = get_current_site(self.request)
self.send_email(form.instance) return super(ApiRequestView, self).form_valid(form)
return result
class ApiRequestStatusView(View): class ApiRequestStatusView(View):
...@@ -77,7 +48,7 @@ class ApiRequestStatusView(View): ...@@ -77,7 +48,7 @@ class ApiRequestStatusView(View):
""" """
status = ApiAccessRequest.api_access_status(request.user) status = ApiAccessRequest.api_access_status(request.user)
if status is None: if status is None:
return redirect(reverse('api-request')) return redirect(reverse('api_admin:api-request'))
return render_to_response('api_admin/status.html', { return render_to_response('api_admin/status.html', {
'status': status, 'status': status,
'api_support_link': _('TODO'), 'api_support_link': _('TODO'),
......
...@@ -24,7 +24,7 @@ class TermsOfServiceCheckboxInput(CheckboxInput): ...@@ -24,7 +24,7 @@ class TermsOfServiceCheckboxInput(CheckboxInput):
# platform_name is the name of this Open edX installation. # platform_name is the name of this Open edX installation.
label = _('I, and my company, accept the {link_start}{platform_name} API Terms of Service{link_end}.').format( label = _('I, and my company, accept the {link_start}{platform_name} API Terms of Service{link_end}.').format(
platform_name=settings.PLATFORM_NAME, platform_name=settings.PLATFORM_NAME,
link_start='<a href="{url}" target="_blank">'.format(url=reverse('api-tos')), link_start='<a href="{url}" target="_blank">'.format(url=reverse('api_admin:api-tos')),
link_end='</a>', link_end='</a>',
) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment