Switch to new.css and cleanup markup & models

This commit is contained in:
Gabriel Augendre 2020-12-24 17:49:47 +01:00
parent 8bf3438746
commit e23b988c55
No known key found for this signature in database
GPG key ID: 1E693F4CE4AEE7B4
30 changed files with 347 additions and 628 deletions

View file

@ -5,7 +5,7 @@ from django.contrib.admin import register
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.shortcuts import redirect from django.shortcuts import redirect
from .models import Article, Page, User from .models import Article, User
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
@ -35,6 +35,7 @@ class ArticleAdmin(admin.ModelAdmin):
("status", "published_at"), ("status", "published_at"),
("created_at", "updated_at"), ("created_at", "updated_at"),
"views_count", "views_count",
"has_code",
] ]
}, },
), ),
@ -92,13 +93,3 @@ class ArticleAdmin(admin.ModelAdmin):
messages.success(request, "Item has been unpublished") messages.success(request, "Item has been unpublished")
return redirect(".") return redirect(".")
return super().response_change(request, obj) 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

View file

@ -1,6 +1,6 @@
from django.conf import settings from django.conf import settings
from articles.models import Article, Page from articles.models import Article
from attachments.models import Attachment from attachments.models import Attachment
IGNORED_PATHS = [ 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): def drafts_count(request):
if request.path in IGNORED_PATHS: if request.path in IGNORED_PATHS:
return {} return {}
@ -52,3 +46,11 @@ def open_graph_image_url(request):
if open_graph_image: if open_graph_image:
url = open_graph_image.processed_file.get_full_absolute_url(request) url = open_graph_image.processed_file.get_full_absolute_url(request)
return {"open_graph_image_url": url} 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

View file

@ -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),
),
]

View file

@ -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),
]

View file

@ -18,11 +18,6 @@ class User(AbstractUser):
pass pass
class ArticleManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(page__isnull=True)
class AdminUrlMixin: class AdminUrlMixin:
def get_admin_url(self): def get_admin_url(self):
content_type = ContentType.objects.get_for_model(self.__class__) content_type = ContentType.objects.get_for_model(self.__class__)
@ -49,18 +44,14 @@ class Article(AdminUrlMixin, models.Model):
views_count = models.IntegerField(default=0) views_count = models.IntegerField(default=0)
slug = models.SlugField(unique=True, max_length=255) slug = models.SlugField(unique=True, max_length=255)
keywords = models.CharField(max_length=255, blank=True) keywords = models.CharField(max_length=255, blank=True)
has_code = models.BooleanField(default=False, blank=True)
objects = models.Manager() is_home = models.BooleanField(default=False, blank=True)
without_pages = ArticleManager()
class Meta: class Meta:
ordering = ["-published_at"] ordering = ["-published_at"]
def __str__(self): def __str__(self):
type_ = "Article" return f"{self.title}"
if hasattr(self, "page"):
type_ = "Page"
return f"{self.title} ({type_})"
def get_absolute_url(self): def get_absolute_url(self):
return reverse("article-detail", kwargs={"slug": self.slug}) return reverse("article-detail", kwargs={"slug": self.slug})
@ -116,11 +107,3 @@ class Article(AdminUrlMixin, models.Model):
if not self.slug: if not self.slug:
self.slug = slugify(self.title) self.slug = slugify(self.title)
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
class Page(Article):
objects = models.Manager()
position = models.IntegerField(default=0)
class Meta:
ordering = ["position", "-published_at"]

View file

@ -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 */

View file

@ -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 */

View file

@ -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;
}
})
}

View file

@ -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;
}
})
}

View file

@ -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);
}
}

View file

@ -1,3 +0,0 @@
{% if user.is_authenticated %}
| <a class="admin-link" href="{{ article.get_admin_url }}">Edit</a>
{% endif %}

View file

@ -1,21 +1,14 @@
{% extends 'articles/base.html' %} {% extends 'articles/base.html' %}
{% block title %}{{ article.title }} | {% endblock %} {% block title %}{{ article.title }} | {% endblock %}
{% block content %} {% block content %}
<article class="article-detail"> <article>
<h1>{{ article.title }}{% if article.status != article.PUBLISHED %} <h1>{{ article.title }}{% if article.status != article.PUBLISHED %}
({{ article.status }}){% endif %}</h1> ({{ article.status }}){% endif %}</h1>
{% include "articles/metadata_snippet.html" %} {% include "articles/snippets/metadata.html" %}
<div> <div>
{{ article.get_formatted_content|safe }} {{ article.get_formatted_content|safe }}
</div> </div>
</article> </article>
<section class="comments">
<h2>Comments</h2>
<p>
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
<a href="/about-me/">about me</a> page.&nbsp;😉
</p>
</section>
{% endblock %} {% endblock %}

