Numerous improvements to blog app
- Added a new method to Comment, telling whether the comment is brand new or not. - Fix the order in which comments appear below a given article. - When submitting a comment, a snackbar now pops up informing the user whether submission was succesful. - Changes to the presentation in the templates.
- Author
- Maarten Vangeneugden
- Date
- April 3, 2022, 7:33 p.m.
- Hash
- 226361afe87e17f982d6457e1e83d315faf0fd18
- Parent
- c20135034fa1e7ba77d5b15c18b947742c03f1aa
- Modified files
- models.py
- templates/blog/comment.djhtml
- templates/blog/post.djhtml
- views.py
models.py ¶
7 additions and 0 deletions.
View changes Hide changes
1 |
1 |
from django.utils import translation |
2 |
2 |
from django.template.defaultfilters import slugify |
3 |
3 |
from django.db import models |
4 |
4 |
from django.conf import settings # Necessary to get the link to the media root folder |
5 |
5 |
import datetime |
+ |
6 |
import datetime |
6 |
7 |
import os |
7 |
8 |
import subprocess |
8 |
9 |
|
9 |
10 |
from django.shortcuts import render as render_shortcut |
10 |
11 |
|
11 |
12 |
""" New version: |
12 |
13 |
- For each post, there's no longer a mandatory Dutch and English |
13 |
14 |
version. Instead, only the title needs to be in multiple languages. |
14 |
15 |
- There's a new table for the links to the articles themselves. These include a |
15 |
16 |
language code and a foreign key to the post they belong to. |
16 |
17 |
- If an article is available in the active language, but not tagged for the same |
17 |
18 |
dialect, then it should just show up without any warnings. |
18 |
19 |
- If an article is not available in the active language, only the title should |
19 |
20 |
show up, but where the short intro text would normally be, there should be an |
20 |
21 |
explanation that it's only available in other languages, and provide links to |
21 |
22 |
those versions. |
22 |
23 |
""" |
23 |
24 |
|
24 |
25 |
# Look, you think this function is worthless, it's not. It's required to make |
25 |
26 |
# migrations with manage.py, so here it stays, being empty and hollow like the |
26 |
27 |
# piece of shit it is. |
27 |
28 |
def post_title_directory(): |
28 |
29 |
pass |
29 |
30 |
|
30 |
31 |
|
31 |
32 |
class Post(models.Model): |
32 |
33 |
""" Represents a blog post.""" |
33 |
34 |
published = models.DateTimeField(auto_now_add=True) |
34 |
35 |
visible = models.BooleanField(default=True, |
35 |
36 |
help_text="Whether this post is shown in the index. If False, it's \ |
36 |
37 |
only accessible by direct link.") |
37 |
38 |
# TODO: The titles should all be changed to 'unique=True' to avoid slug |
38 |
39 |
# collisions. But at this moment, there are still some posts that don't have |
39 |
40 |
# a title in all these languages (and "" collides with ""), so until that's |
40 |
41 |
# fixed, they're set to False. |
41 |
42 |
title_en = models.CharField(max_length=64, unique=False, blank=False) |
42 |
43 |
title_nl = models.CharField(max_length=64, unique=False, blank=False) |
43 |
44 |
title_fr = models.CharField(max_length=64, unique=False, blank=True) |
44 |
45 |
title_de = models.CharField(max_length=64, unique=False, blank=True) |
45 |
46 |
title_es = models.CharField(max_length=64, unique=False, blank=True) |
46 |
47 |
title_eo = models.CharField(max_length=64, unique=False, blank=True) |
47 |
48 |
title_af = models.CharField(max_length=64, unique=False, blank=True) |
48 |
49 |
title_nl_be=models.CharField(max_length=64, unique=False, blank=True) |
49 |
50 |
title_fr_be=models.CharField(max_length=64, unique=False, blank=True) |
50 |
51 |
|
51 |
52 |
|
52 |
53 |
def __str__(self): |
53 |
54 |
return self.title() |
54 |
55 |
|
55 |
56 |
|
56 |
57 |
def articles(self): |
57 |
58 |
#print(len(Article.objects.filter(post=self))) |
58 |
59 |
return Article.objects.filter(post=self) |
59 |
60 |
|
60 |
61 |
def article(self): |
61 |
62 |
language_code = translation.get_language() |
62 |
63 |
print(language_code) |
63 |
64 |
# Retrieves all articles that have this post as their foreign key |
64 |
65 |
articles = Article.objects.filter(post=self) |
65 |
66 |
for a in articles: |
66 |
67 |
if a.language_code == language_code: |
67 |
68 |
return a |
68 |
69 |
# If no exact match was found, try again, but now accept other dialects |
69 |
70 |
# as well: |
70 |
71 |
for a in articles: |
71 |
72 |
if a.language_code.startswith(language_code): |
72 |
73 |
return a |
73 |
74 |
|
74 |
75 |
# If still no article was found, return None |
75 |
76 |
return None |
76 |
77 |
|
77 |
78 |
def title(self): |
78 |
79 |
language_code = translation.get_language() |
79 |
80 |
options = {'af': self.title_af, |
80 |
81 |
'de': self.title_de, |
81 |
82 |
'es': self.title_es, |
82 |
83 |
'en': self.title_en, |
83 |
84 |
'eo': self.title_eo, |
84 |
85 |
'fr': self.title_fr, |
85 |
86 |
'nl-be': self.title_nl_be, |
86 |
87 |
'fr-be': self.title_fr_be, |
87 |
88 |
'nl': self.title_nl} |
88 |
89 |
for code, translated_title in options.items(): |
89 |
90 |
if language_code.startswith(code): |
90 |
91 |
return translated_title |
91 |
92 |
# If no return has happened, default to English |
92 |
93 |
return self.title_en |
93 |
94 |
|
94 |
95 |
def org_to_html(file_path, return_djhtml_path=False): |
95 |
96 |
""" Converts the given org formatted file to HTML. |
96 |
97 |
This function directly returns the resulting HTML code. This function uses |
97 |
98 |
the amazing Haskell library Pandoc to convert the file (and takes care |
98 |
99 |
of header id's and all that stuff). |
99 |
100 |
""" |
100 |
101 |
# FIXME: Remove hardcoded link to media. Replace with media tag! |
101 |
102 |
# XXX: The reason I'm first converting all occurences of .jpg][ and .png][ |
102 |
103 |
# to .jpgPANDOCBUG][ and .pngPANDOCBUG][, is because of a Pandoc bug that |
103 |
104 |
# removes the text links for images. It is afterwards converted back, no |
104 |
105 |
# worries. |
105 |
106 |
file = open(file_path, "r", encoding="utf-8") |
106 |
107 |
text = file.read() |
107 |
108 |
file.close() |
108 |
109 |
text = text.replace(".jpg][", ".jpgPANDOCBUG][") |
109 |
110 |
text = text.replace(".png][", ".pngPANDOCBUG][") |
110 |
111 |
file = open("/tmp/blog-file.org", "w", encoding="utf-8") |
111 |
112 |
file.write(text) |
112 |
113 |
file.close() |
113 |
114 |
html_text = subprocess.check_output(["pandoc", "--from=org", "--to=html","/tmp/blog-file.org"]) |
114 |
115 |
html_text = html_text.decode("utf-8").replace(".jpgPANDOCBUG", ".jpg") |
115 |
116 |
html_text = html_text.replace(".pngPANDOCBUG", ".png") |
116 |
117 |
#rendered_file_path = "file_path.rpartition('.')[0] + ".djhtml" |
117 |
118 |
rendered_file_path = "/tmp/blog-file.djhtml" |
118 |
119 |
rendered_file = open(rendered_file_path, "w", encoding="utf-8") |
119 |
120 |
rendered_file.write(html_text) |
120 |
121 |
rendered_file.close() |
121 |
122 |
if return_djhtml_path: |
122 |
123 |
return rendered_file_path |
123 |
124 |
else: |
124 |
125 |
return html_text |
125 |
126 |
|
126 |
127 |
class Article(models.Model): |
127 |
128 |
AFRIKAANS = 'af' |
128 |
129 |
BELGIAN_FRENCH = 'fr-be' |
129 |
130 |
DUTCH = 'nl' |
130 |
131 |
ESPERANTO = 'eo' |
131 |
132 |
ENGLISH = 'en' |
132 |
133 |
FLEMISH = 'nl-be' |
133 |
134 |
FRENCH = 'fr' |
134 |
135 |
GERMAN = 'de' |
135 |
136 |
SPANISH = 'es' |
136 |
137 |
|
137 |
138 |
LANGUAGE_CODES = [ |
138 |
139 |
(AFRIKAANS, 'Afrikaans'), |
139 |
140 |
(BELGIAN_FRENCH, 'Français (Belgique)'), |
140 |
141 |
(DUTCH, 'Nederlands'), |
141 |
142 |
(ESPERANTO, 'Esperanto'), |
142 |
143 |
(ENGLISH, 'English'), |
143 |
144 |
(FLEMISH, 'Vlaams'), |
144 |
145 |
(FRENCH, 'Français'), |
145 |
146 |
(GERMAN, 'Deutsch'), |
146 |
147 |
(SPANISH, 'Español')] |
147 |
148 |
|
148 |
149 |
visible = models.BooleanField(default=True) |
149 |
150 |
post = models.ForeignKey(Post, on_delete=models.CASCADE) |
150 |
151 |
language_code = models.CharField(max_length=16, |
151 |
152 |
choices = LANGUAGE_CODES, |
152 |
153 |
blank=False) |
153 |
154 |
# file_path shouldn't be unique, because the same article file could be used |
154 |
155 |
# for multiple dialects of the same language. |
155 |
156 |
file_path = models.FilePathField(path=settings.MEDIA_ROOT + "blog/articles/", |
156 |
157 |
blank=False) |
157 |
158 |
# Same reason, slug shouldn't be unique |
158 |
159 |
slug = models.SlugField(unique=False, blank=False, allow_unicode=True) |
159 |
160 |
title = models.CharField(max_length=64, unique=False, blank=True) |
160 |
161 |
|
161 |
162 |
def text(self): |
162 |
163 |
return org_to_html(self.file_path) |
163 |
164 |
def djhtml_file(self): |
164 |
165 |
return org_to_html(self.file_path, return_djhtml_path=True) |
165 |
166 |
|
166 |
167 |
|
167 |
168 |
class Comment(models.Model): |
168 |
169 |
""" Represents a comment on a blog post. |
169 |
170 |
Comments are not filtered by language; a |
170 |
171 |
comment made by someone reading the article in Dutch, that's written in |
171 |
172 |
Dutch, will show up (unedited) for somebody whom's reading the Spanish |
172 |
173 |
version. |
173 |
174 |
""" |
174 |
175 |
# Allows me to manually hide certain messages if need be |
175 |
176 |
visible = models.BooleanField(default=True) |
176 |
177 |
date = models.DateTimeField(auto_now_add=True) |
177 |
178 |
name = models.CharField(max_length=64, blank=True) |
178 |
179 |
text = models.TextField(max_length=10000, blank=False) # Should be more than enough |
179 |
180 |
# reaction_to is null if it's not a reaction to an existing comment |
180 |
181 |
reaction_to = models.ForeignKey('Comment', on_delete=models.CASCADE, null=True) |
181 |
182 |
from_myself = models.BooleanField(default=False) |
182 |
183 |
post = models.ForeignKey( |
183 |
184 |
Post, |
184 |
185 |
on_delete=models.CASCADE, |
185 |
186 |
null=False, |
186 |
187 |
) |
187 |
188 |
class meta: |
188 |
189 |
ordering = ['date'] # When printed, prints the oldest comment first. |
189 |
190 |
|
190 |
191 |
def reactions(self): |
191 |
192 |
# Should return the comments that are a reaction to this comment |
192 |
193 |
return Comment.objects.filter(reaction_to=self).order_by('-date') |
193 |
194 |
def __str__(self): |
194 |
195 |
return str(self.id) +" | "+ self.name |
195 |
196 |
|
+ |
197 |
# True if this comment was created less than one minute ago. |
+ |
198 |
now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC) |
+ |
199 |
delta = now-self.date |
+ |
200 |
return delta.seconds < 60 |
+ |
201 |
|
+ |
202 |
|
196 |
203 |
class FeedItem(models.Model): |
197 |
204 |
""" An item that shows up in the RSS feed.""" |
198 |
205 |
title = models.CharField(max_length=64) |
199 |
206 |
added = models.DateTimeField(auto_now_add=True) |
200 |
207 |
description = models.CharField(max_length=400) |
201 |
208 |
link = models.URLField() |
202 |
209 |
templates/blog/comment.djhtml ¶
17 additions and 5 deletions.
View changes Hide changes
1 |
1 |
{% load humanize %} |
2 |
2 |
|
3 |
3 |
<div class="comment" id="reago-{{ comment.id }}"> |
+ |
4 |
{# Check if this comment was added just now #} |
+ |
5 |
{% if comment.is_new %} |
+ |
6 |
<div class="comment recently-posted" id="reago-{{ comment.id }}"> |
+ |
7 |
{% else %} |
+ |
8 |
<div class="comment" id="reago-{{ comment.id }}"> |
4 |
9 |
<p><a href="#reago-{{ comment.id }}">#{{ comment.id }}</a> |
+ |
10 |
|
+ |
11 |
|
+ |
12 |
<!--<div class="comment recently-posted" id="reago-{{ comment.id }}">--> |
+ |
13 |
<p><a href="#reago-{{ comment.id }}">#{{ comment.id }}</a> |
5 |
14 |
{{ comment.name }} | |
6 |
15 |
{{ comment.date|naturaltime }} |
7 |
16 |
({{ comment.date|date:"SHORT_DATE_FORMAT" }}) |
8 |
- | </p> |
+ |
17 |
</p> |
9 |
18 |
<p>{{ comment.text|urlize }}</p> |
10 |
19 |
<details> |
11 |
20 |
<summary>{% translate "Respond" %}</summary> |
12 |
- | <form method="POST"> |
13 |
- | {% csrf_token %} |
+ |
21 |
{# Doing action="#reago-{{ comment.id }}" is a clever hack if I say so #} |
+ |
22 |
{# myself; it makes sure that, after submission, the user is immediately #} |
+ |
23 |
{# redirected to the comment he responded to, instead of going to the #} |
+ |
24 |
{# top again. Pretty neat huh? =D #} |
+ |
25 |
<form method="POST" action="#reago-{{ comment.id }}"> |
+ |
26 |
{% csrf_token %} |
14 |
27 |
<input type="hidden" name="reaction_to" value="{{ comment.id }}"> |
15 |
28 |
<input type="hidden" name="post" value="{{ article.post.id }}"> |
16 |
29 |
<input type="text" id="name-{{ comment.id }}" name="name" maxlength="64" required> |
17 |
30 |
<label for="name-{{ comment.id }}">{% translate "Your name" %}</label><br> |
18 |
31 |
<textarea name="text" id="text-{{ comment.id }}" maxlength="10000" required></textarea> |
19 |
- | <label for="text-{{ comment.id }}">{% translate "Your comment" %}</label><br> |
20 |
- | <input type="submit" value="{% translate "Submit" %}"> |
+ |
32 |
<input type="submit" value="{% translate "Submit" %}"> |
21 |
33 |
</form> |
22 |
34 |
</details> |
23 |
35 |
{% for subcomment in comment.reactions %} |
24 |
36 |
{% include "blog/comment.djhtml" with comment=subcomment %} |
25 |
37 |
{% endfor %} |
26 |
38 |
</div> |
27 |
39 |
templates/blog/post.djhtml ¶
20 additions and 5 deletions.
View changes Hide changes
1 |
1 |
{% load humanize %} |
2 |
2 |
{% load i18n %} |
3 |
3 |
{% load static %} |
4 |
4 |
|
5 |
5 |
|
6 |
6 |
{% block stylesheets %} |
7 |
7 |
{{ block.super }} |
8 |
8 |
<style> |
9 |
9 |
@font-face { |
10 |
10 |
font-family: 'Merriweather'; |
11 |
11 |
font-style: italic; |
12 |
12 |
font-weight: 400; |
13 |
13 |
src: url({% get_static_prefix %}fonts/merriweather-400-italic.woff2) format('woff2'); |
14 |
14 |
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
15 |
15 |
} |
16 |
16 |
@font-face { |
17 |
17 |
font-family: 'Merriweather'; |
18 |
18 |
font-style: normal; |
19 |
19 |
font-weight: 400; |
20 |
20 |
src: url({% get_static_prefix %}fonts/merriweather-400-regular.woff2) format('woff2'); |
21 |
21 |
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
22 |
22 |
} |
23 |
23 |
@font-face { |
24 |
24 |
font-family: 'Merriweather'; |
25 |
25 |
font-style: normal; |
26 |
26 |
font-weight: 700; |
27 |
27 |
src: url({% get_static_prefix %}fonts/merriweather-700-regular.woff2) format('woff2'); |
28 |
28 |
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
29 |
29 |
} |
30 |
30 |
|
31 |
31 |
body { |
32 |
32 |
background-color: #0d1521; /*#0d47a1; /* Material blue P900 */ |
33 |
33 |
} |
34 |
34 |
body video { |
35 |
35 |
width: 80%; |
36 |
36 |
} |
37 |
37 |
header { |
38 |
38 |
background-color: #3e2723; |
39 |
39 |
} |
40 |
40 |
section.article { |
41 |
41 |
background-color: #efebe9;/*rgb(210, 188, 157);*/ |
42 |
42 |
font-family: Merriweather, serif; |
43 |
43 |
} |
44 |
44 |
|
45 |
45 |
.comment { |
46 |
46 |
margin-left: 1em; |
47 |
- | padding-left: 1em; |
48 |
- | border-left-style: solid; |
+ |
47 |
padding-left: 0.5em; |
+ |
48 |
padding-bottom: 0.5em; |
+ |
49 |
border-left-style: solid; |
49 |
50 |
border-color: var(--primary); |
50 |
51 |
} |
51 |
52 |
</style> |
+ |
53 |
background-color: var(--P100); |
+ |
54 |
} |
+ |
55 |
|
+ |
56 |
span.small-detail { |
+ |
57 |
font-size: smaller; |
+ |
58 |
color: grey; |
+ |
59 |
} |
+ |
60 |
</style> |
52 |
61 |
{% endblock stylesheets %} |
53 |
62 |
|
54 |
63 |
{% block description %} |
55 |
64 |
{{ article.text|safe|truncatewords_html:10 }} |
56 |
65 |
{% endblock description %} |
57 |
66 |
{% block title %}📚 {{ navbar_title }}{% endblock title %} |
58 |
67 |
|
59 |
68 |
|
60 |
69 |
{% block header %} |
61 |
70 |
<header> |
62 |
71 |
<h1>{{ navbar_title }}</h1> |
63 |
72 |
</header> |
64 |
73 |
{% endblock header %} |
65 |
74 |
{% block main %} |
66 |
75 |
<section class="article"> |
67 |
76 |
<!--<article style="font-family:serif;">--> |
68 |
77 |
{#{{ article.text|safe }}#} |
69 |
78 |
{% include "/tmp/blog-file.djhtml" %} |
70 |
79 |
|
71 |
80 |
<!--</article>--> |
72 |
81 |
</section> |
73 |
82 |
<section class="reagoj"> |
+ |
83 |
{% if comment_response %} |
+ |
84 |
<div class="snackbar"> |
+ |
85 |
{{ comment_response }} |
+ |
86 |
</div> |
+ |
87 |
{% endif %} |
+ |
88 |
|
+ |
89 |
<section class="reagoj"> |
74 |
90 |
<h2>{% translate "Comments" %}</h2> |
75 |
91 |
<form method="POST"> |
76 |
- | {% csrf_token %} |
+ |
92 |
{% csrf_token %} |
77 |
93 |
<input type="hidden" name="reaction_to" value=""> |
78 |
94 |
<input type="hidden" name="post" value="{{ article.post.id }}"> |
79 |
95 |
<input type="text" id="name-root" name="name" maxlength="64" required> |
80 |
96 |
<label for="name-root">{% translate "Your name" %}</label><br> |
81 |
97 |
<textarea name="text" id="text-root" maxlength="10000" required></textarea> |
82 |
- | <label for="text-root">{% translate "Your comment" %}</label><br> |
83 |
- | <input type="submit" value="{% translate "Submit" %}"> |
+ |
98 |
<input type="submit" value="{% translate "Submit" %}"> |
84 |
99 |
</form> |
85 |
100 |
<hr> |
86 |
101 |
{% for root_comment in root_comments %} |
87 |
102 |
{% include "blog/comment.djhtml" with comment=root_comment %} |
88 |
103 |
{% endfor %} |
89 |
104 |
</section> |
90 |
105 |
|
91 |
106 |
{% comment %} |
92 |
107 |
<h5 class="white-text">{% trans "This article in other languages" %}</h5> |
93 |
108 |
|
94 |
109 |
{% get_language_info for 'nl' as LANG %} |
95 |
110 |
<a {% if dutch_link %} href="{{dutch_link}}" {% endif %} |
96 |
111 |
class="btn fill |
97 |
112 |
{% if not dutch_link %}disabled{% endif %}"> |
98 |
113 |
🇧🇪 {{ LANG.name_translated}} 🇳🇱 |
99 |
114 |
</a> |
100 |
115 |
{% get_current_language as lang %} |
101 |
116 |
{% get_language_info for 'fr' as LANG %} |
102 |
117 |
<a {% if french_link %} href="{{french_link}}" {% endif %} |
103 |
118 |
class="btn fill |
104 |
119 |
{% if not french_link %}disabled{% endif %}"> |
105 |
120 |
🇧🇪 {{ LANG.name_translated}} 🇫🇷 |
106 |
121 |
</a> |
107 |
122 |
{% get_language_info for 'en' as LANG %} |
108 |
123 |
<a {% if english_link %} href="{{english_link}}" {% endif %} |
109 |
124 |
class="btn fill |
110 |
125 |
{% if not english_link %}disabled{% endif %}"> |
111 |
126 |
🇬🇧 {{ LANG.name_translated}} 🇺🇸 |
112 |
127 |
</a> |
113 |
128 |
{% get_language_info for 'de' as LANG %} |
114 |
129 |
<a {% if german_link %} href="{{german_link}}" {% endif %} |
115 |
130 |
class="btn fill |
116 |
131 |
{% if not german_link %}disabled{% endif %}"> |
117 |
132 |
🇧🇪 {{ LANG.name_translated}} 🇩🇪 |
118 |
133 |
</a> |
119 |
134 |
{% get_language_info for 'es' as LANG %} |
120 |
135 |
<a {% if spanish_link %} href="{{spanish_link}}" {% endif %} |
121 |
136 |
class="btn |
122 |
137 |
{% if not spanish_link %}disabled{% endif %}"> |
123 |
138 |
🇪🇸 {{ LANG.name_translated}} 🇲🇽 |
124 |
139 |
</a> |
125 |
140 |
{% endcomment %} |
126 |
141 |
|
127 |
142 |
{% comment %} |
128 |
143 |
<a href="{% url 'blog-post' post_slug %}" class="btn {{accent_color}} accent-4 black-text tooltipped" data-position="bottom" data-delay="50" data-tooltip="{% trans "Multilingual link. Links to the version in the viewer's preferred language." %}">🏳️🌈 {% trans "All available languages" %}</a> |
129 |
144 |
{# TODO: Change to rainbow flag when possible #} |
130 |
145 |
{% endcomment %} |
131 |
146 |
</div> |
132 |
147 |
|
133 |
148 |
</div> |
134 |
149 |
{% endblock main %} |
135 |
150 |
views.py ¶
5 additions and 2 deletions.
View changes Hide changes
1 |
1 |
import requests |
2 |
2 |
|
3 |
3 |
|
4 |
4 |
from django.utils.translation import ugettext as _ |
5 |
5 |
from django.shortcuts import get_object_or_404, render # This allows to render the template with the view here. It's pretty cool and important. |
6 |
6 |
from django.http import HttpResponseRedirect, HttpResponse |
7 |
7 |
from django.urls import reverse |
8 |
8 |
from django.template import loader # This allows to actually load the template. |
9 |
9 |
from .models import * |
10 |
10 |
from .forms import CommentForm |
11 |
11 |
from django.core.exceptions import ObjectDoesNotExist |
12 |
12 |
from django.utils import translation |
13 |
13 |
|
14 |
14 |
GERMAN = "de" |
15 |
15 |
SPANISH = "es" |
16 |
16 |
FRENCH = "fr" |
17 |
17 |
DUTCH = "nl" |
18 |
18 |
ENGLISH = "en" |
19 |
19 |
|
20 |
20 |
def index(request): |
21 |
21 |
template = "blog/index.djhtml" |
22 |
22 |
posts = Post.objects.exclude(visible=False) |
23 |
23 |
|
24 |
24 |
|
25 |
25 |
context = { |
26 |
26 |
'posts': posts, |
27 |
27 |
'navbar_title': _("Notepad from a student"), |
28 |
28 |
'navbar_backArrow': True, |
29 |
29 |
'stylesheet_name': "blog", |
30 |
30 |
} |
31 |
31 |
return render(request, template, context) |
32 |
32 |
|
33 |
33 |
def post(request, language_code, post_slug): |
34 |
34 |
if request.method == "POST": # Handling a reply if one is sent |
+ |
35 |
if request.method == "POST": # Handling a reply if one is sent |
35 |
36 |
form = CommentForm(request.POST) |
36 |
37 |
|
37 |
38 |
form.post = Post.objects.get(id=request.POST['post']) |
38 |
39 |
if form.is_valid(): |
39 |
40 |
new_comment = form.save(commit=False) |
40 |
41 |
if request.POST['reaction_to'] != "": |
41 |
42 |
new_comment.reaction_to = Comment.objects.get(id=request.POST['reaction_to']) |
42 |
43 |
new_comment.save() |
43 |
44 |
else: |
+ |
45 |
else: |
44 |
46 |
print("ERROR") |
45 |
47 |
print(form.errors) |
46 |
48 |
|
+ |
49 |
|
47 |
50 |
template = "blog/post.djhtml" |
48 |
51 |
article = Article.objects.get(slug=post_slug, language_code=language_code) |
49 |
52 |
root_comments = Comment.objects.filter(post=article.post, reaction_to=None) |
50 |
- | context = { |
51 |
- | 'article': article, |
+ |
53 |
context = context | { |
+ |
54 |
'article': article, |
52 |
55 |
'root_comments': root_comments, |
53 |
56 |
'title': article.post.title(), |
54 |
57 |
'navbar_title': article.post.title(), |
55 |
58 |
'navbar_backArrow': True, |
56 |
59 |
'stylesheet_name': "blog"} |
57 |
60 |
|
58 |
61 |
return render(request, template, context) |
59 |
62 |
|
60 |
63 |
def rss(request): |
61 |
64 |
template = "blog/feed.rss" |
62 |
65 |
context = { |
63 |
66 |
'items': FeedItem.objects.all(), |
64 |
67 |
} |
65 |
68 |
return render(request, template, context, content_type="application/rss+xml") |
66 |
69 |
|
67 |
70 |
|
68 |
71 |
def archive(request): |
69 |
72 |
template = "blog/monthly_archive.djhtml" |
70 |
73 |
language = translation.get_language() |
71 |
74 |
|
72 |
75 |
file_2017 = org_to_html("blog/weekly/2017.org") |
73 |
76 |
file_2018 = org_to_html("blog/weekly/2018.org") |
74 |
77 |
file_2019 = org_to_html("blog/weekly/2019.org") |
75 |
78 |
|
76 |
79 |
|
77 |
80 |
|
78 |
81 |
context = { |
79 |
82 |
't2017': file_2017, |
80 |
83 |
't2018': file_2018, |
81 |
84 |
't2019': file_2019, |
82 |
85 |
'materialDesign_color': "brown", |
83 |
86 |
'materialDesign_accentColor': "blue", |
84 |
87 |
'navbar_title': _("Weekly-archief"), |
85 |
88 |
'navbar_backArrow': True, |
86 |
89 |
'footer_links': footer_links, |
87 |
90 |
'footer_description': footer_description, |
88 |
91 |
'stylesheet_name': "blog", |
89 |
92 |
} |
90 |
93 |
return render(request, template, context) |
91 |
94 |