Skip to content
This repository
  • 2 commits
  • 29 files changed
  • 0 comments
  • 1 contributor

Showing 29 changed files with 517 additions and 172 deletions. Show diff stats Hide diff stats

  1. 3  src/cms/apps/historylinks/__init__.py
  2. 1  src/cms/apps/historylinks/management/__init__.py
  3. 1  src/cms/apps/historylinks/management/commands/__init__.py
  4. 25  src/cms/apps/historylinks/management/commands/createinitialhistorylinks.py
  5. 25  src/cms/apps/historylinks/middleware.py
  6. 61  src/cms/apps/historylinks/models.py
  7. 45  src/cms/apps/historylinks/registration.py
  8. 0  src/cms/apps/news/__init__.py
  9. 64  src/cms/apps/news/admin.py
  10. 150  src/cms/apps/news/models.py
  11. BIN  src/cms/apps/news/static/news/img/news-feed.png
  12. 11  src/cms/apps/news/templates/news/article_archive.html
  13. 14  src/cms/apps/news/templates/news/article_archive_day.html
  14. 14  src/cms/apps/news/templates/news/article_archive_month.html
  15. 14  src/cms/apps/news/templates/news/article_archive_year.html
  16. 11  src/cms/apps/news/templates/news/article_category_archive.html
  17. 21  src/cms/apps/news/templates/news/article_detail.html
  18. 1  src/cms/apps/news/templates/news/base.html
  19. 17  src/cms/apps/news/templates/news/includes/article_list.html
  20. 19  src/cms/apps/news/templates/news/includes/article_list_item.html
  21. 1  src/cms/apps/news/templatetags/__init__.py
  22. 36  src/cms/apps/news/templatetags/news.py
  23. 22  src/cms/apps/news/urls.py
  24. 82  src/cms/apps/news/views.py
  25. 11  src/cms/apps/pages/models.py
  26. 4  src/cms/project_template/project_name/settings/production.py
  27. 4  src/cms/project_template/project_name/templates/base.html
  28. 17  src/cms/templatetags/html.py
  29. 15  src/cms/views.py
3  src/cms/apps/historylinks/__init__.py
... ...
@@ -1,3 +0,0 @@
1  src/cms/apps/historylinks/management/__init__.py
... ...
@@ -1 +0,0 @@
1  src/cms/apps/historylinks/management/commands/__init__.py
... ...
@@ -1 +0,0 @@
25  src/cms/apps/historylinks/management/commands/createinitialhistorylinks.py
... ...
@@ -1,25 +0,0 @@
25  src/cms/apps/historylinks/middleware.py
... ...
@@ -1,25 +0,0 @@
61  src/cms/apps/historylinks/models.py
... ...
@@ -1,61 +0,0 @@
45  src/cms/apps/historylinks/registration.py
... ...
@@ -1,45 +0,0 @@
0  src/cms/apps/news/__init__.py
No changes.
64  src/cms/apps/news/admin.py
... ...
@@ -0,0 +1,64 @@
  1
+"""Admin settings for the CMS news app."""
  2
+
  3
+from django.contrib import admin
  4
+
  5
+from cms.admin import PageBaseAdmin
  6
+from cms.apps.news.models import Category, Article
  7
+
  8
+
  9
+class CategoryAdmin(PageBaseAdmin):
  10
+    
  11
+    """Admin settings for the Category model."""
  12
+    
  13
+    fieldsets = (
  14
+        PageBaseAdmin.TITLE_FIELDS,
  15
+        ("Content", {
  16
+            "fields": ("content_primary",),
  17
+        }),
  18
+        PageBaseAdmin.PUBLICATION_FIELDS,
  19
+        PageBaseAdmin.NAVIGATION_FIELDS,
  20
+        PageBaseAdmin.SEO_FIELDS,
  21
+    )
  22
+
  23
+
  24
+admin.site.register(Category, CategoryAdmin)
  25
+
  26
+
  27
+class ArticleAdmin(PageBaseAdmin):
  28
+    
  29
+    """Admin settings for the Article model."""
  30
+    
  31
+    date_hierarchy = "date"
  32
+    
  33
+    list_display = ("title", "date", "is_online", "get_date_modified",)
  34
+    
  35
+    list_filter = ("is_online", "categories",)
  36
+    
  37
