diff --git a/articles/admin.py b/articles/admin.py index d224a0d..ca10299 100644 --- a/articles/admin.py +++ b/articles/admin.py @@ -5,7 +5,7 @@ from django.contrib.admin import register from django.contrib.auth.admin import UserAdmin from django.shortcuts import redirect -from .models import Article, Page, User +from .models import Article, User admin.site.register(User, UserAdmin) @@ -35,6 +35,7 @@ class ArticleAdmin(admin.ModelAdmin): ("status", "published_at"), ("created_at", "updated_at"), "views_count", + "has_code", ] }, ), @@ -92,13 +93,3 @@ class ArticleAdmin(admin.ModelAdmin): messages.success(request, "Item has been unpublished") return redirect(".") return super().response_change(request, obj) - - -@register(Page) -class PageAdmin(ArticleAdmin): - list_display = ["position"] + ArticleAdmin.list_display - - def get_fieldsets(self, request, obj=None): - article_fieldsets = copy.deepcopy(ArticleAdmin.fieldsets) - article_fieldsets[0][1]["fields"][0] = ("title", "slug", "position") - return article_fieldsets diff --git a/articles/context_processors.py b/articles/context_processors.py index a97a56b..e329a30 100644 --- a/articles/context_processors.py +++ b/articles/context_processors.py @@ -1,6 +1,6 @@ from django.conf import settings -from articles.models import Article, Page +from articles.models import Article from attachments.models import Attachment IGNORED_PATHS = [ @@ -8,12 +8,6 @@ IGNORED_PATHS = [ ] -def pages(request): - if request.path in IGNORED_PATHS: - return {} - return {"pages": Page.objects.filter(status=Article.PUBLISHED).exclude(position=0)} - - def drafts_count(request): if request.path in IGNORED_PATHS: return {} @@ -52,3 +46,11 @@ def open_graph_image_url(request): if open_graph_image: url = open_graph_image.processed_file.get_full_absolute_url(request) return {"open_graph_image_url": url} + + +def blog_metadata(request): + context = {} + context["blog_title"] = settings.BLOG["title"] + context["blog_description"] = settings.BLOG["description"] + context["blog_author"] = settings.BLOG["author"] + return context diff --git a/articles/migrations/0023_article_has_code.py b/articles/migrations/0023_article_has_code.py new file mode 100644 index 0000000..c7d20bd --- /dev/null +++ b/articles/migrations/0023_article_has_code.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.3 on 2020-12-24 16:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0022_article_keywords"), + ] + + operations = [ + migrations.AddField( + model_name="article", + name="has_code", + field=models.BooleanField(blank=True, default=False), + ), + ] diff --git a/articles/migrations/0024_auto_20201224_1746.py b/articles/migrations/0024_auto_20201224_1746.py new file mode 100644 index 0000000..067660e --- /dev/null +++ b/articles/migrations/0024_auto_20201224_1746.py @@ -0,0 +1,32 @@ +# Generated by Django 3.1.3 on 2020-12-24 16:46 + +from django.db import migrations, models + + +def forwards_func(apps, schema_editor): + Article = apps.get_model("articles", "Article") + db_alias = schema_editor.connection.alias + Article.objects.using(db_alias).filter(slug="about-me").update(is_home=True) + + +def reverse_func(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0023_article_has_code"), + ] + + operations = [ + migrations.AddField( + model_name="article", + name="is_home", + field=models.BooleanField(blank=True, default=False), + ), + migrations.DeleteModel( + name="Page", + ), + migrations.RunPython(forwards_func, reverse_func), + ] diff --git a/articles/models.py b/articles/models.py index 6119f04..e7d6c2f 100644 --- a/articles/models.py +++ b/articles/models.py @@ -18,11 +18,6 @@ class User(AbstractUser): pass -class ArticleManager(models.Manager): - def get_queryset(self): - return super().get_queryset().filter(page__isnull=True) - - class AdminUrlMixin: def get_admin_url(self): content_type = ContentType.objects.get_for_model(self.__class__) @@ -49,18 +44,14 @@ class Article(AdminUrlMixin, models.Model): views_count = models.IntegerField(default=0) slug = models.SlugField(unique=True, max_length=255) keywords = models.CharField(max_length=255, blank=True) - - objects = models.Manager() - without_pages = ArticleManager() + has_code = models.BooleanField(default=False, blank=True) + is_home = models.BooleanField(default=False, blank=True) class Meta: ordering = ["-published_at"] def __str__(self): - type_ = "Article" - if hasattr(self, "page"): - type_ = "Page" - return f"{self.title} ({type_})" + return f"{self.title}" def get_absolute_url(self): return reverse("article-detail", kwargs={"slug": self.slug}) @@ -116,11 +107,3 @@ class Article(AdminUrlMixin, models.Model): if not self.slug: self.slug = slugify(self.title) return super().save(*args, **kwargs) - - -class Page(Article): - objects = models.Manager() - position = models.IntegerField(default=0) - - class Meta: - ordering = ["position", "-published_at"] diff --git a/articles/static/code-dark.css b/articles/static/code-dark.css deleted file mode 100644 index ed8323f..0000000 --- a/articles/static/code-dark.css +++ /dev/null @@ -1,48 +0,0 @@ -.codehilite .hll { background-color: #49483e } -.codehilite { background: #232629; color: #cccccc } -.codehilite .c { color: #777777; font-style: italic } /* Comment */ -.codehilite .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -.codehilite .k { color: #7686bb; font-weight: bold } /* Keyword */ -.codehilite .ch { color: #777777; font-style: italic } /* Comment.Hashbang */ -.codehilite .cm { color: #777777; font-style: italic } /* Comment.Multiline */ -.codehilite .cp { color: #777777; font-style: italic } /* Comment.Preproc */ -.codehilite .cpf { color: #777777; font-style: italic } /* Comment.PreprocFile */ -.codehilite .c1 { color: #777777; font-style: italic } /* Comment.Single */ -.codehilite .cs { color: #777777; font-style: italic } /* Comment.Special */ -.codehilite .gp { color: #ffffff } /* Generic.Prompt */ -.codehilite .kc { color: #7686bb; font-weight: bold } /* Keyword.Constant */ -.codehilite .kd { color: #7686bb; font-weight: bold } /* Keyword.Declaration */ -.codehilite .kn { color: #7686bb; font-weight: bold } /* Keyword.Namespace */ -.codehilite .kp { color: #7686bb; font-weight: bold } /* Keyword.Pseudo */ -.codehilite .kr { color: #7686bb; font-weight: bold } /* Keyword.Reserved */ -.codehilite .kt { color: #7686bb; font-weight: bold } /* Keyword.Type */ -.codehilite .m { color: #4FB8CC } /* Literal.Number */ -.codehilite .s { color: #51cc99 } /* Literal.String */ -.codehilite .nf { color: #6a6aff } /* Name.Function */ -.codehilite .nx { color: #e2828e } /* Name.Other */ -.codehilite .nv { color: #7AB4DB; font-weight: bold } /* Name.Variable */ -.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ -.codehilite .mb { color: #4FB8CC } /* Literal.Number.Bin */ -.codehilite .mf { color: #4FB8CC } /* Literal.Number.Float */ -.codehilite .mh { color: #4FB8CC } /* Literal.Number.Hex */ -.codehilite .mi { color: #4FB8CC } /* Literal.Number.Integer */ -.codehilite .mo { color: #4FB8CC } /* Literal.Number.Oct */ -.codehilite .sa { color: #51cc99 } /* Literal.String.Affix */ -.codehilite .sb { color: #51cc99 } /* Literal.String.Backtick */ -.codehilite .sc { color: #51cc99 } /* Literal.String.Char */ -.codehilite .dl { color: #51cc99 } /* Literal.String.Delimiter */ -.codehilite .sd { color: #51cc99 } /* Literal.String.Doc */ -.codehilite .s2 { color: #51cc99 } /* Literal.String.Double */ -.codehilite .se { color: #51cc99 } /* Literal.String.Escape */ -.codehilite .sh { color: #51cc99 } /* Literal.String.Heredoc */ -.codehilite .si { color: #51cc99 } /* Literal.String.Interpol */ -.codehilite .sx { color: #51cc99 } /* Literal.String.Other */ -.codehilite .sr { color: #51cc99 } /* Literal.String.Regex */ -.codehilite .s1 { color: #51cc99 } /* Literal.String.Single */ -.codehilite .ss { color: #51cc99 } /* Literal.String.Symbol */ -.codehilite .fm { color: #6a6aff } /* Name.Function.Magic */ -.codehilite .vc { color: #7AB4DB; font-weight: bold } /* Name.Variable.Class */ -.codehilite .vg { color: #BE646C; font-weight: bold } /* Name.Variable.Global */ -.codehilite .vi { color: #7AB4DB; font-weight: bold } /* Name.Variable.Instance */ -.codehilite .vm { color: #7AB4DB; font-weight: bold } /* Name.Variable.Magic */ -.codehilite .il { color: #4FB8CC } /* Literal.Number.Integer.Long */ diff --git a/articles/static/code-light.css b/articles/static/code-light.css deleted file mode 100644 index bb14f47..0000000 --- a/articles/static/code-light.css +++ /dev/null @@ -1,66 +0,0 @@ -.codehilite .hll { background-color: #ffffcc } -.codehilite { background: #ffffff; } -.codehilite .c { color: #aaaaaa; font-style: italic } /* Comment */ -.codehilite .err { color: #FF0000; background-color: #FFAAAA } /* Error */ -.codehilite .k { color: #0000aa } /* Keyword */ -.codehilite .ch { color: #aaaaaa; font-style: italic } /* Comment.Hashbang */ -.codehilite .cm { color: #aaaaaa; font-style: italic } /* Comment.Multiline */ -.codehilite .cp { color: #4c8317 } /* Comment.Preproc */ -.codehilite .cpf { color: #aaaaaa; font-style: italic } /* Comment.PreprocFile */ -.codehilite .c1 { color: #aaaaaa; font-style: italic } /* Comment.Single */ -.codehilite .cs { color: #0000aa; font-style: italic } /* Comment.Special */ -.codehilite .gd { color: #aa0000 } /* Generic.Deleted */ -.codehilite .ge { font-style: italic } /* Generic.Emph */ -.codehilite .gr { color: #aa0000 } /* Generic.Error */ -.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.codehilite .gi { color: #00aa00 } /* Generic.Inserted */ -.codehilite .go { color: #888888 } /* Generic.Output */ -.codehilite .gp { color: #555555 } /* Generic.Prompt */ -.codehilite .gs { font-weight: bold } /* Generic.Strong */ -.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.codehilite .gt { color: #aa0000 } /* Generic.Traceback */ -.codehilite .kc { color: #0000aa } /* Keyword.Constant */ -.codehilite .kd { color: #0000aa } /* Keyword.Declaration */ -.codehilite .kn { color: #0000aa } /* Keyword.Namespace */ -.codehilite .kp { color: #0000aa } /* Keyword.Pseudo */ -.codehilite .kr { color: #0000aa } /* Keyword.Reserved */ -.codehilite .kt { color: #00aaaa } /* Keyword.Type */ -.codehilite .m { color: #009999 } /* Literal.Number */ -.codehilite .s { color: #aa5500 } /* Literal.String */ -.codehilite .na { color: #1e90ff } /* Name.Attribute */ -.codehilite .nb { color: #00aaaa } /* Name.Builtin */ -.codehilite .nc { color: #00aa00; text-decoration: underline } /* Name.Class */ -.codehilite .no { color: #aa0000 } /* Name.Constant */ -.codehilite .nd { color: #888888 } /* Name.Decorator */ -.codehilite .ni { color: #880000; font-weight: bold } /* Name.Entity */ -.codehilite .nf { color: #00aa00 } /* Name.Function */ -.codehilite .nn { color: #00aaaa; text-decoration: underline } /* Name.Namespace */ -.codehilite .nt { color: #1e90ff; font-weight: bold } /* Name.Tag */ -.codehilite .nv { color: #aa0000 } /* Name.Variable */ -.codehilite .ow { color: #0000aa } /* Operator.Word */ -.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ -.codehilite .mb { color: #009999 } /* Literal.Number.Bin */ -.codehilite .mf { color: #009999 } /* Literal.Number.Float */ -.codehilite .mh { color: #009999 } /* Literal.Number.Hex */ -.codehilite .mi { color: #009999 } /* Literal.Number.Integer */ -.codehilite .mo { color: #009999 } /* Literal.Number.Oct */ -.codehilite .sa { color: #aa5500 } /* Literal.String.Affix */ -.codehilite .sb { color: #aa5500 } /* Literal.String.Backtick */ -.codehilite .sc { color: #aa5500 } /* Literal.String.Char */ -.codehilite .dl { color: #aa5500 } /* Literal.String.Delimiter */ -.codehilite .sd { color: #aa5500 } /* Literal.String.Doc */ -.codehilite .s2 { color: #aa5500 } /* Literal.String.Double */ -.codehilite .se { color: #aa5500 } /* Literal.String.Escape */ -.codehilite .sh { color: #aa5500 } /* Literal.String.Heredoc */ -.codehilite .si { color: #aa5500 } /* Literal.String.Interpol */ -.codehilite .sx { color: #aa5500 } /* Literal.String.Other */ -.codehilite .sr { color: #009999 } /* Literal.String.Regex */ -.codehilite .s1 { color: #aa5500 } /* Literal.String.Single */ -.codehilite .ss { color: #0000aa } /* Literal.String.Symbol */ -.codehilite .bp { color: #00aaaa } /* Name.Builtin.Pseudo */ -.codehilite .fm { color: #00aa00 } /* Name.Function.Magic */ -.codehilite .vc { color: #aa0000 } /* Name.Variable.Class */ -.codehilite .vg { color: #aa0000 } /* Name.Variable.Global */ -.codehilite .vi { color: #aa0000 } /* Name.Variable.Instance */ -.codehilite .vm { color: #aa0000 } /* Name.Variable.Magic */ -.codehilite .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/articles/static/edit-keymap.js b/articles/static/edit-keymap.js new file mode 100644 index 0000000..8d82f02 --- /dev/null +++ b/articles/static/edit-keymap.js @@ -0,0 +1,12 @@ +window.onload = function () { + const adminLinkElement = document.querySelector(".article-detail .metadata a.admin-link"); + if (adminLinkElement === undefined || adminLinkElement === null) { + return; + } + const adminLocation = adminLinkElement.href; + document.addEventListener("keydown", event => { + if (event.code === "KeyE") { + window.location = adminLocation; + } + }) +} diff --git a/articles/static/scripts.js b/articles/static/scripts.js deleted file mode 100644 index aaa4bda..0000000 --- a/articles/static/scripts.js +++ /dev/null @@ -1,32 +0,0 @@ -function activateDarkMode() { - document.getElementById("code-dark").removeAttribute("disabled"); -} - -function activateLightMode() { - document.getElementById("code-dark").setAttribute("disabled", "true"); -} - -function darkModeListener(e) { - if (e.matches) { - activateDarkMode(); - } else { - activateLightMode(); - } -} - -let mql = window.matchMedia("(prefers-color-scheme: dark)"); -darkModeListener(mql); -mql.addListener(darkModeListener); - -window.onload = function () { - const adminLinkElement = document.querySelector(".article-detail .metadata a.admin-link"); - if (adminLinkElement === undefined || adminLinkElement === null) { - return; - } - const adminLocation = adminLinkElement.href; - document.addEventListener("keydown", event => { - if (event.code === "KeyE") { - window.location = adminLocation; - } - }) -} diff --git a/articles/static/style.css b/articles/static/style.css deleted file mode 100644 index d1641a7..0000000 --- a/articles/static/style.css +++ /dev/null @@ -1,310 +0,0 @@ -:root { - --accent: #226997; - --accent-light: #7c96a9; - --main: #111111; - --main2: #575757; - --main3: #7d7d7d; - --background: #ffffff; - --background2: #f7f7f7; - --background3: #d0d0d0; - --success-background: #d4edda; - --success-text: #155724; - --error-background: #f8d7da; - --error-text: #721c24; - --warning-background: #fff3cd; - --warning-text: #856404; - --description-margin: .2rem; -} - -html { - font-size: 110%; - max-width: calc(640px + 2em); - padding: 8px 0 3em; - margin: 0 auto; -} - -body { - margin: 0 1em; - font-family: Arial, sans-serif; - color: var(--main); - background-color: var(--background); -} - -p { - line-height: 1.7; -} - -li { - line-height: 1.4; -} - -body img { - max-width: 100%; -} - -a { - color: var(--main); - text-decoration: none; - border-bottom: .3ex solid var(--accent); -} - -.index-page h2, .article-detail h2 { - margin-top: 2em; -} - -.article-list h2 a { - border-color: transparent; -} - -.pic-container { - display: flex; - justify-content: space-evenly; - flex-wrap: wrap; -} - -.pic-container .profile-pic { - max-width: 200px; - min-width: 100px; - max-height: 200px; - min-height: 100px; - border-radius: 10%; - padding: 1rem; - flex-shrink: 1; - flex-grow: 0; -} - -.article-list + .article-list { - margin-top: 4em; -} - -.article-list h2, h1 { - margin-bottom: var(--description-margin); -} - -.article-detail h1 { - font-size: 2em; -} - -.metadata { - margin-top: var(--description-margin); - color: var(--main2); -} - -.pagination { - display: flex; - justify-content: space-between; -} - -code { - white-space: pre-wrap; -} - -.codehilite { - border-radius: .5ex; - background-color: var(--background2); - border: 1px solid var(--background3); -} - -.codehilite pre { - padding: 0; - margin: 1em; -} - -section.comments { - border-top: 2px dashed var(--background3); - margin-top: 2em; -} - -footer { - border-top: 2px solid var(--background2); - margin-top: 2em; - font-size: 80%; - color: var(--main3); -} - -footer a { - color: var(--main3); - border-bottom-color: var(--accent-light); -} - -table { - border-collapse: collapse; - width: 100%; -} - -th, td { - padding: 1ex; - text-align: left; -} - -tr { - border-bottom: 1px solid var(--background3); -} - -tr:hover { - background-color: var(--background2); -} - -.pill { - font-size: 60%; - background-color: var(--main2); - color: var(--background2); - padding: .5ex 1ex; - border-radius: 1ex; - vertical-align: 15%; -} - -blockquote { - background-color: var(--background2); - margin: 0; - padding: 1px .5em 1px 1.5em; - border-left: 6px solid var(--background3); -} - -details { - padding: .6rem 1rem; - background: var(--background2); - border: 1px solid var(--background3); - border-radius: 4px; -} - -summary { - cursor: pointer; -} - -details .codehilite { - border: none; -} - -/* FORMS */ -.helptext { - color: var(--main3); - font-size: 80%; -} - -.helptext a { - color: var(--main3); -} - -p.helptext { - margin-bottom: 0; -} - -form table th { - vertical-align: top; -} - -form table tr th { - font-weight: normal; -} - -form tr.required th { - font-weight: bold; -} - -form table tr { - border-bottom: none; -} - -form { - background-color: var(--background2); - border-radius: .5ex; - padding: 1em; -} - -form button[type=submit] { - font-size: 1em; - padding: .9ex 1.2ex; - border-radius: .8ex; - background-color: var(--accent); - border: none; - color: var(--background); - cursor: pointer; - margin-top: 1em; -} - -form .error, form .error .helptext, .errorlist { - color: var(--error-text); - background-color: var(--error-background); -} - -.errorlist { - margin-top: 0; - list-style-type: none; - padding-left: 0; -} - -.errorlist.nonfield { - margin-bottom: 0; -} - -tr.spacer { - padding: 1em; -} - -textarea, input { - width: 100%; -} - -/* MESSAGES */ -.messages p { - background-color: var(--background2); - padding: .5ex 1ex; -} - -.messages .error { - background-color: var(--error-background); - color: var(--error-text); -} - -.messages .success { - background-color: var(--success-background); - color: var(--success-text); -} - -.messages .warning { - background-color: var(--warning-background); - color: var(--warning-text); -} - -/* LINKS */ -a:hover, a:focus { - text-decoration: none; - background-color: var(--accent); - color: var(--background); -} - -footer a:hover, footer a:focus { - background-color: var(--accent-light); -} - -@media (prefers-color-scheme: dark) { - :root { - --accent: #226997; - --main: #eeeeee; - --main2: #cecece; - --main3: #b1b1b1; - --background: #111111; - --background2: #282828; - --success-background: #155724; - --success-text: #d4edda; - --error-background: #721c24; - --error-text: #f8d7da; - --warning-background: #856404; - --warning-text: #fff3cd; - } - - img { - opacity: .75; - transition: opacity .2s ease-in-out; - } - - img:hover { - opacity: 1; - } - - #manage ul ul li img { - opacity: 1; - filter: invert(1); - } -} diff --git a/articles/templates/articles/admin_link_snippet.html b/articles/templates/articles/admin_link_snippet.html deleted file mode 100644 index aa6518c..0000000 --- a/articles/templates/articles/admin_link_snippet.html +++ /dev/null @@ -1,3 +0,0 @@ -{% if user.is_authenticated %} - | Edit -{% endif %} diff --git a/articles/templates/articles/article_detail.html b/articles/templates/articles/article_detail.html index b517771..1e0e2aa 100644 --- a/articles/templates/articles/article_detail.html +++ b/articles/templates/articles/article_detail.html @@ -1,21 +1,14 @@ {% extends 'articles/base.html' %} {% block title %}{{ article.title }} | {% endblock %} + {% block content %} -
+

{{ article.title }}{% if article.status != article.PUBLISHED %} ({{ article.status }}){% endif %}

- {% include "articles/metadata_snippet.html" %} + {% include "articles/snippets/metadata.html" %}
{{ article.get_formatted_content|safe }}
-
-

Comments

-

- Comments are hard to manage. I tried enabling them but I only got spam. - If you want to react to this content or interact with me, please head to the - about me page. ๐Ÿ˜‰ -

-
{% endblock %} diff --git a/articles/templates/articles/article_list.html b/articles/templates/articles/article_list.html index 2a72a68..5e5ad2b 100644 --- a/articles/templates/articles/article_list.html +++ b/articles/templates/articles/article_list.html @@ -1,38 +1,44 @@ {% extends 'articles/base.html' %} +{% block override_header %} + {% include "articles/snippets/style_home.html" %} +{% endblock %} + {% block title %}{% endblock %} + {% block content %} -

{{ blog_title }}{% if title %} · {{ title }}{% endif %}

-

{{ blog_description }} {% include "articles/admin_link_snippet.html" %}

-
+

Blog posts

-
    - {% for article in articles %} -
  • - {% include "articles/datetime_snippet.html" %} - : {{ article.title }} -
  • - {% empty %} -
  • No article here. Come back later ๐Ÿ™‚
  • - {% endfor %} -
+ + + {% for article in articles %} + + + + + {% empty %} + + {% endfor %} +
DateTitle
{% include "articles/snippets/datetime.html" %}{{ article.title }}
No article here. Come back later ๐Ÿ™‚
-
-

{{ article.title }}

-
- {{ article.get_formatted_content|safe }} -
-
+ {% if article %} +
+

{{ article.title }}{% include "articles/snippets/admin_link.html" %}

+
+ {{ article.get_formatted_content|safe }} +
+
+ {% endif %} {% endblock %} diff --git a/articles/templates/articles/base.html b/articles/templates/articles/base.html index 4cf899b..0f6f1cc 100644 --- a/articles/templates/articles/base.html +++ b/articles/templates/articles/base.html @@ -6,57 +6,45 @@ - {% if article %} - - - - - - - {% endif %} - {% if open_graph_image_url %} - - {% endif %} - {% block title %}Home | {% endblock %}Gab's Notes by Gabriel Augendre - - - - + {% include "articles/snippets/page_metadata.html" %} + {% block title %}Home | {% endblock %}{{ blog_title }} by {{ blog_author }} - {% include "articles/favicon.html" %} - {% include "articles/analytics.html" %} + {% include "articles/snippets/style.html" %} + {% if article and article.has_code %} + {% include "articles/snippets/code_style.html" %} + {% endif %} + {% if user.is_authenticated %} + + {% endif %} + {% include "articles/snippets/favicon.html" %} + {% include "articles/snippets/analytics.html" %} + {% block override_header %}{% endblock %} - + + +{% block content %} +{% endblock %}