View file

@ -1,38 +1,44 @@
{% extends 'articles/base.html' %} {% extends 'articles/base.html' %}
{% block override_header %}
{% include "articles/snippets/style_home.html" %}
{% endblock %}
{% block title %}{% endblock %} {% block title %}{% endblock %}
{% block content %} {% block content %}
<h1>{{ blog_title }}{% if title %} &middot; {{ title }}{% endif %}</h1> <section>
<p class="metadata">{{ blog_description }} {% include "articles/admin_link_snippet.html" %}</p>
<section class="blog-posts">
<h2>Blog posts</h2> <h2>Blog posts</h2>
<ul> <table>
<tr><th scope="col">Date</th><th scope="col">Title</th></tr>
{% for article in articles %} {% for article in articles %}
<li> <tr>
{% include "articles/datetime_snippet.html" %} <td>{% include "articles/snippets/datetime.html" %}</td>
: <a href="{% url 'article-detail' slug=article.slug %}">{{ article.title }}</a> <td><a href="{% url 'article-detail' slug=article.slug %}">{{ article.title }}</a></td>
</li> </tr>
{% empty %} {% empty %}
<li>No article here. Come back later 🙂</li> <tr><td colspan="2">No article here. Come back later 🙂</td></tr>
{% endfor %} {% endfor %}
</ul> </table>
</section> </section>
<nav class="pagination"> <nav class="pagination">
<div class="older"> <div>
{% if page_obj.has_next %} {% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">⇠ Older</a> <a href="?page={{ page_obj.next_page_number }}">⇠ Older</a>
{% endif %} {% endif %}
</div> </div>
<div class="newer"> <div>
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">Newer ⇢</a> <a href="?page={{ page_obj.previous_page_number }}">Newer ⇢</a>
{% endif %} {% endif %}
</div> </div>
</nav> </nav>
<section class="index-page"> {% if article %}
<h2>{{ article.title }}</h2> <section>
<h2>{{ article.title }}{% include "articles/snippets/admin_link.html" %}</h2>
<div> <div>
{{ article.get_formatted_content|safe }} {{ article.get_formatted_content|safe }}
</div> </div>
</section> </section>
{% endif %}
{% endblock %} {% endblock %}

View file

@ -6,57 +6,45 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Gabriel Augendre"> <meta name="author" content="Gabriel Augendre">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
{% if article %} {% include "articles/snippets/page_metadata.html" %}
<meta name="keywords" content="{{ article.keywords }}"> <title>{% block title %}Home | {% endblock %}{{ blog_title }} by {{ blog_author }}</title>
<meta name="description" content="{{ article.get_description }}">
<meta property="og:title" content="{{ article.title }}" />
<meta property="og:type" content="article">
<meta property="og:description" content="{{ article.get_description }}">
<meta property="og:site_name" content="{{ blog_title }}">
{% endif %}
{% if open_graph_image_url %}
<meta property="og:image" content="{{ open_graph_image_url }}">
{% endif %}
<title>{% block title %}Home | {% endblock %}Gab's Notes by Gabriel Augendre</title>
<link rel="stylesheet" id="code-light" href="{% static 'code-light.css' %}" type="text/css">
<link rel="stylesheet" id="code-dark" href="{% static 'code-dark.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'style.css' %}" type="text/css">
<script src="{% static 'scripts.js' %}" async></script>
<link rel="alternate" type="application/rss+xml" title="Gab's Notes » Feed" href="{% url 'complete-feed' %}"> <link rel="alternate" type="application/rss+xml" title="Gab's Notes » Feed" href="{% url 'complete-feed' %}">
{% include "articles/favicon.html" %} {% include "articles/snippets/style.html" %}
{% include "articles/analytics.html" %} {% if article and article.has_code %}
{% include "articles/snippets/code_style.html" %}
{% endif %}
{% if user.is_authenticated %}
<script src="{% static 'edit-keymap.js' %}" async></script>
{% endif %}
{% include "articles/snippets/favicon.html" %}
{% include "articles/snippets/analytics.html" %}
{% block override_header %}{% endblock %}
</head> </head>
<body> <body>
<header>
<h1>{{ blog_title }}</h1>
<p>{{ blog_description }}</p>
<nav> <nav>
<a href="{% url 'articles-list' %}">Articles list</a> <a href="{% url 'articles-list' %}">Home</a>
&centerdot;
<a href="{% url 'complete-feed' %}">RSS</a>
&centerdot; &centerdot;
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url 'admin:articles_article_add' %}">Write</a> <a href="{% url 'admin:articles_article_add' %}">Write</a>
&centerdot;
<a href="{% url 'drafts-list' %}">View drafts <span class="pill">{{ drafts_count }}</span></a> <a href="{% url 'drafts-list' %}">View drafts <span class="pill">{{ drafts_count }}</span></a>
&centerdot; &centerdot;
<a href="{% url 'admin:index' %}">Admin</a> <a href="{% url 'admin:index' %}">Admin</a>
&centerdot;
<a href="{% url 'admin:logout' %}?next=/">Log out</a> <a href="{% url 'admin:logout' %}?next=/">Log out</a>
{% else %} {% else %}
<a href="{% url 'admin:login' %}?next=/">Log in</a> <a href="{% url 'admin:login' %}?next=/">Log in</a>
{% endif %} {% endif %}
{% if pages %}
&centerdot;
{% for page in pages %}
<a href="{% url 'article-detail' slug=page.slug %}">{{ page.title }}</a>
{% endfor %}
{% endif %}
</nav> </nav>
{% if messages %} </header>
<div class="messages">
{% for message in messages %}
<p class="{{ message.level_tag }}">{{ message|safe }}</p>
{% endfor %}
</div>
{% endif %}
<div class="content">
{% block content %} {% block content %}
{% endblock %} {% endblock %}
</div>
<footer> <footer>
<p> <p>

