+
+
+{% endblock %}
+
diff --git a/eti_marketing/news/urls.py b/eti_marketing/news/urls.py
new file mode 100644
index 0000000..5c79445
--- /dev/null
+++ b/eti_marketing/news/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import url
+from . import views
+
+
+urlpatterns = [
+ url(r'^$', views.ListView.as_view(), name='news-article-list'),
+ url(r'^(?P[^\.]+)/$', views.DetailView.as_view(), name='news-article-detail'),
+]
diff --git a/eti_marketing/news/views.py b/eti_marketing/news/views.py
new file mode 100644
index 0000000..40017b9
--- /dev/null
+++ b/eti_marketing/news/views.py
@@ -0,0 +1,17 @@
+from django.views.generic import (
+ ListView as BaseListView,
+ DetailView as BaseDetailView,
+)
+
+from .models import Article
+from eti_marketing.utils import BaseTemplateMixin, PublishedQuerysetMixin
+
+
+class ListView(PublishedQuerysetMixin, BaseTemplateMixin, BaseListView):
+ model = Article
+ template_name = 'eti_marketing/news/list.html'
+
+
+class DetailView(PublishedQuerysetMixin, BaseTemplateMixin, BaseDetailView):
+ model = Article
+ template_name = 'eti_marketing/news/detail.html'
diff --git a/eti_marketing/utils.py b/eti_marketing/utils.py
index 4ed41d0..2ff39ad 100644
--- a/eti_marketing/utils.py
+++ b/eti_marketing/utils.py
@@ -2,6 +2,32 @@
def get_base_template():
+ """
+ Returns the configured base template.
+ """
return getattr(settings, 'ETI_MARKETING_BASE_TEMPLATE', 'base.html')
+class PublishedQuerysetMixin(object):
+ """
+ Mixin for views that need to be limited to "published" items.
+ """
+
+ def get_queryset(self):
+ queryset = super().get_queryset()
+
+ if not self.request.user.is_superuser:
+ queryset = queryset.published()
+
+ return queryset
+
+
+class BaseTemplateMixin(object):
+ """
+ Mixin for views that need to include the `base_template` variable.
+ """
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['base_template'] = get_base_template()
+ return context
diff --git a/tests/news/__init__.py b/tests/news/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/news/test_models.py b/tests/news/test_models.py
new file mode 100644
index 0000000..4ca46bb
--- /dev/null
+++ b/tests/news/test_models.py
@@ -0,0 +1,24 @@
+from django.test import TestCase
+from django.template.defaultfilters import truncatewords
+from eti_marketing.news.models import Article
+
+
+class ArticleTest(TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.__article = Article.objects.create(
+ title='Test Title',
+ slug='test-title',
+ columns=Article.LAYOUT_HALF,
+ published=True
+ )
+
+ def test_str(self):
+ self.assertEqual(str(self.__article), self.__article.title)
+
+ def test_short_title(self):
+ self.assertEqual(self.__article.short_title, truncatewords('Test Title', 5))
+
+ def test_get_absolute_url(self):
+ self.assertEqual(self.__article.get_absolute_url(), '/news/test-title/')
diff --git a/tests/settings.py b/tests/settings.py
index 3ae69e1..33b9745 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -4,6 +4,7 @@
INSTALLED_APPS = [
'eti_marketing',
'eti_marketing.landing_page',
+ 'eti_marketing.news',
'ckeditor',
]
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..5c68e87
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,72 @@
+from unittest import TestCase, mock
+
+from eti_marketing.utils import PublishedQuerysetMixin, BaseTemplateMixin
+
+from faker import Faker
+fake = Faker()
+
+
+class _PublishedViewParent(object):
+
+ def __init__(self):
+ self.__mock_queryset = mock.Mock()
+ self.__mock_queryset.filtered = False
+
+ def published(*args, **kwargs):
+ self.__mock_queryset.filtered = True
+ return self.__mock_queryset
+
+ self.__mock_queryset.published.side_effect = published
+
+ def get_queryset(self):
+ return self.__mock_queryset
+
+
+class _PublishedView(PublishedQuerysetMixin, _PublishedViewParent):
+
+ def __init__(self, is_superuser=False):
+ super().__init__()
+ self.request = mock.Mock()
+ self.request.user = mock.Mock()
+ self.request.user.is_superuser = is_superuser
+
+
+class PublishedQuerysetMixinTests(TestCase):
+
+ def test_limits_the_queryset_to_published_items(self):
+ queryset = _PublishedView().get_queryset()
+ self.assertTrue(queryset.filtered)
+
+ def test_bypasses_the_limit_for_superadmins(self):
+ queryset = _PublishedView(True).get_queryset()
+ self.assertFalse(queryset.filtered)
+
+
+class _BaseTemplateViewParent(object):
+
+ def get_context_data(self, **kwargs):
+ return {}
+
+
+class _BaseTemplateView(BaseTemplateMixin, _BaseTemplateViewParent):
+ pass
+
+
+class BaseTemplateMixinTests(TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.__subject = _BaseTemplateView()
+
+ @mock.patch('eti_marketing.utils.settings')
+ def test_adds_the_configured_base_template_to_the_context(self, settings):
+ template = fake.word()
+ settings.ETI_MARKETING_BASE_TEMPLATE = template
+
+ context = self.__subject.get_context_data()
+
+ self.assertEqual(template, context['base_template'])
+
+ def test_defaults_to_something_reasonable(self):
+ context = self.__subject.get_context_data()
+ self.assertEqual('base.html', context['base_template'])
diff --git a/tests/urls.py b/tests/urls.py
index 9b7d11a..6ce3a90 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -3,4 +3,5 @@
urlpatterns = [
url(r'^p/', include('eti_marketing.landing_page.urls')),
+ url(r'^news/', include('eti_marketing.news.urls')),
]
+
+ {% if article.subheader %}
+
+
+ {% endif %}
+
+ {% if article.columns == "HALF" %}
+
+
+
+ {{ article.column_1|safe }}
+
+
+ {{ article.column_2|safe }}
+
+ {% else %}
+
+ {{ article.column_1|safe }}
+
+ {% endif %}
+
+ {% if article.cta %}
+ {{ cta_html|safe }}
+ {% endif %}
+
+ {% if article.socials %}
+
+
+
+ {% if article.footer %}
+ {% include 'includes/page-footer.html' %}
+ {% endif %}
+
+{% endblock %}
diff --git a/eti_marketing/news/templates/eti_marketing/news/list.html b/eti_marketing/news/templates/eti_marketing/news/list.html
new file mode 100644
index 0000000..d88e0cf
--- /dev/null
+++ b/eti_marketing/news/templates/eti_marketing/news/list.html
@@ -0,0 +1,42 @@
+{% extends base_template %}
+{% load i18n %}
+
+{% block title %}
+ {% trans "News" %}
+{% endblock title %}
+
+{% block content %}
+
+ {% endif %}
+
+
+ {% for article in news %}
+
+
+ {% endfor %}
+
+
+
+
+ {% empty %}
+
+
+
+
+
+ {{ article.column_1|safe|truncatewords_html:20 }}
+
+