+    fieldsets = (
  38
+        (None, {
  39
+            "fields": ("title", "url_title", "news_feed", "date",),
  40
+        }),
  41
+        ("Content", {
  42
+            "fields": ("image", "content", "summary",),
  43
+        }),
  44
+        ("Publication", {
  45
+            "fields": ("categories", "authors", "is_online",),
  46
+            "classes": ("collapse",),
  47
+        }),
  48
+        PageBaseAdmin.NAVIGATION_FIELDS,
  49
+        PageBaseAdmin.SEO_FIELDS,
  50
+    )
  51
+    
  52
+    raw_id_fields = ("image",)
  53
+    
  54
+    filter_horizontal = ("categories", "authors",)
  55
+    
  56
+    def save_related(self, request, form, formsets, change):
  57
+        """Saves the author of the article."""
  58
+        super(ArticleAdmin, self).save_related(request, form, formsets, change)
  59
+        # For new articles, add in the current author.
  60
+        if not change and not form.cleaned_data["authors"]:
  61
+            form.instance.authors.add(request.user)
  62
+    
  63
+    
  64
+admin.site.register(Article, ArticleAdmin)
150  src/cms/apps/news/models.py
... ...
@@ -0,0 +1,150 @@
  1
+"""Models used by the CMS news app."""
  2
+
  3
+from django.contrib.auth.models import User
  4
+from django.utils import timezone
  5
+from django.db import models
  6
+
  7
+import historylinks
  8
+
  9
+from cms import sitemaps
  10
+from cms.apps.media.models import ImageRefField
  11
+from cms.apps.pages.models import ContentBase, Page
  12
+from cms.models import PageBase, OnlineBaseManager, HtmlField
  13
+
  14
+
  15
+class NewsFeed(ContentBase):
  16
+    
  17
+    """A stream of news articles."""
  18
+    
  19
+    icon = "news/img/news-feed.png"
  20
+
  21
+    # The heading that the admin places this content under.
  22
+    classifier = "syndication"
  23
+
  24
+    # The urlconf used to power this content's views.
  25
+    urlconf = "cms.apps.news.urls"
  26
+    
  27
+    content_primary = HtmlField(
  28
+        "primary content",
  29
+        blank = True
  30
+    )
  31
+    
  32
+    
  33
+def get_default_news_feed():
  34
+    """Returns the default news feed for the site."""
  35
+    try:
  36
+        return NewsFeed.objects.order_by("page__left")[0]
  37
+    except IndexError:
  38
+        return None
  39
+
  40
+
  41
+class Category(PageBase):
  42
+    
  43
+    """A category for news articles."""
  44
+    
  45
+    content_primary = HtmlField(
  46
+        "primary content",
  47
+        blank = True
  48
+    )
  49
+    
  50
+    def get_permalinks(self):
  51
+        """Returns a dictionary of all permalinks for the given category."""
  52
+        pages = Page.objects.filter(
  53
+            id__in = Article.objects.filter(
  54
+                categories = self
  55
+            ).values_list("news_feed_id", flat=True)
  56
+        )
  57
+        return dict(
  58
+            (u"page_{id}".format(id=page.id), page.reverse("article_category_archive", kwargs={
  59
+                "url_title": self.url_title,
  60
+            }))
  61
+            for page in pages
  62
+        )
  63
+    
  64
+    class Meta:
  65
+        verbose_name_plural = "categories"
  66
+        unique_together = (("url_title",),)
  67
+        ordering = ("title",)
  68
+        
  69
+
  70
+class CategoryHistoryLinkAdapter(historylinks.HistoryLinkAdapter):
  71
+    
  72
+    """History link adapter for category models."""
  73
+    
  74
+    def get_permalinks(self, obj):
  75
+        """Returns all permalinks for the given category."""
  76
+        return obj.get_permalinks()
  77
+
  78
+        
  79
+historylinks.register(Category, CategoryHistoryLinkAdapter)
  80
+
  81
+
  82
+class ArticleManager(OnlineBaseManager):
  83
+    
  84
+    """Manager for Article models."""
  85
+    
  86
+    def select_published(self, queryset):
  87
+        queryset = super(ArticleManager, self).select_published(queryset)
  88
+        queryset = queryset.filter(
  89
+            date__lte = timezone.now(),
  90
+        )
  91
+        return queryset
  92
+
  93
