diff --git a/MANIFEST.in b/MANIFEST.in index 69b3f6a..68d76c8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ include README.rst AUTHORS.txt LICENSE -recursive-include djrill/templates *.html recursive-include djrill *.py prune djrill/tests diff --git a/README.rst b/README.rst index 4ef012b..6e0dab3 100644 --- a/README.rst +++ b/README.rst @@ -28,7 +28,6 @@ package. It includes: * Mandrill-specific extensions like tags, metadata, tracking, and MailChimp templates * Optional support for Mandrill inbound email and other webhook notifications, via Django signals -* An optional Django admin interface Djrill is released under the BSD license. It is tested against Django 1.3--1.8 (including Python 3 with Django 1.6+, and PyPy support with Django 1.5+). diff --git a/djrill/__init__.py b/djrill/__init__.py index 92b5432..3f5fb88 100644 --- a/djrill/__init__.py +++ b/djrill/__init__.py @@ -1,79 +1,10 @@ from django.conf import settings -from django.contrib.admin.sites import AdminSite -from django.utils.text import capfirst - -from djrill.exceptions import MandrillAPIError, NotSupportedByMandrillError, removed_in_djrill_2 +from djrill.exceptions import MandrillAPIError, NotSupportedByMandrillError from ._version import * + # This backend was developed against this API endpoint. # You can override in settings.py, if desired. MANDRILL_API_URL = getattr(settings, "MANDRILL_API_URL", "https://mandrillapp.com/api/1.0") - - -class DjrillAdminSite(AdminSite): - # This was originally adapted from https://github.com/jsocol/django-adminplus. - # If new versions of Django break DjrillAdminSite, it's worth checking to see - # whether django-adminplus has dealt with something similar. - - def __init__(self, *args, **kwargs): - removed_in_djrill_2( - "DjrillAdminSite will be removed in Djrill 2.0. " - "You should remove references to it from your code. " - "(All of its data is available in the Mandrill dashboard.)" - ) - super(DjrillAdminSite, self).__init__(*args, **kwargs) - - - index_template = "djrill/index.html" - custom_views = [] - custom_urls = [] - - def register_view(self, path, view, name, display_name=None): - """Add a custom admin view. - - * `path` is the path in the admin where the view will live, e.g. - http://example.com/admin/somepath - * `view` is any view function you can imagine. - * `name` is an optional pretty name for the list of custom views. If - empty, we'll guess based on view.__name__. - """ - self.custom_views.append((path, view, name, display_name)) - - def register_url(self, path, view, name): - self.custom_urls.append((path, view, name)) - - def get_urls(self): - """Add our custom views to the admin urlconf.""" - urls = super(DjrillAdminSite, self).get_urls() - try: - from django.conf.urls import include, url - except ImportError: - # Django 1.3 - #noinspection PyDeprecation - from django.conf.urls.defaults import include, url - for path, view, name, display_name in self.custom_views: - urls += [ - url(r'^%s$' % path, self.admin_view(view), name=name) - ] - for path, view, name in self.custom_urls: - urls += [ - url(r'^%s$' % path, self.admin_view(view), name=name) - ] - - return urls - - def index(self, request, extra_context=None): - """Make sure our list of custom views is on the index page.""" - if not extra_context: - extra_context = {} - custom_list = [(path, display_name if display_name else - capfirst(view.__name__)) for path, view, name, display_name in - self.custom_views] - # Sort views alphabetically. - custom_list.sort(key=lambda x: x[1]) - extra_context.update({ - 'custom_list': custom_list - }) - return super(DjrillAdminSite, self).index(request, extra_context) diff --git a/djrill/admin.py b/djrill/admin.py deleted file mode 100644 index 0ad70ee..0000000 --- a/djrill/admin.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.contrib import admin - -from djrill.views import (DjrillIndexView, DjrillSendersListView, - DjrillTagListView, - DjrillUrlListView) - -# Only try to register Djrill admin views if DjrillAdminSite -# or django-adminplus is in use -if hasattr(admin.site,'register_view'): - admin.site.register_view("djrill/senders/", DjrillSendersListView.as_view(), - "djrill_senders", "senders") - admin.site.register_view("djrill/status/", DjrillIndexView.as_view(), - "djrill_status", "status") - admin.site.register_view("djrill/tags/", DjrillTagListView.as_view(), - "djrill_tags", "tags") - admin.site.register_view("djrill/urls/", DjrillUrlListView.as_view(), - "djrill_urls", "urls") diff --git a/djrill/templates/djrill/_status.html b/djrill/templates/djrill/_status.html deleted file mode 100644 index 6796adc..0000000 --- a/djrill/templates/djrill/_status.html +++ /dev/null @@ -1,9 +0,0 @@ -
-

