Initial commit of blog app files
- Author
- Maarten 'Vngngdn' Vangeneugden
- Date
- July 9, 2017, 7:39 p.m.
- Hash
- 1957abb2ef5154f3fa0096f5079c6ea8a4d246e7
- Parents
- Modified files
- .gitignore
- DEPENDENCIES.md
- __init__.py
- admin.py
- apps.py
- dependencies.txt
- header.html
- models.py
- templates/blog/context.html
- templates/blog/footer.html
- tests.py
- urls.py
- views.py
.gitignore ¶
2 additions and 0 deletions.
DEPENDENCIES.md ¶
28 additions and 0 deletions.
View changes Hide changes
+ |
1 |
============ |
+ |
2 |
|
+ |
3 |
Since web apps (and everything web related) is known to suck at dependency handling (which is why we have Bower, NPM, cdnjs, docker, ... (I'll explain sometime somewhere why this is done, and why it's useless), I've decided to take matters in my own hands; I'll just provide one simple markdown file listing all dependencies in human-readable format. I don't care whether this helps or not, but no harm is done, right? |
+ |
4 |
|
+ |
5 |
Legend |
+ |
6 |
------ |
+ |
7 |
|
+ |
8 |
The dependencies themselves have been ranked with a certain grade. I explain what they mean here: |
+ |
9 |
|
+ |
10 |
* A : This dependency is present inside the app. This will be in the form of ready-to-use source code files. As such, they need no further attention. |
+ |
11 |
* B : This dependency is present inside the app. This will be in the form of an archive file (i.e. .tar.gz), OR this needs additional work to function properly. In these cases, I refer to their documentation which will be available at the location of the files. |
+ |
12 |
* C : This dependency does not come with the app. These are often system packages, which often means (if you're on GNU/Linux) to search for it in your repositories. Their documentation is your last resort for these if it doesn't work as it should. |
+ |
13 |
|
+ |
14 |
I hope I never will, but when it's necessary, I'll add a D grade, for unreadable, egregious bogus software that will give you all horrible kinds of diseases if you dare to work with it, but are indispensable to make the web app function. Just be happy I don't use it yet. (For example, PHP qualifies as D, so praise [our lord and saviour](https://rms.sexy) I write Python.) |
+ |
15 |
|
+ |
16 |
A |
+ |
17 |
- |
+ |
18 |
* [Materialize](http://materializecss.com/): The layout framework for the web app. (Material Design is the best) |
+ |
19 |
|
+ |
20 |
|
+ |
21 |
B |
+ |
22 |
- |
+ |
23 |
|
+ |
24 |
C |
+ |
25 |
- |
+ |
26 |
* [Markdown](https://pypi.python.org/pypi/Markdown): Required to compile the |
+ |
27 |
blog post files to HTML. |
+ |
28 |
__init__.py ¶
0 additions and 0 deletions.
View changes Hide changes
admin.py ¶
6 additions and 0 deletions.
apps.py ¶
5 additions and 0 deletions.
dependencies.txt ¶
3 additions and 0 deletions.
header.html ¶
23 additions and 0 deletions.
View changes Hide changes
+ |
1 |
Copyright 2015 Maarten Vangeneugden |
+ |
2 |
|
+ |
3 |
Licensed under the Apache License, Version 2.0 (the "License"); |
+ |
4 |
you may not use this file except in compliance with the License. |
+ |
5 |
You may obtain a copy of the License at |
+ |
6 |
|
+ |
7 |
https://www.apache.org/licenses/LICENSE-2.0 |
+ |
8 |
|
+ |
9 |
Unless required by applicable law or agreed to in writing, software |
+ |
10 |
distributed under the License is distributed on an "AS IS" BASIS, |
+ |
11 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+ |
12 |
See the License for the specific language governing permissions and |
+ |
13 |
limitations under the License. |
+ |
14 |
{% endcomment %} |
+ |
15 |
<!DOCTYPE html> |
+ |
16 |
<html> |
+ |
17 |
<head> |
+ |
18 |
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> |
+ |
19 |
<link href="/static/winkellijst/css/materialize.min.css" rel="stylesheet" media="screen, projection" /> |
+ |
20 |
|
+ |
21 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <!--Because this page is suited for mobile devices.--> |
+ |
22 |
</head> |
+ |
23 |
models.py ¶
50 additions and 0 deletions.
View changes Hide changes
+ |
1 |
from django.db import models |
+ |
2 |
|
+ |
3 |
def post_title_directory(instance, filename): |
+ |
4 |
""" Files will be uploaded to MEDIA_ROOT/blog/<year of publishing>/<blog |
+ |
5 |
title> |
+ |
6 |
The blog title is determined by the text before the first period (".") in |
+ |
7 |
the filename. So if the file has the name "Trains are bæ.en.md", the file |
+ |
8 |
will be stored in "blog/<this year>/Trains are bæ". Name your files |
+ |
9 |
properly! |
+ |
10 |
It should also be noted that all files are stored in the same folder if they |
+ |
11 |
belong to the same blogpost, regardless of language. The titles that are |
+ |
12 |
displayed to the user however, should be the titles of the files themselves, |
+ |
13 |
which should be in the native language. So if a blog post is titled |
+ |
14 |
"Universities of Belgium", its Dutch counterpart should be titled |
+ |
15 |
"Universiteiten van België", so the correct title can be derived from the |
+ |
16 |
filename. |
+ |
17 |
|
+ |
18 |
Recommended way to name the uploaded file: "<name of blog post in language |
+ |
19 |
it's written>.md". This removes the maximum amount of redundancy (e.g. the |
+ |
20 |
language of the file can be derived from the title, no ".fr.md" or something |
+ |
21 |
like that necessary), and can directly be used for the end user (the title |
+ |
22 |
is what should be displayed). |
+ |
23 |
""" |
+ |
24 |
english_file_name = instance.file_en.name # TODO: Test if this returns the file name! |
+ |
25 |
english_title = english_file_name.partition(".")[0] |
+ |
26 |
|
+ |
27 |
return "blog/%Y/{0}".format(english_title) |
+ |
28 |
|
+ |
29 |
class Post(models.Model): |
+ |
30 |
""" Represents a blog post. The title of the blog post is determnined by the name |
+ |
31 |
of the files. |
+ |
32 |
A blog post can be in 5 different languages: German, Spanish, English, French, |
+ |
33 |
and Dutch. For all these languages, a seperate field exists. Thus, a |
+ |
34 |
translated blog post has a seperate file for each translation, and is |
+ |
35 |
seperated from Django's internationalization/localization system. |
+ |
36 |
Only the English field is mandatory. The others may contain a value if a |
+ |
37 |
translated version exists, which will be displayed accordingly. |
+ |
38 |
""" |
+ |
39 |
published = models.DateTimeField(auto_now_add=True) |
+ |
40 |
file_de = models.FileField(upload_to=post_title_directory, unique=True, blank=True) |
+ |
41 |
file_es = models.FileField(upload_to=post_title_directory, unique=True, blank=True) |
+ |
42 |
file_en = models.FileField(upload_to=post_title_directory, unique=True, blank=False) |
+ |
43 |
file_fr = models.FileField(upload_to=post_title_directory, unique=True, blank=True) |
+ |
44 |
file_nl = models.FileField(upload_to=post_title_directory, unique=True, blank=True) |
+ |
45 |
# Please note that there's no need to create any indexes, because unique |
+ |
46 |
# fields constitute an index already. |
+ |
47 |
|
+ |
48 |
def __str__(): |
+ |
49 |
return self.file_en.name.partition(".")[0] |
+ |
50 |
templates/blog/context.html ¶
2 additions and 0 deletions.
templates/blog/footer.html ¶
2 additions and 0 deletions.
tests.py ¶
3 additions and 0 deletions.
urls.py ¶
9 additions and 0 deletions.
views.py ¶
77 additions and 0 deletions.
View changes Hide changes
+ |
1 |
|
+ |
2 |
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. |
+ |
3 |
from django.http import HttpResponseRedirect, HttpResponse |
+ |
4 |
from django.core.urlresolvers import reverse # Why? |
+ |
5 |
from django.template import loader # This allows to actually load the template. |
+ |
6 |
from django.contrib.auth.decorators import login_required |
+ |
7 |
from django.contrib.auth import authenticate, login |
+ |
8 |
from .models import Post |
+ |
9 |
from django.core.exceptions import ObjectDoesNotExist |
+ |
10 |
from markdown import markdown |
+ |
11 |
|
+ |
12 |
# Common data: |
+ |
13 |
footer_links = [ |
+ |
14 |
["Home", "/"], |
+ |
15 |
] |
+ |
16 |
|
+ |
17 |
# FIXME: Remove this template trash. THIS IS A VIEW, NOT A FUCKING TEMPLATE FFS |
+ |
18 |
context = { |
+ |
19 |
'materialDesign_color': "green", |
+ |
20 |
'materialDesign_accentColor': "purple", |
+ |
21 |
'navbar_title': "Blog", |
+ |
22 |
'navbar_fixed': True, |
+ |
23 |
'navbar_backArrow': True, |
+ |
24 |
#'footer_title': "Maarten's blog", |
+ |
25 |
#'footer_description': "My personal scribbly notepad.", |
+ |
26 |
'footer_links': footer_links, |
+ |
27 |
} |
+ |
28 |
|
+ |
29 |
def get_preferred_language( |
+ |
30 |
request, |
+ |
31 |
possible_locales = {'de','en','es','fr','nl'}, |
+ |
32 |
default = 'en'): |
+ |
33 |
""" Returns the best language that can be offered to the user. |
+ |
34 |
An HTTP request header contains an Accept-Language field, in which the user |
+ |
35 |
lists his requested languages, in descending order for preferability. This |
+ |
36 |
function will read this field, and return a locale that can be offered by |
+ |
37 |
the website (possible_locales). If nothing suitable can be offered, the |
+ |
38 |
default is returned (English). |
+ |
39 |
""" |
+ |
40 |
accepted_languages = request.META.HTTP_ACCEPT_LANGUAGE |
+ |
41 |
accepted_languages = accepted_languages.split(",") |
+ |
42 |
for accepted_language in accepted_languages: |
+ |
43 |
for possible_locale in possible_locales: |
+ |
44 |
if accepted_language.lower().startswith(possible_locale): |
+ |
45 |
return possible_locale |
+ |
46 |
return default # No possible locale found, so return the default. |
+ |
47 |
|
+ |
48 |
def index(request): |
+ |
49 |
template = "blog/index.html" |
+ |
50 |
posts = Post.objects.all() |
+ |
51 |
|
+ |
52 |
post_links = [] |
+ |
53 |
for post in posts: |
+ |
54 |
title = str(post) |
+ |
55 |
date = post.published |
+ |
56 |
# TODO: The link can possibly be reversed in the DTL, which is actually |
+ |
57 |
# a cleaner way to do it. Investigate. |
+ |
58 |
link = reverse("blog-post", args=[title]) |
+ |
59 |
post_links.append([title, date, description, link]) |
+ |
60 |
|
+ |
61 |
context = { |
+ |
62 |
'post_links': post_links, |
+ |
63 |
} |
+ |
64 |
return render(request, template, context) |
+ |
65 |
|
+ |
66 |
def post(request, title): |
+ |
67 |
template = "blog/post.html" |
+ |
68 |
posts = Post.objects.get(file_en=title) |
+ |
69 |
blog_text = markdown(text_file) |
+ |
70 |
|
+ |
71 |
context = { |
+ |
72 |
'article': blog_text, |
+ |
73 |
} |
+ |
74 |
return render(request, template, context) |
+ |
75 |
|
+ |
76 |
|
+ |
77 |