+
  94
+class Article(PageBase):
  95
+    
  96
+    """A news article."""
  97
+    
  98
+    objects = ArticleManager()
  99
+    
  100
+    news_feed = models.ForeignKey(
  101
+        NewsFeed,
  102
+        default = get_default_news_feed,
  103
+    )
  104
+    
  105
+    date = models.DateField(
  106
+        db_index = True,
  107
+        default = timezone.now,
  108
+    )
  109
+    
  110
+    image = ImageRefField(
  111
+        blank = True,
  112
+        null = True,
  113
+    )
  114
+    
  115
+    content = HtmlField(
  116
+        blank = True,
  117
+    )
  118
+    
  119
+    summary = HtmlField(
  120
+        blank = True,
  121
+    )
  122
+    
  123
+    categories = models.ManyToManyField(
  124
+        Category,
  125
+        blank = True,
  126
+    )
  127
+    
  128
+    authors = models.ManyToManyField(
  129
+        User,
  130
+        blank = True,
  131
+    )
  132
+    
  133
+    def get_absolute_url(self):
  134
+        """Returns the URL of the article."""
  135
+        return self.news_feed.page.reverse("article_detail", kwargs={
  136
+            "year": self.date.year,
  137
+            "month": self.date.strftime("%b").lower(),
  138
+            "day": self.date.day,
  139
+            "url_title": self.url_title,
  140
+        })
  141
+    
  142
+    class Meta:
  143
+        unique_together = (("news_feed", "date", "url_title",),)
  144
+        ordering = ("-date",)
  145
+        
  146
+        
  147
+historylinks.register(Article)
  148
+
  149
+
  150
+sitemaps.register(Article)
BIN  src/cms/apps/news/static/news/img/news-feed.png
11  src/cms/apps/news/templates/news/article_archive.html
... ...
@@ -0,0 +1,11 @@
  1
+{% extends "news/base.html" %}
  2
+{% load news %}
  3
+
  4
+
  5
+{% block content_primary %}
  6
+
  7
+    {{block.super}}
  8
+    
  9
+    {% article_list article_list %}
  10
+
  11
+{% endblock %}
14  src/cms/apps/news/templates/news/article_archive_day.html
... ...
@@ -0,0 +1,14 @@
  1
+{% extends "news/base.html" %}
  2
+{% load news %}
  3
+
  4
+
  5
+{% block header %}{{block.super}} for {{day}}{% endblock %}
  6
+
  7
+
  8
+{% block content_primary %}
  9
+
  10
+    {{block.super}}
  11
+    
  12
+    {% article_list article_list %}
  13
+
  14
+{% endblock %}
14  src/cms/apps/news/templates/news/article_archive_month.html
... ...
@@ -0,0 +1,14 @@
  1
+{% extends "news/base.html" %}
  2
+{% load news %}
  3
+
  4
+
  5
+{% block header %}{{block.super}} for {{month|date:"YEAR_MONTH_FORMAT"}}{% endblock %}
  6
+
  7
+
  8
+{% block content_primary %}
  9
+
  10
+    {{block.super}}
  11
+    
  12
+    {% article_list article_list %}
  13
+
  14
+{% endblock %}
14  src/cms/apps/news/templates/news/article_archive_year.html
... ...
@@ -0,0 +1,14 @@
  1
+{% extends "news/base.html" %}
  2
+{% load news %}
  3
+
  4
+
  5
+{% block header %}{{block.super}} for {{year}}{% endblock %}
  6
+
  7
+
  8
+{% block content_primary %}
  9
+
  10
+    {{block.super}}
  11
+    
  12
+    {% article_list article_list %}
  13
+
  14
+{% endblock %}
11  src/cms/apps/news/templates/news/article_category_archive.html
... ...
@@ -0,0 +1,11 @@
  1
+{% extends "news/base.html" %}
  2
+{% load news html %}
  3
+
  4
+
  5
+{% block content_primary %}
  6
+
  7
+    {{category.content_primary|html}}
  8
+    
  9
+    {% article_list article_list %}
  10
+
  11
+{% endblock %}
21  src/cms/apps/news/templates/news/article_detail.html
... ...
@@ -0,0 +1,21 @@
  1
+{% extends "news/base.html" %}
  2
+{% load html %}
  3
+
  4
+
  5
+{% block header_group %}
  6
