Remove some bugs pertaining to previous commit
- Remove a redundant piece of the comments section - Fix a bug in the CommentForm where it required a slug of the Post. It now requires the ID of the Post. - Fix a bug in views.post where it would not accept comments to comments, including some other bugs in that system. - Remove the Python comment generator, since the DTL version supports recursion properly, takes the context into account ánd is a lot cleaner to work with.
- Author
- Maarten Vangeneugden
- Date
- April 3, 2022, 2:15 p.m.
- Hash
- c20135034fa1e7ba77d5b15c18b947742c03f1aa
- Parent
- 05280c1b832e2534eef2b40eac3a07e0653ff4b9
- Modified files
- forms.py
- migrations/0012_auto_20220403_1326.py
- models.py
- templates/blog/comment.djhtml
- templates/blog/post.djhtml
- views.py
forms.py ¶
1 addition and 1 deletion.
migrations/0012_auto_20220403_1326.py ¶
33 additions and 0 deletions.
View changes Hide changes
+ |
1 |
|
+ |
2 |
from django.db import migrations, models |
+ |
3 |
|
+ |
4 |
|
+ |
5 |
class Migration(migrations.Migration): |
+ |
6 |
|
+ |
7 |
dependencies = [ |
+ |
8 |
('blog', '0011_auto_20220220_1906'), |
+ |
9 |
] |
+ |
10 |
|
+ |
11 |
operations = [ |
+ |
12 |
migrations.AddField( |
+ |
13 |
model_name='comment', |
+ |
14 |
name='from_myself', |
+ |
15 |
field=models.BooleanField(default=False), |
+ |
16 |
), |
+ |
17 |
migrations.AddField( |
+ |
18 |
model_name='post', |
+ |
19 |
name='title_fr_be', |
+ |
20 |
field=models.CharField(blank=True, max_length=64), |
+ |
21 |
), |
+ |
22 |
migrations.AddField( |
+ |
23 |
model_name='post', |
+ |
24 |
name='title_nl_be', |
+ |
25 |
field=models.CharField(blank=True, max_length=64), |
+ |
26 |
), |
+ |
27 |
migrations.AddField( |
+ |
28 |
model_name='post', |
+ |
29 |
name='visible', |
+ |
30 |
field=models.BooleanField(default=True, help_text="Whether this post is shown in the index. If False, it's only accessible by direct link."), |
+ |
31 |
), |
+ |
32 |
] |
+ |
33 |
models.py ¶
15 additions and 9 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 |
6 |
import os |
7 |
7 |
import subprocess |
8 |
8 |
|
9 |
9 |
from django.shortcuts import render as render_shortcut |
10 |
10 |
|
11 |
11 |
""" New version: |
12 |
12 |
- For each post, there's no longer a mandatory Dutch and English |
13 |
13 |
version. Instead, only the title needs to be in multiple languages. |
14 |
14 |
- There's a new table for the links to the articles themselves. These include a |
15 |
15 |
language code and a foreign key to the post they belong to. |
16 |
16 |
- If an article is available in the active language, but not tagged for the same |
17 |
17 |
dialect, then it should just show up without any warnings. |
18 |
18 |
- If an article is not available in the active language, only the title should |
19 |
19 |
show up, but where the short intro text would normally be, there should be an |
20 |
20 |
explanation that it's only available in other languages, and provide links to |
21 |
21 |
those versions. |
22 |
22 |
""" |
23 |
23 |
|
24 |
24 |
# Look, you think this function is worthless, it's not. It's required to make |
25 |
25 |
# migrations with manage.py, so here it stays, being empty and hollow like the |
26 |
26 |
# piece of shit it is. |
27 |
27 |
def post_title_directory(): |
28 |
28 |
pass |
29 |
29 |
|
30 |
30 |
|
31 |
31 |
class Post(models.Model): |
32 |
32 |
""" Represents a blog post.""" |
33 |
33 |
published = models.DateTimeField(auto_now_add=True) |
34 |
34 |
visible = models.BooleanField(default=True, |
35 |
35 |
help_text="Whether this post is shown in the index. If False, it's \ |
36 |
36 |
only accessible by direct link.") |
37 |
37 |
title_en = models.CharField(max_length=64, unique=True, blank=False) |
38 |
- | title_nl = models.CharField(max_length=64, unique=True, blank=False) |
39 |
- | title_fr = models.CharField(max_length=64, unique=True, blank=True) |
40 |
- | title_de = models.CharField(max_length=64, unique=True, blank=True) |
41 |
- | title_es = models.CharField(max_length=64, unique=True, blank=True) |
42 |
- | title_eo = models.CharField(max_length=64, unique=True, blank=True) |
43 |
- | title_af = models.CharField(max_length=64, unique=True, blank=True) |
44 |
- | title_nl_be=models.CharField(max_length=64, unique=True, blank=True) |
45 |
- | title_fr_be=models.CharField(max_length=64, unique=True, blank=True) |
46 |
- | |
+ |
38 |
# collisions. But at this moment, there are still some posts that don't have |
+ |
39 |
# a title in all these languages (and "" collides with ""), so until that's |
+ |
40 |
# fixed, they're set to False. |
+ |
41 |
title_en = models.CharField(max_length=64, unique=False, blank=False) |
+ |
42 |
title_nl = models.CharField(max_length=64, unique=False, blank=False) |
+ |
43 |
title_fr = models.CharField(max_length=64, unique=False, blank=True) |
+ |
44 |
title_de = models.CharField(max_length=64, unique=False, blank=True) |
+ |
45 |
title_es = models.CharField(max_length=64, unique=False, blank=True) |
+ |
46 |
title_eo = models.CharField(max_length=64, unique=False, blank=True) |
+ |
47 |
title_af = models.CharField(max_length=64, unique=False, blank=True) |
+ |
48 |
title_nl_be=models.CharField(max_length=64, unique=False, blank=True) |
+ |
49 |
title_fr_be=models.CharField(max_length=64, unique=False, blank=True) |
+ |
50 |
|
47 |
51 |
|
48 |
52 |
def __str__(self): |
49 |
53 |
return self.title() |
50 |
54 |
|
51 |
55 |
|
52 |
56 |
def articles(self): |
53 |
57 |
#print(len(Article.objects.filter(post=self))) |
54 |
58 |
return Article.objects.filter(post=self) |
55 |
59 |
|
56 |
60 |
def article(self): |
57 |
61 |
language_code = translation.get_language() |
58 |
62 |
print(language_code) |
59 |
63 |
# Retrieves all articles that have this post as their foreign key |
60 |
64 |
articles = Article.objects.filter(post=self) |
61 |
65 |
for a in articles: |
62 |
66 |
if a.language_code == language_code: |
63 |
67 |
return a |
64 |
68 |
# If no exact match was found, try again, but now accept other dialects |
65 |
69 |
# as well: |
66 |
70 |
for a in articles: |
67 |
71 |
if a.language_code.startswith(language_code): |
68 |
72 |
return a |
69 |
73 |
|
70 |
74 |
# If still no article was found, return None |
71 |
75 |
return None |
72 |
76 |
|
73 |
77 |
def title(self): |
74 |
78 |
language_code = translation.get_language() |
75 |
79 |
options = {'af': self.title_af, |
76 |
80 |
'de': self.title_de, |
77 |
81 |
'es': self.title_es, |
78 |
82 |
'en': self.title_en, |
79 |
83 |
'eo': self.title_eo, |
80 |
84 |
'fr': self.title_fr, |
81 |
85 |
'nl-be': self.title_nl_be, |
82 |
86 |
'fr-be': self.title_fr_be, |
83 |
87 |
'nl': self.title_nl} |
84 |
88 |
for code, translated_title in options.items(): |
85 |
89 |
if language_code.startswith(code): |
86 |
90 |
return translated_title |
87 |
91 |
# If no return has happened, default to English |
88 |
92 |
return self.title_en |
89 |
93 |
|
90 |
94 |
def org_to_html(file_path, return_djhtml_path=False): |
91 |
95 |
""" Converts the given org formatted file to HTML. |
92 |
96 |
This function directly returns the resulting HTML code. This function uses |
93 |
97 |
the amazing Haskell library Pandoc to convert the file (and takes care |
94 |
98 |
of header id's and all that stuff). |
95 |
99 |
""" |
96 |
100 |
# FIXME: Remove hardcoded link to media. Replace with media tag! |
97 |
101 |
# XXX: The reason I'm first converting all occurences of .jpg][ and .png][ |
98 |
102 |
# to .jpgPANDOCBUG][ and .pngPANDOCBUG][, is because of a Pandoc bug that |
99 |
103 |
# removes the text links for images. It is afterwards converted back, no |
100 |
104 |
# worries. |
101 |
105 |
file = open(file_path, "r", encoding="utf-8") |
102 |
106 |
text = file.read() |
103 |
107 |
file.close() |
104 |
108 |
text = text.replace(".jpg][", ".jpgPANDOCBUG][") |
105 |
109 |
text = text.replace(".png][", ".pngPANDOCBUG][") |
106 |
110 |
file = open("/tmp/blog-file.org", "w", encoding="utf-8") |
107 |
111 |
file.write(text) |
108 |
112 |
file.close() |
109 |
113 |
html_text = subprocess.check_output(["pandoc", "--from=org", "--to=html","/tmp/blog-file.org"]) |
110 |
114 |
html_text = html_text.decode("utf-8").replace(".jpgPANDOCBUG", ".jpg") |
111 |
115 |
html_text = html_text.replace(".pngPANDOCBUG", ".png") |
112 |
116 |
#rendered_file_path = "file_path.rpartition('.')[0] + ".djhtml" |
113 |
117 |
rendered_file_path = "/tmp/blog-file.djhtml" |
114 |
118 |
rendered_file = open(rendered_file_path, "w", encoding="utf-8") |
115 |
119 |
rendered_file.write(html_text) |
116 |
120 |
rendered_file.close() |
117 |
121 |
if return_djhtml_path: |
118 |
122 |
return rendered_file_path |
119 |
123 |
else: |
120 |
124 |
return html_text |
121 |
125 |
|
122 |
126 |
class Article(models.Model): |
123 |
127 |
AFRIKAANS = 'af' |
124 |
128 |
BELGIAN_FRENCH = 'fr-be' |
125 |
129 |
DUTCH = 'nl' |
126 |
130 |
ESPERANTO = 'eo' |
127 |
131 |
ENGLISH = 'en' |
128 |
132 |
FLEMISH = 'nl-be' |
129 |
133 |
FRENCH = 'fr' |
130 |
134 |
GERMAN = 'de' |
131 |
135 |
SPANISH = 'es' |
132 |
136 |
|
133 |
137 |
LANGUAGE_CODES = [ |
134 |
138 |
(AFRIKAANS, 'Afrikaans'), |
135 |
139 |
(BELGIAN_FRENCH, 'Français (Belgique)'), |
136 |
140 |
(DUTCH, 'Nederlands'), |
137 |
141 |
(ESPERANTO, 'Esperanto'), |
138 |
142 |
(ENGLISH, 'English'), |
139 |
143 |
(FLEMISH, 'Vlaams'), |
140 |
144 |
(FRENCH, 'Français'), |
141 |
145 |
(GERMAN, 'Deutsch'), |
142 |
146 |
(SPANISH, 'Español')] |
143 |
147 |
|
144 |
148 |
visible = models.BooleanField(default=True) |
145 |
149 |
post = models.ForeignKey(Post, on_delete=models.CASCADE) |
146 |
150 |
language_code = models.CharField(max_length=16, |
147 |
151 |
choices = LANGUAGE_CODES, |
148 |
152 |
blank=False) |
149 |
153 |
# file_path shouldn't be unique, because the same article file could be used |
150 |
154 |
# for multiple dialects of the same language. |
151 |
155 |
file_path = models.FilePathField(path=settings.MEDIA_ROOT + "blog/articles/", |
152 |
156 |
blank=False) |
153 |
157 |
# Same reason, slug shouldn't be unique |
154 |
158 |
slug = models.SlugField(unique=False, blank=False, allow_unicode=True) |
155 |
159 |
title = models.CharField(max_length=64, unique=False, blank=True) |
156 |
160 |
|
157 |
161 |
def text(self): |
158 |
162 |
return org_to_html(self.file_path) |
159 |
163 |
def djhtml_file(self): |
160 |
164 |
return org_to_html(self.file_path, return_djhtml_path=True) |
161 |
165 |
|
162 |
166 |
|
163 |
167 |
class Comment(models.Model): |
164 |
168 |
""" Represents a comment on a blog post. |
165 |
169 |
Comments are not filtered by language; a |
166 |
170 |
comment made by someone reading the article in Dutch, that's written in |
167 |
171 |
Dutch, will show up (unedited) for somebody whom's reading the Spanish |
168 |
172 |
version. |
169 |
173 |
""" |
170 |
174 |
# Allows me to manually hide certain messages if need be |
171 |
175 |
visible = models.BooleanField(default=True) |
172 |
176 |
date = models.DateTimeField(auto_now_add=True) |
173 |
177 |
name = models.CharField(max_length=64, blank=True) |
174 |
178 |
text = models.TextField(max_length=10000, blank=False) # Should be more than enough |
175 |
179 |
# reaction_to is null if it's not a reaction to an existing comment |
176 |
180 |
reaction_to = models.ForeignKey('Comment', on_delete=models.CASCADE, null=True) |
177 |
181 |
from_myself = models.BooleanField(default=False) |
178 |
182 |
post = models.ForeignKey( |
179 |
183 |
Post, |
180 |
184 |
on_delete=models.CASCADE, |
181 |
185 |
null=False, |
182 |
186 |
) |
183 |
187 |
class meta: |
184 |
188 |
ordering = ['date'] # When printed, prints the oldest comment first. |
185 |
189 |
|
186 |
190 |
def reactions(self): |
187 |
191 |
# Should return the comments that are a reaction to this comment |
188 |
192 |
return Comment.objects.filter(reaction_to=self).order_by('-date') |
189 |
193 |
|
+ |
194 |
return str(self.id) +" | "+ self.name |
+ |
195 |
|
190 |
196 |
class FeedItem(models.Model): |
191 |
197 |
""" An item that shows up in the RSS feed.""" |
192 |
198 |
title = models.CharField(max_length=64) |
193 |
199 |
added = models.DateTimeField(auto_now_add=True) |
194 |
200 |
description = models.CharField(max_length=400) |
195 |
201 |
link = models.URLField() |
196 |
202 |
templates/blog/comment.djhtml ¶
1 addition and 0 deletions.
View changes Hide changes
1 |
1 |
{% load humanize %} |
2 |
2 |
|
3 |
3 |
<div class="comment" id="reago-{{ comment.id }}"> |
4 |
4 |
<p><a href="#reago-{{ comment.id }}">#{{ comment.id }}</a> |
5 |
5 |
{{ comment.name }} | |
6 |
6 |
{{ comment.date|naturaltime }} |
7 |
7 |
({{ comment.date|date:"SHORT_DATE_FORMAT" }}) |
8 |
8 |
</p> |
9 |
9 |
<p>{{ comment.text|urlize }}</p> |
10 |
10 |
<details> |
11 |
11 |
<summary>{% translate "Respond" %}</summary> |
12 |
12 |
<form method="POST"> |
13 |
13 |
{% csrf_token %} |
14 |
14 |
<input type="hidden" name="reaction_to" value="{{ comment.id }}"> |
15 |
15 |
<input type="text" id="name-{{ comment.id }}" name="name" maxlength="64" required> |
+ |
16 |
<input type="text" id="name-{{ comment.id }}" name="name" maxlength="64" required> |
16 |
17 |
<label for="name-{{ comment.id }}">{% translate "Your name" %}</label><br> |
17 |
18 |
<textarea name="text" id="text-{{ comment.id }}" maxlength="10000" required></textarea> |
18 |
19 |
<label for="text-{{ comment.id }}">{% translate "Your comment" %}</label><br> |
19 |
20 |
<input type="submit" value="{% translate "Submit" %}"> |
20 |
21 |
</form> |
21 |
22 |
</details> |
22 |
23 |
{% for subcomment in comment.reactions %} |
23 |
24 |
{% include "blog/comment.djhtml" with comment=subcomment %} |
24 |
25 |
{% endfor %} |
25 |
26 |
</div> |
26 |
27 |
templates/blog/post.djhtml ¶
9 additions and 20 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 |
</style> |
+ |
45 |
.comment { |
+ |
46 |
margin-left: 1em; |
+ |
47 |
padding-left: 1em; |
+ |
48 |
border-left-style: solid; |
+ |
49 |
border-color: var(--primary); |
+ |
50 |
} |
+ |
51 |
</style> |
45 |
52 |
{% endblock stylesheets %} |
46 |
53 |
|
47 |
54 |
{% block description %} |
48 |
55 |
{{ article.text|safe|truncatewords_html:10 }} |
49 |
56 |
{% endblock description %} |
50 |
57 |
{% block title %}📚 {{ navbar_title }}{% endblock title %} |
51 |
58 |
|
52 |
59 |
|
53 |
60 |
{% block header %} |
54 |
61 |
<header> |
55 |
62 |
<h1>{{ navbar_title }}</h1> |
56 |
63 |
</header> |
57 |
64 |
{% endblock header %} |
58 |
65 |
{% block main %} |
59 |
66 |
<section class="article"> |
60 |
67 |
<!--<article style="font-family:serif;">--> |
61 |
68 |
{#{{ article.text|safe }}#} |
62 |
69 |
{% include "/tmp/django-templates/blog-file.djhtml" %} |
63 |
- | |
+ |
70 |
|
64 |
71 |
<!--</article>--> |
65 |
72 |
</section> |
66 |
73 |
<section class="reagoj"> |
67 |
74 |
<h2>{% translate "Comments" %}</h2> |
68 |
75 |
<form method="POST"> |
69 |
76 |
{% csrf_token %} |
70 |
77 |
<input type="hidden" name="reaction_to" value=""> |
71 |
78 |
<input type="text" id="name-root" name="name" maxlength="64" required> |
+ |
79 |
<input type="text" id="name-root" name="name" maxlength="64" required> |
72 |
80 |
<label for="name-root">{% translate "Your name" %}</label><br> |
73 |
81 |
<textarea name="text" id="text-root" maxlength="10000" required></textarea> |
74 |
82 |
<label for="text-root">{% translate "Your comment" %}</label><br> |
75 |
83 |
<input type="submit" value="{% translate "Submit" %}"> |
76 |
84 |
</form> |
77 |
85 |
<hr> |
78 |
86 |
{% for root_comment in root_comments %} |
79 |
87 |
{% include "blog/comment.djhtml" with comment=root_comment %} |
80 |
88 |
{% endfor %} |
81 |
89 |
</section> |
82 |
90 |
|
83 |
91 |
{% comment %} |
84 |
92 |
<h5 class="white-text">{% trans "This article in other languages" %}</h5> |
85 |
93 |
|
86 |
94 |
{% get_language_info for 'nl' as LANG %} |
87 |
95 |
<a {% if dutch_link %} href="{{dutch_link}}" {% endif %} |
88 |
96 |
class="btn fill |
89 |
97 |
{% if not dutch_link %}disabled{% endif %}"> |
90 |
98 |
🇧🇪 {{ LANG.name_translated}} 🇳🇱 |
91 |
99 |
</a> |
92 |
100 |
{% get_current_language as lang %} |
93 |
101 |
{% get_language_info for 'fr' as LANG %} |
94 |
102 |
<a {% if french_link %} href="{{french_link}}" {% endif %} |
95 |
103 |
class="btn fill |
96 |
104 |
{% if not french_link %}disabled{% endif %}"> |
97 |
105 |
🇧🇪 {{ LANG.name_translated}} 🇫🇷 |
98 |
106 |
</a> |
99 |
107 |
{% get_language_info for 'en' as LANG %} |
100 |
108 |
<a {% if english_link %} href="{{english_link}}" {% endif %} |
101 |
109 |
class="btn fill |
102 |
110 |
{% if not english_link %}disabled{% endif %}"> |
103 |
111 |
🇬🇧 {{ LANG.name_translated}} 🇺🇸 |
104 |
112 |
</a> |
105 |
113 |
{% get_language_info for 'de' as LANG %} |
106 |
114 |
<a {% if german_link %} href="{{german_link}}" {% endif %} |
107 |
115 |
class="btn fill |
108 |
116 |
{% if not german_link %}disabled{% endif %}"> |
109 |
117 |
🇧🇪 {{ LANG.name_translated}} 🇩🇪 |
110 |
118 |
</a> |
111 |
119 |
{% get_language_info for 'es' as LANG %} |
112 |
120 |
<a {% if spanish_link %} href="{{spanish_link}}" {% endif %} |
113 |
121 |
class="btn |
114 |
122 |
{% if not spanish_link %}disabled{% endif %}"> |
115 |
123 |
🇪🇸 {{ LANG.name_translated}} 🇲🇽 |
116 |
124 |
</a> |
117 |
125 |
{% endcomment %} |
118 |
126 |
|
119 |
127 |
<h5 class="white-text">{% trans "Comments" %}</h5> |
120 |
- | {% for comment in comments %} {# Whoops =P #} |
121 |
- | <span class="white-text">{{ comment.name|title }} | </span> |
122 |
- | <time class="grey-text" datetime="{{ comment.date|date:'c' }}">{{ comment.date|naturaltime }}</time> |
123 |
- | <br /> |
124 |
- | <p class="white-text">{{ comment.text|urlize }}</p> |
125 |
- | <hr /> |
126 |
- | {% endfor %} |
127 |
- | {# Form for new comment #} |
128 |
- | <form action="" method="POST"> |
129 |
- | {% csrf_token %} |
130 |
- | {{ form.name.label_tag }} |
131 |
- | <input class="white-text browser-default" type="text" id="id_name" name="name" maxlength="64" required /> |
132 |
- | {{ form.text.label_tag }} |
133 |
- | <textarea id="id_text" class="white-text" name="text" maxlength="1000" required></textarea> |
134 |
- | <input type="submit" value="{% trans "Submit" %}" /> |
135 |
- | </form> |
136 |
- | |
137 |
- | |
138 |
- | {% comment %} |
139 |
128 |
<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> |
140 |
129 |
{# TODO: Change to rainbow flag when possible #} |
141 |
130 |
{% endcomment %} |
142 |
131 |
</div> |
143 |
132 |
|
144 |
133 |
</div> |
145 |
134 |
{% endblock main %} |
146 |
135 |
views.py ¶
8 additions and 51 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 generate_comments_section(post, comment=None): |
21 |
- | """Since it's possible to comment on a comment, the comment section needs to |
22 |
- | be built recursively, which the DTL can't do. So I'll have to prepare it in |
23 |
- | here, generating the DTL code for the comment section. |
24 |
- | The returned code should then be rendered using Django's template engine, |
25 |
- | with appropriate context and request data.""" |
26 |
- | code = "" |
27 |
- | if comment is None: # Initial call to the function |
28 |
- | # To make sure the template formats correctly, we need to build it here |
29 |
- | # with code |
30 |
- | code += '<div id="reagoj">\n' |
31 |
- | root_comments = Comment.objects.filter(reaction_to=None).order_by('-date') |
32 |
- | for root_comment in root_comments: |
33 |
- | code += generate_comments_section(post, comment=root_comment) |
34 |
- | else: |
35 |
- | code += '<div class="comment" id="reago-'+str(comment.id)+'">\n' |
36 |
- | code += '<p>{{ comments.'+ str(comment.id) +'.name }} | ' |
37 |
- | code += '{{ comments.'+ str(comment.id) +'.date|naturaltime }} ' |
38 |
- | code += '({{ comments.'+ str(comment.id) +'.date|date:"SHORT_DATE_FORMAT" }})' |
39 |
- | code += '</p>\n' |
40 |
- | #code += '<time class="grey-text" datetime="{{ comment.date|date:'c' }}">{{ comment.date|naturaltime }}</time>' |
41 |
- | code += '<p>{{ comment.'+ str(comment.id) +'.text|urlize }}</p>\n' |
42 |
- | code += '<summary><details>' |
43 |
- | code += _('Respond') |
44 |
- | code += '</details>\n' |
45 |
- | code += '<form method="POST">\n' |
46 |
- | code += '{% csrf_token %}\n' |
47 |
- | code += '<input type="hidden" name="reaction_to" value="'+ str(comment.id) +'">\n' |
48 |
- | code += '<input type="text" name="name" maxlength="64" required>\n' |
49 |
- | code += '<textarea id="id_text" name="text" maxlength="10000" required></textarea>\n' |
50 |
- | code += '<input type="submit" value="'+ _("Submit") +'">\n' |
51 |
- | code += '</form></details>\n' |
52 |
- | |
53 |
- | # This comment is now written, so let's add the subcomments to this comment |
54 |
- | subcomments = Comment.objects.filter(reaction_to=comment).order_by('-date') |
55 |
- | for subcomment in subcomments: |
56 |
- | code += generate_comments_section(post, comment=subcomment) |
57 |
- | |
58 |
- | # At this point, everything related to this comment is written, so writeback |
59 |
- | code += '</div>' |
60 |
- | return code |
61 |
- | |
62 |
- | def index(request): |
63 |
21 |
template = "blog/index.djhtml" |
64 |
22 |
posts = Post.objects.exclude(visible=False) |
65 |
23 |
|
66 |
24 |
|
67 |
25 |
context = { |
68 |
26 |
'posts': posts, |
69 |
27 |
'navbar_title': _("Notepad from a student"), |
70 |
28 |
'navbar_backArrow': True, |
71 |
29 |
'stylesheet_name': "blog", |
72 |
30 |
} |
73 |
31 |
return render(request, template, context) |
74 |
32 |
|
75 |
33 |
def post(request, language_code, post_slug): |
76 |
34 |
if request.method == "POST": # Handling a reply if one is sent |
77 |
35 |
form = CommentForm(request.POST) |
78 |
36 |
for post in Post.objects.all(): |
79 |
- | if post.slug(language) == post_slug: |
80 |
- | form.post = post |
81 |
- | break |
82 |
- | if form.is_valid(): |
+ |
37 |
form.post = Post.objects.get(id=request.POST['post']) |
+ |
38 |
if form.is_valid(): |
83 |
39 |
new_comment = form.save(commit=False) |
84 |
40 |
for post in Post.objects.all(): |
85 |
- | if post.slug(language) == post_slug: |
86 |
- | new_comment.post = post |
87 |
- | new_comment.save() |
88 |
- | |
+ |
41 |
new_comment.reaction_to = Comment.objects.get(id=request.POST['reaction_to']) |
+ |
42 |
new_comment.save() |
+ |
43 |
else: |
+ |
44 |
print("ERROR") |
+ |
45 |
print(form.errors) |
+ |
46 |
|
89 |
47 |
template = "blog/post.djhtml" |
90 |
48 |
article = Article.objects.get(slug=post_slug, language_code=language_code) |
91 |
49 |
root_comments = Comment.objects.filter(post=article.post, reaction_to=None) |
92 |
50 |
context = { |
93 |
51 |
'article': article, |
94 |
52 |
'navbar_title': article.post.title(), |
95 |
- | 'root_comments': root_comments, |
96 |
53 |
'title': article.post.title(), |
97 |
54 |
'navbar_title': article.post.title(), |
98 |
55 |
'navbar_backArrow': True, |
99 |
56 |
'stylesheet_name': "blog"} |
100 |
57 |
|
101 |
58 |
return render(request, template, context) |
102 |
59 |
|
103 |
60 |
def rss(request): |
104 |
61 |
template = "blog/feed.rss" |
105 |
62 |
context = { |
106 |
63 |
'items': FeedItem.objects.all(), |
107 |
64 |
} |
108 |
65 |
return render(request, template, context, content_type="application/rss+xml") |
109 |
66 |
|
110 |
67 |
|
111 |
68 |
def archive(request): |
112 |
69 |
template = "blog/monthly_archive.djhtml" |
113 |
70 |
language = translation.get_language() |
114 |
71 |
|
115 |
72 |
file_2017 = org_to_html("blog/weekly/2017.org") |
116 |
73 |
file_2018 = org_to_html("blog/weekly/2018.org") |
117 |
74 |
file_2019 = org_to_html("blog/weekly/2019.org") |
118 |
75 |
|
119 |
76 |
|
120 |
77 |
|
121 |
78 |
context = { |
122 |
79 |
't2017': file_2017, |
123 |
80 |
't2018': file_2018, |
124 |
81 |
't2019': file_2019, |
125 |
82 |
'materialDesign_color': "brown", |
126 |
83 |
'materialDesign_accentColor': "blue", |
127 |
84 |
'navbar_title': _("Weekly-archief"), |
128 |
85 |
'navbar_backArrow': True, |
129 |
86 |
'footer_links': footer_links, |
130 |
87 |
'footer_description': footer_description, |
131 |
88 |
'stylesheet_name': "blog", |
132 |
89 |
} |
133 |
90 |
return render(request, template, context) |
134 |
91 |