View file

@ -1,5 +0,0 @@
{% load i18n %}
<p class="metadata">
{% include "articles/datetime_snippet.html" %}
{% include "articles/admin_link_snippet.html" %}
</p>

View file

@ -0,0 +1,3 @@
{% if user.is_authenticated %}
| <a id="admin-link" href="{{ article.get_admin_url }}">Edit</a>
{% endif %}

View file

@ -0,0 +1,121 @@
<style>
@media(prefers-color-scheme: light) {
.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 */
}
@media(prefers-color-scheme: dark) {
.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 */
}
</style>

View file

@ -1,3 +1,4 @@
{% load i18n %}
{% if article.published_at %} {% if article.published_at %}
<time datetime="{{ article.published_at|date:CUSTOM_ISO }}">{{ article.published_at|date:ISO_DATE }}</time> <time datetime="{{ article.published_at|date:CUSTOM_ISO }}">{{ article.published_at|date:ISO_DATE }}</time>
{% else %} {% else %}

View file

@ -0,0 +1,4 @@
<p>
Published on {% include "articles/snippets/datetime.html" %}
{% include "articles/snippets/admin_link.html" %}
</p>

View file

@ -0,0 +1,11 @@
{% if article %}
<meta name="keywords" content="{{ article.keywords }}">
<meta name="description" content="{{ article.get_description }}">
<meta property="og:title" content="{{ article.title }}" />
<meta property="og:type" content="article">
<meta property="og:description" content="{{ article.get_description }}">
<meta property="og:site_name" content="{{ blog_title }}">
{% endif %}
{% if open_graph_image_url %}
<meta property="og:image" content="{{ open_graph_image_url }}">
{% endif %}

View file