+    <hgroup>
  7
+        {{block.super}}
  8
+        <h2>{{article.date}}</h2>
  9
+    </hgroup>
  10
+{% endblock %}
  11
+
  12
+
  13
+{% block content_primary %}
  14
+    
  15
+    {% if article.content %}
  16
+        {{article.content|html}}
  17
+    {% else %}
  18
+        {{article.summary|html}}
  19
+    {% endif %}
  20
+
  21
+{% endblock %}
1  src/cms/apps/news/templates/news/base.html
... ...
@@ -0,0 +1 @@
  1
+{% extends "base.html" %}
17  src/cms/apps/news/templates/news/includes/article_list.html
... ...
@@ -0,0 +1,17 @@
  1
+{% load pagination news %}
  2
+{% paginate articles per_page=5 as article_page %}
  3
+{% if article_page.object_list %}
  4
+    
  5
+    <ul class="news-article-list">
  6
+        {% for article in article_page.object_list %}
  7
+            <li>
  8
+                {% article_list_item article %}
  9
+            </li>
  10
+        {% endfor %}
  11
+    </ul>
  12
+    
  13
+    {% pagination article_page %}
  14
+    
  15
+{% else %}
  16
+    <p><em>There are not articles to display.</em>
  17
+{% endif %}
19  src/cms/apps/news/templates/news/includes/article_list_item.html
... ...
@@ -0,0 +1,19 @@
  1
+{% load html assets %}
  2
+<article{% if article.image %} class="news-article-list-item-with-image"{% endif %}>
  3
+    <hgroup>
  4
+        <h1><a href="{{url}}">{{article}}</a></h1>
  5
+        <h2>{{article.date}}</h2>
  6
+    </hgroup>
  7
+    {% if article.image %}
  8
+        <aside>
  9
+            <a href="{{url}}">
  10
+                {% img article.image.file width=140 alt=article.image %}
  11
+            </a>
  12
+        </aside>
  13
+    {% endif %}
  14
+    {% if article.summary %}
  15
+        {{article.summary|html}}
  16
+    {% else %}
  17
+        {{article.content|truncate_paragraphs:1|html}}
  18
+    {% endif %}
  19
+</article>
1  src/cms/apps/news/templatetags/__init__.py
... ...
@@ -0,0 +1 @@
  1
+"""Template tags used by the news module."""
36  src/cms/apps/news/templatetags/news.py
... ...
@@ -0,0 +1,36 @@
  1
+"""Template tags used by the news module."""
  2
+
  3
+from django import template
  4
+
  5
+
  6
+register = template.Library()
  7
+
  8
+
  9
+@register.inclusion_tag("news/includes/article_list.html", takes_context=True)
  10
+def article_list(context, articles):
  11
+    """Renders a list of news articles."""
  12
+    request = context["request"]
  13
+    pages = context["pages"]
  14
+    return {
  15
+        "articles": articles,
  16
+        "request": request,
  17
+        "pages": pages,
  18
+    }
  19
+    
  20
+    
  21
+@register.inclusion_tag("news/includes/article_list_item.html", takes_context=True)
  22
+def article_list_item(context, article):
  23
+    """Renders an item in an article list."""
  24
+    pages = context["pages"]
  25
+    page = pages.current
  26
+    # Calculate the URL rather than have to look up the article page AGAIN.
  27
+    url = page.reverse("article_detail", kwargs={
  28
+        "year": article.date.year,
  29
+        "month": article.date.strftime("%b").lower(),
  30
+        "day": article.date.day,
  31
+        "url_title": article.url_title,
  32
+    })
  33
+    return {
  34
+        "article": article,
  35
+        "url": url,
  36
+    }
22  src/cms/apps/news/urls.py
... ...
@@ -0,0 +1,22 @@
  1
+"""URLs used by the CMS news app."""
  2
+
  3
+from django.conf.urls import patterns, url
  4
+
  5
+from cms.apps.news import views
  6
+
  7
+
  8