Tools & Info

-

Status

- {% if status %} -

Mandrill is UP

- {% else %} -

Mandrill is DOWN

- {% endif %} -
diff --git a/djrill/templates/djrill/index.html b/djrill/templates/djrill/index.html deleted file mode 100644 index 0c268a3..0000000 --- a/djrill/templates/djrill/index.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "admin/index.html" %} - -{% block sidebar %} - {{ block.super }} - - {% if custom_list %} -
- - - - {% for path, name in custom_list %} - - {% endfor %} - -
Djrill
{{ name|capfirst }}
-
- {% endif %} -{% endblock %} diff --git a/djrill/templates/djrill/senders_list.html b/djrill/templates/djrill/senders_list.html deleted file mode 100644 index df86a9d..0000000 --- a/djrill/templates/djrill/senders_list.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load admin_list i18n %} -{% load url from future %} -{% load cycle from djrill_future %} - -{% block extrastyle %} - {{ block.super }} - - {{ media.css }} - {% if not actions_on_top and not actions_on_bottom %} - - {% endif %} -{% endblock %} - -{% block extrahead %} -{{ block.super }} -{{ media.js }} -{% endblock %} - -{% block title %} Djrill Senders | {% trans "Django site admin" %}{% endblock %} - -{% block bodyclass %}change-list{% endblock %} - -{% if not is_popup %} - {% block breadcrumbs %} - - {% endblock %} -{% endif %} - -{% block coltype %}flex{% endblock %} - -{% block content %} -
- -
- {% block date_hierarchy %}{% endblock %} - - {% block filters %} - {% include "djrill/_status.html" %} - {% endblock %} - - {% block result_list %} - {% if objects %} -
- - - - {% for header in objects.0.keys %} - - {% endfor %} - - - - {% for result in objects %} - - {% for item in result.values %} - - {% endfor %} - - {% endfor %} - -
{{ header|capfirst }}
{{ item }}
-
- {% endif %} - {% endblock %} - {% block pagination %}{% endblock %} -
-
-{% endblock %} diff --git a/djrill/templates/djrill/status.html b/djrill/templates/djrill/status.html deleted file mode 100644 index 33dac6f..0000000 --- a/djrill/templates/djrill/status.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load admin_list i18n %} -{% load url from future %} -{% block extrastyle %} - {{ block.super }} - - {{ media.css }} - {% if not actions_on_top and not actions_on_bottom %} - - {% endif %} -{% endblock %} - -{% block extrahead %} -{{ block.super }} -{{ media.js }} -{% if action_form %}{% if actions_on_top or actions_on_bottom %} - -{% endif %}{% endif %} -{% endblock %} - -{% block title %} Djrill Status | {% trans "Django site admin" %}{% endblock %} - -{% block bodyclass %}change-list{% endblock %} - -{% if not is_popup %} - {% block breadcrumbs %} - - {% endblock %} -{% endif %} - -{% block coltype %}flex{% endblock %} - -{% block content %} -
- {% block object-tools %} - {% endblock %} -
- {% block search %}{% endblock %} - {% block date_hierarchy %}{% endblock %} - - {% block filters %}{% endblock %} - {% block pagination %}{% endblock %} -
- {% for term, value in status.items %} -
{{ term|capfirst }}
-
{{ value }}
- {% endfor %} -
-
-
-{% endblock %} diff --git a/djrill/templates/djrill/tags_list.html b/djrill/templates/djrill/tags_list.html deleted file mode 100644 index f6fb80f..0000000 --- a/djrill/templates/djrill/tags_list.html +++ /dev/null @@ -1,91 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load admin_list i18n %} -{% load url from future %} -{% load cycle from djrill_future %} - -{% block extrastyle %} - {{ block.super }} - - {{ media.css }} - {% if not actions_on_top and not actions_on_bottom %} - - {% endif %} -{% endblock %} - -{% block extrahead %} -{{ block.super }} -{{ media.js }} -{% endblock %} - -{% block title %} Djrill Tags | {% trans "Django site admin" %}{% endblock %} - -{% block bodyclass %}change-list{% endblock %} - -{% if not is_popup %} - {% block breadcrumbs %} - - {% endblock %} -{% endif %} - -{% block coltype %}flex{% endblock %} - -{% block content %} -
- -
- {% block search %} {% endblock %} - - {% block date_hierarchy %}{% endblock %} - - {% block filters %} - {% include "djrill/_status.html" %} - {% endblock %} - - {% block result_list %} - {% if objects %} -
- - - - - - - - - - - - - - - {% for result in objects %} - - - - - - - - - - - {% endfor %} - -
TagIDSentOpensClicksRejectsBouncesComplaints
{{ result.tag }}{{ result.id }}{{ result.sent }}{{ result.opens }}{{ result.clicks }}{{ result.rejects }}{{ result.bounces }}{{ result.complaints }}
-
- {% endif %} - {% endblock %} - {% block pagination %}{% endblock %} -
-
-{% endblock %} diff --git a/djrill/templates/djrill/urls_list.html b/djrill/templates/djrill/urls_list.html deleted file mode 100644 index 71c3e23..0000000 --- a/djrill/templates/djrill/urls_list.html +++ /dev/null @@ -1,81 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load admin_list i18n %} -{% load url from future %} -{% load cycle from djrill_future %} - -{% block extrastyle %} - {{ block.super }} - - {{ media.css }} - {% if not actions_on_top and not actions_on_bottom %} - - {% endif %} -{% endblock %} - -{% block extrahead %} -{{ block.super }} -{{ media.js }} -{% endblock %} - -{% block title %} Djrill URLs | {% trans "Django site admin" %}{% endblock %} - -{% block bodyclass %}change-list{% endblock %} - -{% if not is_popup %} - {% block breadcrumbs %} - - {% endblock %} -{% endif %} - -{% block coltype %}flex{% endblock %} - -{% block content %} -
- -
- {% block search %}{% endblock %} - - {% block date_hierarchy %}{% endblock %} - - {% block filters %} - {% include "djrill/_status.html" %} - {% endblock %} - - {% block result_list %} - {% if objects %} -
- - - - {% for header in objects.0.keys %} - - {% endfor %} - - - - {% for result in objects %} - - {% for item in result.values %} - - {% endfor %} - - {% endfor %} - -
{{ header|capfirst }}
{{ item }}
-
- {% endif %} - {% endblock %} - {% block pagination %}{% endblock %} -
-
-{% endblock %} diff --git a/djrill/templatetags/__init__.py b/djrill/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/djrill/templatetags/djrill_future.py b/djrill/templatetags/djrill_future.py deleted file mode 100644 index 0555158..0000000 --- a/djrill/templatetags/djrill_future.py +++ /dev/null @@ -1,16 +0,0 @@ -# Future templatetags library that is also backwards compatible with -# older versions of Django (so long as Djrill's code is compatible -# with the future behavior). - -from django import template - -# Django 1.8 changes autoescape behavior in cycle tag. -# Djrill has been compatible with future behavior all along. -try: - from django.templatetags.future import cycle -except ImportError: - from django.template.defaulttags import cycle - - -register = template.Library() -register.tag(cycle) diff --git a/djrill/tests/__init__.py b/djrill/tests/__init__.py index 910905a..c1b7504 100644 --- a/djrill/tests/__init__.py +++ b/djrill/tests/__init__.py @@ -1,4 +1,3 @@ -from djrill.tests.test_admin import * from djrill.tests.test_legacy import * from djrill.tests.test_mandrill_send import * from djrill.tests.test_mandrill_send_template import * diff --git a/djrill/tests/admin_urls.py b/djrill/tests/admin_urls.py deleted file mode 100644 index c8e5192..0000000 --- a/djrill/tests/admin_urls.py +++ /dev/null @@ -1,18 +0,0 @@ -try: - from django.conf.urls import include, url -except ImportError: - # Django 1.3 - from django.conf.urls.defaults import include, url - -from django.contrib import admin - -from djrill import DjrillAdminSite - -# Set up the DjrillAdminSite as suggested in the docs - -admin.site = DjrillAdminSite() -admin.autodiscover() - -urlpatterns = [ - url(r'^admin/', include(admin.site.urls)), -] diff --git a/djrill/tests/test_admin.py b/djrill/tests/test_admin.py deleted file mode 100644 index 299cdd7..0000000 --- a/djrill/tests/test_admin.py +++ /dev/null @@ -1,155 +0,0 @@ -import sys -import warnings - -from django.test import TestCase -from django.contrib.auth.models import User -from django.contrib import admin -import six - -from djrill.exceptions import RemovedInDjrill2 -from djrill.tests.mock_backend import DjrillBackendMockAPITestCase -from djrill.tests.utils import override_settings - - -def reset_admin_site(): - """Return the Django admin globals to their original state""" - admin.site = admin.AdminSite() # restore default - if 'djrill.admin' in sys.modules: - del sys.modules['djrill.admin'] # force autodiscover to re-import - - -@override_settings(ROOT_URLCONF='djrill.tests.admin_urls') -class DjrillAdminTests(DjrillBackendMockAPITestCase): - """Test the Djrill admin site""" - - @classmethod - def setUpClass(cls): - super(DjrillAdminTests, cls).setUpClass() - # Other test cases may muck with the Django admin site globals, - # so return it to the default state before loading test_admin_urls - reset_admin_site() - - def run(self, result=None): - with warnings.catch_warnings(): - # DjrillAdminSite deprecation is tested in test_legacy - warnings.filterwarnings('ignore', category=RemovedInDjrill2, - message="DjrillAdminSite will be removed in Djrill 2.0") - # We don't care that the `cycle` template tag will be removed in Django 2.0, - # because we're planning to drop the Djrill admin templates before then. - warnings.filterwarnings('ignore', category=PendingDeprecationWarning, - message="Loading the `cycle` tag from the `future` library") - # We don't care that user messaging was deprecated in Django 1.3 - # (testing artifact of our runtests.py minimal Django settings) - warnings.filterwarnings('ignore', category=DeprecationWarning, - message="The user messaging API is deprecated.") - super(DjrillAdminTests, self).run(result) - - def setUp(self): - super(DjrillAdminTests, self).setUp() - # Must be authenticated staff to access admin site... - admin = User.objects.create_user('admin', 'admin@example.com', 'secret') - admin.is_staff = True - admin.save() - self.client.login(username='admin', password='secret') - - def test_admin_senders(self): - self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['users/senders.json']) - response = self.client.get('/admin/djrill/senders/') - self.assertEqual(response.status_code, 200) - self.assertContains(response, "Senders") - self.assertContains(response, "sender.example@mandrillapp.com") - - def test_admin_status(self): - self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['users/info.json']) - response = self.client.get('/admin/djrill/status/') - self.assertEqual(response.status_code, 200) - self.assertContains(response, "Status") - self.assertContains(response, "myusername") - - def test_admin_tags(self): - self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['tags/list.json']) - response = self.client.get('/admin/djrill/tags/') - self.assertEqual(response.status_code, 200) - self.assertContains(response, "Tags") - self.assertContains(response, "example-tag") - - def test_admin_urls(self): - self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['urls/list.json']) - response = self.client.get('/admin/djrill/urls/') - self.assertEqual(response.status_code, 200) - self.assertContains(response, "URLs") - self.assertContains(response, "example.com/example-page") - - def test_admin_index(self): - """Make sure Djrill section is included in the admin index page""" - response = self.client.get('/admin/') - self.assertEqual(response.status_code, 200) - self.assertContains(response, "Djrill") - - - mock_api_content = { - 'users/senders.json': six.b(''' - [ - { - "address": "sender.example@mandrillapp.com", - "created_at": "2013-01-01 15:30:27", - "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "clicks": 42, "unique_opens": 42, "unique_clicks": 42 - } - ] - '''), - - 'users/info.json': six.b(''' - { - "username": "myusername", - "created_at": "2013-01-01 15:30:27", - "public_id": "aaabbbccc112233", - "reputation": 42, - "hourly_quota": 42, - "backlog": 42, - "stats": { - "today": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, - "last_7_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, - "last_30_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, - "last_60_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, - "last_90_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, - "all_time": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 } - } - } - '''), - - 'tags/list.json': six.b(''' - [ - { - "tag": "example-tag", - "reputation": 42, - "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, - "unsubs": 42, "opens": 42, "clicks": 42, "unique_opens": 42, "unique_clicks": 42 - } - ] - '''), - - 'urls/list.json': six.b(''' - [ - { - "url": "http://example.com/example-page", - "sent": 42, - "clicks": 42, - "unique_clicks": 42 - } - ] - '''), - } - - -class DjrillNoAdminTests(TestCase): - def test_admin_autodiscover_without_djrill(self): - """Make sure autodiscover doesn't die without DjrillAdminSite""" - reset_admin_site() - admin.autodiscover() # test: this shouldn't error diff --git a/djrill/tests/test_legacy.py b/djrill/tests/test_legacy.py index 2bcb68b..3213508 100644 --- a/djrill/tests/test_legacy.py +++ b/djrill/tests/test_legacy.py @@ -6,7 +6,7 @@ import warnings from django.core import mail from django.test import TestCase -from djrill import MandrillAPIError, NotSupportedByMandrillError, DjrillAdminSite +from djrill import MandrillAPIError, NotSupportedByMandrillError from djrill.exceptions import RemovedInDjrill2 from djrill.mail import DjrillMessage from djrill.tests.mock_backend import DjrillBackendMockAPITestCase @@ -19,12 +19,6 @@ class DjrillBackendDeprecationTests(DjrillBackendMockAPITestCase): reset_warning_registry() super(DjrillBackendDeprecationTests, self).setUp() - def test_deprecated_admin_site(self): - """Djrill 2.0 drops the custom DjrillAdminSite""" - self.assertWarnsMessage(DeprecationWarning, - "DjrillAdminSite will be removed in Djrill 2.0", - DjrillAdminSite) - def test_deprecated_json_date_encoding(self): """Djrill 2.0+ avoids a blanket JSONDateUTCEncoder""" # Djrill allows dates for send_at, so shouldn't warn: diff --git a/djrill/views.py b/djrill/views.py index 3caaec9..cb226bb 100644 --- a/djrill/views.py +++ b/djrill/views.py @@ -2,88 +2,15 @@ from base64 import b64encode import hashlib import hmac import json -from django import forms from django.conf import settings -from django.contrib import messages from django.core.exceptions import ImproperlyConfigured -from django.views.generic import TemplateView, View +from django.views.generic import View from django.http import HttpResponse from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt -import requests - -from djrill import MANDRILL_API_URL, signals -from .compat import b - - -class DjrillAdminMedia(object): - def _media(self): - js = ["js/core.js", "js/jquery.min.js", "js/jquery.init.js"] - - return forms.Media(js=["%s%s" % (settings.STATIC_URL, url) for url in js]) - media = property(_media) - - -class DjrillApiMixin(object): - """ - Simple Mixin to grab the api info from the settings file. - """ - def __init__(self): - self.api_key = getattr(settings, "MANDRILL_API_KEY", None) - self.api_url = MANDRILL_API_URL - - if not self.api_key: - raise ImproperlyConfigured( - "You have not set your mandrill api key in the settings file.") - - def get_context_data(self, **kwargs): - kwargs = super(DjrillApiMixin, self).get_context_data(**kwargs) - - status = False - req = requests.post("%s/%s" % (self.api_url, "users/ping.json"), - data={"key": self.api_key}) - if req.status_code == 200: - status = True - - kwargs.update({"status": status}) - return kwargs - - -class DjrillApiJsonObjectsMixin(object): - """ - Mixin to grab json objects from the api. - """ - api_uri = None - - def get_api_uri(self): - if self.api_uri is None: - raise NotImplementedError( - "%(cls)s is missing an api_uri. " - "Define %(cls)s.api_uri or override %(cls)s.get_api_uri()." % { - "cls": self.__class__.__name__ - }) - - def get_json_objects(self, extra_dict=None, extra_api_uri=None): - request_dict = {"key": self.api_key} - if extra_dict: - request_dict.update(extra_dict) - payload = json.dumps(request_dict) - api_uri = extra_api_uri or self.api_uri - req = requests.post("%s/%s" % (self.api_url, api_uri), - data=payload) - if req.status_code == 200: - return req.text - messages.error(self.request, self._api_error_handler(req)) - return json.dumps("error") - - def _api_error_handler(self, req): - """ - If the API returns an error, display it to the user. - """ - content = json.loads(req.text) - return "Mandrill returned a %d response: %s" % (req.status_code, - content["message"]) +from djrill import signals +from djrill.compat import b class DjrillWebhookSecretMixin(object): @@ -139,66 +66,6 @@ class DjrillWebhookSignatureMixin(object): request, *args, **kwargs) -class DjrillIndexView(DjrillApiMixin, TemplateView): - template_name = "djrill/status.html" - - def get(self, request, *args, **kwargs): - - payload = json.dumps({"key": self.api_key}) - req = requests.post("%s/users/info.json" % self.api_url, data=payload) - - return self.render_to_response({"status": json.loads(req.text)}) - - -class DjrillSendersListView(DjrillAdminMedia, DjrillApiMixin, - DjrillApiJsonObjectsMixin, TemplateView): - - api_uri = "users/senders.json" - template_name = "djrill/senders_list.html" - - def get(self, request, *args, **kwargs): - objects = self.get_json_objects() - context = self.get_context_data() - context.update({ - "objects": json.loads(objects), - "media": self.media, - }) - - return self.render_to_response(context) - - -class DjrillTagListView(DjrillAdminMedia, DjrillApiMixin, - DjrillApiJsonObjectsMixin, TemplateView): - - api_uri = "tags/list.json" - template_name = "djrill/tags_list.html" - - def get(self, request, *args, **kwargs): - objects = self.get_json_objects() - context = self.get_context_data() - context.update({ - "objects": json.loads(objects), - "media": self.media, - }) - return self.render_to_response(context) - - -class DjrillUrlListView(DjrillAdminMedia, DjrillApiMixin, - DjrillApiJsonObjectsMixin, TemplateView): - - api_uri = "urls/list.json" - template_name = "djrill/urls_list.html" - - def get(self, request, *args, **kwargs): - objects = self.get_json_objects() - context = self.get_context_data() - context.update({ - "objects": json.loads(objects), - "media": self.media - }) - return self.render_to_response(context) - - class DjrillWebhookView(DjrillWebhookSecretMixin, DjrillWebhookSignatureMixin, View): def head(self, request, *args, **kwargs): return HttpResponse() diff --git a/docs/history.rst b/docs/history.rst index 7d72a31..745b2f7 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -18,19 +18,28 @@ that will change. (Warnings appear in the console when running Django in debug mode.) -**Djrill Admin site** +Breaking Changes in Djrill 2.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Djrill 2.0 will remove the custom Djrill admin site. It duplicates -information from Mandrill's dashboard, most Djrill users are unaware -it exists, and it has caused problems tracking Django admin changes. +Removed DjrillAdminSite + Earlier versions of Djrill included a custom Django admin site. + The equivalent functionality is available in Mandrill's dashboard. -Drill 1.4 will report a DeprecationWarning when you try to load -the `DjrillAdminSite`. You should remove it from your code. + You should remove any references to DjrillAdminSite from your + :file:`urls.py`. E.g.:: -Also, if you changed Django's :setting:`INSTALLED_APPS` setting to use -`'django.contrib.admin.apps.SimpleAdminConfig'`, you may be able to -switch that back to `'django.contrib.admin'` and let Django -handle the admin.autodiscover() for you. + .. code-block:: python + + # Remove these: + from djrill import DjrillAdminSite + admin.site = DjrillAdminSite() + + Also, on Django 1.7 or later if you had switched your :setting:`INSTALLED_APPS` + (in :file:`settings.py`) to use ``'django.contrib.admin.apps.SimpleAdminConfig'`` + you *may* want to switch back to the default ``'django.contrib.admin'`` + and remove the call to ``admin.autodiscover()`` in your :file:`urls.py`. + (Do this only if you changed to SimpleAdminConfig for Djrill, and aren't + creating custom admin sites for any other Django apps you use.) **Dates in merge data and other attributes** diff --git a/docs/index.rst b/docs/index.rst index 4d7c489..91d881a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,9 +36,7 @@ Thanks ------ Thanks to the MailChimp team for asking us to build this nifty little app, and to all of Djrill's -:doc:`contributors `. Also thanks to James Socol on Github for his django-adminplus_ -library that got us off on the right foot for the custom admin views. +:doc:`contributors `. Oh, and, of course, Kenneth Reitz for the awesome requests_ library. .. _requests: http://docs.python-requests.org -.. _django-adminplus: https://github.com/jsocol/django-adminplus diff --git a/docs/installation.rst b/docs/installation.rst index 822a473..743f53b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -65,47 +65,3 @@ with :ref:`Mandrill-specific sending options `.) .. _subaccounts: http://help.mandrill.com/entries/25523278-What-are-subaccounts- - - -Admin (Optional) ----------------- - -Djrill includes an optional Django admin interface, which allows you to: - -* Check the status of your Mandrill API connection -* See stats on email senders, tags and urls - -If you want to enable the Djrill admin interface, edit your base :file:`urls.py`: - - .. code-block:: python - :emphasize-lines: 4,6 - - ... - from django.contrib import admin - - from djrill import DjrillAdminSite - - admin.site = DjrillAdminSite() - admin.autodiscover() - ... - - urlpatterns = [ - ... - url(r'^admin/', include(admin.site.urls)), - ] - -If you are on **Django 1.7 or later,** you will also need to change the config used -by the django.contrib.admin app in your :file:`settings.py`: - - .. code-block:: python - :emphasize-lines: 4 - - ... - INSTALLED_APPS = ( - # For Django 1.7+, use SimpleAdminConfig because we'll call autodiscover... - 'django.contrib.admin.apps.SimpleAdminConfig', # instead of 'django.contrib.admin' - ... - 'djrill', - ... - ) - ... diff --git a/runtests.py b/runtests.py index 9482530..9056905 100644 --- a/runtests.py +++ b/runtests.py @@ -3,13 +3,9 @@ # python runtests.py import sys -from django import VERSION as django_version from django.conf import settings APP = 'djrill' -ADMIN = 'django.contrib.admin' -if django_version >= (1, 7): - ADMIN = 'django.contrib.admin.apps.SimpleAdminConfig' settings.configure( DEBUG=True, @@ -23,7 +19,7 @@ settings.configure( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', - ADMIN, + 'django.contrib.admin', APP, ), MIDDLEWARE_CLASSES=( @@ -33,25 +29,10 @@ settings.configure( 'django.contrib.auth.middleware.AuthenticationMiddleware', ), TEMPLATES=[ - # Django 1.8 starter-project template settings - # (needed for test_admin) + # Djrill doesn't have any templates, but tests need a TEMPLATES + # setting to avoid warnings from the Django 1.8+ test client. { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - # insert your TEMPLATE_DIRS here - ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.debug', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', - ], - }, }, ], )