@ -0,0 +1,29 @@
<style>
/* new.css 1.1.2 */
:root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000000;--nc-tx-2:#1A1A1A;--nc-bg-1:#FFFFFF;--nc-bg-2:#F6F8FA;--nc-bg-3:#E5E7EB;--nc-lk-1:#0070F3;--nc-lk-2:#0366D6;--nc-lk-tx:#FFFFFF;--nc-ac-1:#79FFE1;--nc-ac-tx:#0C4047}@media (prefers-color-scheme:dark){:root{--nc-tx-1:#ffffff;--nc-tx-2:#eeeeee;--nc-bg-1:#000000;--nc-bg-2:#111111;--nc-bg-3:#222222;--nc-lk-1:#3291FF;--nc-lk-2:#0070F3;--nc-lk-tx:#FFFFFF;--nc-ac-1:#7928CA;--nc-ac-tx:#FFFFFF}}*{margin:0;padding:0}address,area,article,aside,audio,blockquote,datalist,details,dl,fieldset,figure,form,iframe,img,input,meter,nav,ol,optgroup,option,output,p,pre,progress,ruby,section,table,textarea,ul,video{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:break-word;overflow-wrap:break-word;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2)}abbr:hover{cursor:help}blockquote{padding:1.5rem;background:var(--nc-bg-2);border-left:5px solid var(--nc-bg-3)}abbr{cursor:help}blockquote :last-child{padding-bottom:0;margin-bottom:0}header{background:var(--nc-bg-2);border-bottom:1px solid var(--nc-bg-3);padding:2rem 1.5rem;margin:-2rem calc(0px - (50vw - 50%)) 2rem;padding-left:calc(50vw - 50%);padding-right:calc(50vw - 50%)}header h1,header h2,header h3{padding-bottom:0;border-bottom:0}header>:first-child{margin-top:0;padding-top:0}header>:last-child{margin-bottom:0}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}code,kbd,pre,samp{font-family:var(--nc-font-mono)}code,kbd,pre,samp{background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px;padding:3px 6px;font-size:.9rem}kbd{border-bottom:3px solid var(--nc-bg-3)}pre{padding:1rem 1.4rem;max-width:100%;overflow:auto}pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline;background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}details{padding:.6rem 1rem;background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px}summary{cursor:pointer;font-weight:700}details[open]{padding-bottom:.75rem}details[open] summary{margin-bottom:6px}details[open]>:last-child{margin-bottom:0}dt{font-weight:700}dd::before{content:'→ '}hr{border:0;border-bottom:1px solid var(--nc-bg-3);margin:1rem auto}fieldset{margin-top:1rem;padding:2rem;border:1px solid var(--nc-bg-3);border-radius:4px}legend{padding:auto .5rem}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}table caption{font-weight:700;margin-bottom:.5rem}textarea{max-width:100%}ol,ul{padding-left:2rem}li{margin-top:.4rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}mark{padding:3px 6px;background:var(--nc-ac-1);color:var(--nc-ac-tx)}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}
</style>
<style>
body {
max-width: 640px;
}
footer {
border-top: 2px solid var(--nc-bg-2);
margin-top: 2em;
font-size: 80%;
color: var(--nc-tx-2);
}
footer > :first-child {
margin-top: 1em;
}
.pill {
font-size: 60%;
background-color: var(--nc-tx-2);
color: var(--nc-bg-1);
padding: .5ex 1ex;
border-radius: 1ex;
vertical-align: 15%;
}
</style>

View file

@ -0,0 +1,23 @@
<style>
.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;
}
.pagination {
display: flex;
justify-content: space-between;
}
</style>

View file

@ -1,7 +1,7 @@
import pytest import pytest
from django.utils import timezone from django.utils import timezone
from articles.models import Article, Page, User from articles.models import Article, User
@pytest.fixture() @pytest.fixture()
@ -40,17 +40,3 @@ def unpublished_article(author: User) -> Article:
slug="some-draft-article-slug", slug="some-draft-article-slug",
content="## some draft article markdown\n\n[a draft article link](https://article.com)", content="## some draft article markdown\n\n[a draft article link](https://article.com)",
) )
@pytest.fixture()
@pytest.mark.django_db
def published_page(author: User) -> Page:
return Page.objects.create(
title="Some interesting page title",
status=Article.PUBLISHED,
author=author,
published_at=timezone.now(),
slug="some-page-slug",
content="## some page markdown\n\n[a page link](https://page.com)",
position=2,
)

View file

@ -3,7 +3,7 @@ from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from articles.models import Article, Page, User from articles.models import Article, User
from articles.views.feeds import CompleteFeed from articles.views.feeds import CompleteFeed
@ -22,11 +22,3 @@ def test_feed_limits_number_of_articles(client: Client, author: User):
res = client.get(reverse("complete-feed")) res = client.get(reverse("complete-feed"))
content = res.content.decode("utf-8") content = res.content.decode("utf-8")
assert content.count("<item>") == CompleteFeed.FEED_LIMIT assert content.count("<item>") == CompleteFeed.FEED_LIMIT
@pytest.mark.django_db
def test_page_not_rendered_in_feed(client: Client, published_page: Page):
res = client.get(reverse("complete-feed"))
assert res.status_code == 200
content = res.content.decode("utf-8")
assert published_page.title not in content

View file