+urlpatterns = patterns("cms.apps.news.view",
  9
+    
  10
+    url(r"^$", views.ArticleArchiveView.as_view(), name="article_archive"),
  11
+    
  12
+    url("^(?P<year>\d+)/$", views.ArticleYearArchiveView.as_view(), name="article_year_archive"),
  13
+    
  14
+    url("^(?P<year>\d+)/(?P<month>\w+)/$", views.ArticleMonthArchiveView.as_view(), name="article_month_archive"),
  15
+    
  16
+    url("^(?P<year>\d+)/(?P<month>\w+)/(?P<day>\d+)/$", views.ArticleDayArchiveView.as_view(), name="article_day_archive"),
  17
+    
  18
+    url("^(?P<year>\d+)/(?P<month>\w+)/(?P<day>\d+)/(?P<url_title>[^/]+)/$", views.ArticleDetailView.as_view(), name="article_detail"),
  19
+    
  20
+    url("^(?P<url_title>[^/]+)/$", views.ArticleCategoryArchiveView.as_view(), name="article_category_archive"),
  21
+    
  22
+)
82  src/cms/apps/news/views.py
... ...
@@ -0,0 +1,82 @@
  1
+"""Views used by the CMS news app."""
  2
+
  3
+from django.views import generic
  4
+from django.shortcuts import get_object_or_404
  5
+
  6
+from cms.views import PageDetailMixin
  7
+from cms.apps.news.models import Article, Category
  8
+
  9
+
  10
+class ArticleListMixin(object):
  11
+    
  12
+    """Base class for every view that handles articles."""
  13
+    
  14
+    model = Article
  15
+    
  16
+    make_object_list = True
  17
+    
  18
+    date_field = "date"
  19
+    
  20
+    allow_future = True  # The publication manager will take care of this.
  21
+    
  22
+    context_object_name = "article_list"
  23
+    
  24
+    def get_queryset(self):
  25
+        """Returns the article queryset."""
  26
+        return super(ArticleListMixin, self).get_queryset().prefetch_related(
  27
+            "categories",
  28
+            "authors",
  29
+        ).select_related("image").filter(
  30
+            news_feed__page = self.request.pages.current,
  31
+        )
  32
+
  33
+
  34
+class ArticleArchiveView(ArticleListMixin, generic.ArchiveIndexView):
  35
+    
  36
+    pass
  37
+
  38
+
  39
+class ArticleYearArchiveView(ArticleListMixin, generic.YearArchiveView):
  40
+    
  41
+    pass
  42
+
  43
+
  44
+class ArticleMonthArchiveView(ArticleListMixin, generic.MonthArchiveView):
  45
+    
  46
+    pass
  47
+
  48
+
  49
+class ArticleDayArchiveView(ArticleListMixin, generic.DayArchiveView):
  50
+    
  51
+    pass
  52
+
  53
+
  54
+class ArticleDetailView(ArticleListMixin, PageDetailMixin, generic.DateDetailView):
  55
+    
  56
+    context_object_name = "article"
  57
+
  58
+
  59
+class ArticleCategoryArchiveView(PageDetailMixin, ArticleArchiveView):
  60
+    
  61
+    """An archive view for articles by category."""
  62
+    
  63
+    template_name = "news/article_category_archive.html"
  64
+    
  65
+    def get_queryset(self):
  66
+        """Returns the queryset filtered by category."""
  67
+        return super(ArticleCategoryArchiveView, self).get_queryset().filter(
  68
+            categories = self.object,
  69
+        )
  70
+        
  71
+    def get_context_data(self, **kwargs):
  72
+        """Adds the category to the context."""
  73
+        context = super(ArticleCategoryArchiveView, self).get_context_data(**kwargs)
  74
+        context["category"] = self.object
  75
+        return context
  76
+    
  77
+    def dispatch(self, request, *args, **kwargs):
  78
+        """Parses the category from the request."""
  79
+        self.object = get_object_or_404(Category,
  80
+            url_title = kwargs["url_title"],
  81
+        )
  82
+        return super(ArticleCategoryArchiveView, self).dispatch(request, *args, **kwargs)
11  src/cms/apps/pages/models.py
@@ -7,9 +7,10 @@
7 7
 from django.utils.functional import cached_property
8 8
 from django.utils import timezone
9 9
 
  10
+import historylinks 
  11
+
10 12
 from cms import sitemaps
11 13
 from cms.models.base import PageBase, OnlineBaseManager
12  
-from cms.apps import historylinks
13 14
 
14 15
 
15 16
 def get_default_page_parent():
@@ -150,8 +151,12 @@ def content(self):
150 151
         content.page = self
151 152
         return content