@ -3,20 +3,16 @@ from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from articles.models import Article, Page, User from articles.models import Article, User
@pytest.mark.django_db @pytest.mark.django_db
def test_can_access_list( def test_can_access_list(client: Client, published_article: Article):
client: Client, published_article: Article, published_page: Page
):
res = client.get(reverse("articles-list")) res = client.get(reverse("articles-list"))
assert res.status_code == 200 assert res.status_code == 200
content = res.content.decode("utf-8") content = res.content.decode("utf-8")
for art in [published_article, published_page]: assert published_article.title in content
assert art.title in content
assert published_article.get_abstract() not in content assert published_article.get_abstract() not in content
assert published_page.get_formatted_content() not in content
@pytest.mark.django_db @pytest.mark.django_db
@ -43,11 +39,6 @@ def test_access_article_by_slug(client: Client, published_article: Article):
_test_access_article_by_slug(client, published_article) _test_access_article_by_slug(client, published_article)
@pytest.mark.django_db
def test_access_page_by_slug(client: Client, published_page: Page):
_test_access_article_by_slug(client, published_page)
def _test_access_article_by_slug(client: Client, item: Article): def _test_access_article_by_slug(client: Client, item: Article):
res = client.get(reverse("article-detail", kwargs={"slug": item.slug})) res = client.get(reverse("article-detail", kwargs={"slug": item.slug}))
assert res.status_code == 200 assert res.status_code == 200

View file

@ -11,7 +11,7 @@ class CompleteFeed(Feed):
description = settings.BLOG["description"] description = settings.BLOG["description"]
def items(self): def items(self):
return Article.without_pages.filter(status=Article.PUBLISHED).order_by( return Article.objects.filter(status=Article.PUBLISHED).order_by(
"-published_at" "-published_at"
)[: self.FEED_LIMIT] )[: self.FEED_LIMIT]

View file

@ -1,11 +1,9 @@
from typing import Union
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import F from django.db.models import F
from django.views import generic from django.views import generic
from articles.models import Article, Page from articles.models import Article
class BaseArticleListView(generic.ListView): class BaseArticleListView(generic.ListView):
@ -21,14 +19,14 @@ class BaseArticleListView(generic.ListView):
class ArticlesListView(BaseArticleListView): class ArticlesListView(BaseArticleListView):
model = Article model = Article
context_object_name = "articles" context_object_name = "articles"
queryset = Article.without_pages.filter(status=Article.PUBLISHED) queryset = Article.objects.filter(status=Article.PUBLISHED)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
index_page = Page.objects.filter( home_article = Article.objects.filter(
status=Article.PUBLISHED, position=0 status=Article.PUBLISHED, is_home=True
).first() # type: Page ).first() # type: Article
context["article"] = index_page context["article"] = home_article
return context return context
@ -55,10 +53,8 @@ class ArticleDetailView(generic.DetailView):
return queryset return queryset
return queryset.filter(status=Article.PUBLISHED) return queryset.filter(status=Article.PUBLISHED)
def get_object(self, queryset=None) -> Union[Article, Page]: def get_object(self, queryset=None) -> Article:
obj = super().get_object(queryset) # type: Article obj = super().get_object(queryset) # type: Article
if hasattr(obj, "page"):
obj = obj.page # type: Page
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
obj.views_count = F("views_count") + 1 obj.views_count = F("views_count") + 1
obj.save(update_fields=["views_count"]) obj.save(update_fields=["views_count"])

View file

@ -96,12 +96,12 @@ TEMPLATES = [
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"articles.context_processors.pages",
"articles.context_processors.drafts_count", "articles.context_processors.drafts_count",
"articles.context_processors.date_format", "articles.context_processors.date_format",
"articles.context_processors.git_version", "articles.context_processors.git_version",
"articles.context_processors.plausible", "articles.context_processors.plausible",
"articles.context_processors.open_graph_image_url", "articles.context_processors.open_graph_image_url",
"articles.context_processors.blog_metadata",
], ],
}, },
}, },
@ -176,6 +176,7 @@ AUTH_USER_MODEL = "articles.User"
BLOG = { BLOG = {
"title": "Gab's Notes", "title": "Gab's Notes",
"author": "Gabriel Augendre",
"description": "My take on tech-related subjects (but not only).", "description": "My take on tech-related subjects (but not only).",
"base_url": os.getenv("BLOG_BASE_URL", "https://gabnotes.org/"), "base_url": os.getenv("BLOG_BASE_URL", "https://gabnotes.org/"),
"repo": { "repo": {