152 153
 
153  
-    def reverse(self, view_func, args, kwargs):
  154
+    def reverse(self, view_func, args=None, kwargs=None):
154 155
         """Performs a reverse URL lookup."""
  156
+        if args is None:
  157
+            args = ()
  158
+        if kwargs is None:
  159
+            kwargs = {}
155 160
         urlconf = ContentType.objects.get_for_id(self.content_type_id).model_class().urlconf
156 161
         return self.get_absolute_url() + urlresolvers.reverse(view_func, args=args, kwargs=kwargs, urlconf=urlconf, prefix="")
157 162
 
@@ -296,7 +301,7 @@ class ContentBase(models.Model):
296 301
     
297 302
     def __unicode__(self):
298 303
         """Returns a unicode representation."""
299  
-        return u"Content for {0}.".format(self.page)
  304
+        return unicode(self.page)
300 305
     
301 306
     class Meta:
302 307
         abstract = True
4  src/cms/project_template/project_name/settings/production.py
@@ -116,8 +116,8 @@
116 116
     "optimizations",
117 117
     "reversion",
118 118
     "usertools",
  119
+    "historylinks",
119 120
     "cms",
120  
-    "cms.apps.historylinks",
121 121
     "cms.apps.pages",
122 122
     "cms.apps.media",
123 123
     "{{ project_name }}.apps.site",
@@ -156,7 +156,7 @@
156 156
     "django.contrib.auth.middleware.AuthenticationMiddleware",
157 157
     "django.contrib.messages.middleware.MessageMiddleware",
158 158
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
159  
-    "cms.apps.historylinks.middleware.HistoryLinkFallbackMiddleware",
  159
+    "historylinks.middleware.HistoryLinkFallbackMiddleware",
160 160
     "cms.middleware.PublicationMiddleware",
161 161
     "cms.apps.pages.middleware.PageMiddleware",
162 162
 )
4  src/cms/project_template/project_name/templates/base.html
@@ -38,7 +38,9 @@
38 38
                 {% if not pages.is_homepage %}
39 39
                     <nav id="nav-breadcrumbs">{% block breadcrumbs %}{% breadcrumbs %}{% endblock %}</nav>
40 40
                 {% endif %}
41  
-                <h1>{% block header %}{% header %}{% endblock %}</h1>
  41
+                {% block header_group %}
  42
+                    <h1>{% block header %}{% header %}{% endblock %}</h1>
  43
+                {% endblock %}
42 44
                 {% block content_primary %}{{pages.current.content.content_primary|html}}{% endblock %}
43 45
             </article>
44 46
             <aside>
17  src/cms/templatetags/html.py
@@ -21,4 +21,19 @@ def html(text):
21 21
     if not text:
22 22
         return ""
23 23
     text = process_html(text)
24  
-    return mark_safe(text)
  24
+    return mark_safe(text)
  25
+
  26
+
  27
+@register.filter
  28
+def truncate_paragraphs(text, number):
  29
+    """Truncates to the end of the given number of paragraphs in the given text."""
  30
+    position = 0
  31
+    count = 0
  32
+    while count < number and position < len(text):
  33
+        position = text.find(u"</p>", position)
  34
+        if position == -1:
  35
+            position = len(text)
  36
+        else:
  37
+            position += 4
  38
+        count += 1
  39
+    return text[:position]
15  src/cms/views.py
@@ -35,13 +35,20 @@ def get_context_data(self, **kwargs):
35 35
         return defaults
36 36
     
37 37
     
  38
+class PageDetailMixin(SearchMetaDetailMixin):
  39
+    
  40
+    """Generates the context for a page detail view."""
  41
+    
  42
+    slug_field = "url_title"
  43
+    
  44
+    slug_url_kwarg = "url_title"
  45
+    
  46
+    
38 47
 class SearchMetaDetailView(SearchMetaDetailMixin, generic.DetailView):
39 48
     
40 49
     """A simple entity detail view."""
41 50
     
42 51
     
43  
-class PageDetailView(SearchMetaDetailView):
44  
-    
45  
-    """A simple page detail view."""
  52
+class PageDetailView(PageDetailMixin, generic.DetailView):
46 53
     
47  
-    slug_field = "url_title"
  54
+    """A simple page detail view."""

No commit comments for this range

Something went wrong with that request. Please try again.