Commit a192fc48 authored by Falk, Dennis Nikolas's avatar Falk, Dennis Nikolas
Browse files

Merge remote-tracking branch 'remotes/origin/master' into Dennis_2020_06_16

# Conflicts:
#	blog/templates/blog/all_blogs.html
#	personal_blog/static/blog/comment-area.css
#	personal_blog/static/ckeditor/ckeditor_uploader/admin_base.css
parents f0205a84 25fe3e69
# Django_Blog
## Description
This is a simple blog, written in Python and Django. As CSS framework, bootstrap 4
is used.
We have a customizable start page and a flexible blog system. In the admin surface,
all blog contents can be edited. Some of them are fixed and needed to be there
to work properly. These are:
* start-page: The start-page, of course :) Change content to something nice
* impress: Fill with impress data
* contact: Contact information of the page owner
* 404: Error page content
There are also some sample-article, which only exist to demonstration purposes.
## Blog fields explained
### Identifier
This unique key is used to identify your article quick and is also used for fixed
content (see above). It is filled with a hash by default.
### Title
This Title will be shown at the browser tab, the top of an article and, if shown there,
in the title of the card tab.
### Description
Content of the blog, filled with an WYSIWYG editor.
### Dates
Creation and edit date. You usually don't need to change them.
### Image
This image is the teaser of your article. It is shown above the article content
and the card element. If none, the area will be without image. However, you can add
more images by the WYSIWYG editor.
### Status
This is a binary combined field to set the states of the blog. You can sum them up to
add several states:
* 0 = Normal Article
* 1 = Article is hidden in overview
* 2 = Article can have comments
Setting the state to 3 would hide the article, but allow comments to it.
### Slug
This is where you can reach your article by. For example, if you wrote "example-1",
you can call http://127.0.0.1:8000/sample-1 to see the details view of it.
If you call a slug which does not exist, you will receive a 404 error message.
## Setup
### Prepare system
Load these requirements by
`pip install [REQ_NAME]`
* asgiref==3.2.7
* beautifulsoup4==4.9.1
* Django==3.0.7
* django-bootstrap4==2.0.1
* importlib-metadata==1.6.1
* Pillow==7.1.2
* pytz==2020.1
* soupsieve==2.0.1
* sqlparse==0.3.1
* zipp==3.1.0
* django-ckeditor==5.9.0
**Hint**: We installed the basic project enviorment by PyCharm using a Django project as default.
After that, only these components needs to be loaded:
* django-bootstrap4==2.0.1
* Pillow==7.1.2
* django-ckeditor==5.9.0
### Prepare Database
Run the following commands to create the database:
`manage.py migrate`
`manage.py loaddata blog`
Afterwards, you need to setup your database user:
`manage.py createsuperuser`
We used admin/blog1234 as default.
### Start your application
To start the application, run
`manage.py runserver --insecure`
The `--insecure` statement is necessary to provide static content and our nicer 404 page for error cases.
Otherwise, an external web server would be necessary.
### Check if it worked
Open the link http://127.0.0.1:8000/ and check, if the page opens up and shows some content like author.
**Note!** If you see a message, telling you to perform manage.py commands, something went wrong, as the
start page content could not be found. Try to redo the steps above, please.
# AB HIER NOCH SAUBER MACHEN
### Django Befehle
#### Neues Module (App)
`manage.py startapp *appname*`
......
[
{
"model": "blog.blog",
"pk": 1,
"fields": {
"identifier": "contact",
"title": "Kontakt",
"description": "<p>Kontakt</p>",
"date": "2020-06-16T21:19:46Z",
"image": "",
"created": "2020-06-16",
"edited": "2020-06-16",
"status": 1,
"slug": "contact"
}
},
{
"model": "blog.blog",
"pk": 2,
"fields": {
"identifier": "impress",
"title": "Impressum",
"description": "<h1 style=\"font-style:italic\">Impressum</h1>",
"date": "2020-06-16T21:21:22Z",
"image": "",
"created": "2020-06-16",
"edited": "2020-06-16",
"status": 1,
"slug": "impress"
}
},
{
"model": "blog.blog",
"pk": 8,
"fields": {
"identifier": "start-page",
"title": "Die Projekt-Startseite",
"description": "<h2>Autoren:</h2>\r\n\r\n<p>Svenja Behr,<br />\r\nDennis Nikolas Falk,<br />\r\nAlexander Tang</p>\r\n\r\n<p>Datenbank:<strong> djangoblog.sqlite3</strong>,<br />\r\nLogin: <strong>admin</strong><br />\r\nPassword: <strong>blog1234</strong></p>",
"date": "2020-06-22T18:14:38Z",
"image": "",
"created": "2020-06-22",
"edited": "2020-06-22",
"status": 1,
"slug": "start"
}
},
{
"model": "blog.blog",
"pk": 9,
"fields": {
"identifier": "404",
"title": "Artikel nicht gefunden",
"description": "<p>Das ist nicht der Artikel, den du suchst&nbsp;<img alt=\"sad\" src=\"http://127.0.0.1:8000/static/ckeditor/ckeditor/plugins/smiley/images/sad_smile.png\" style=\"height:23px; width:23px\" title=\"sad\" /></p>",
"date": "2020-06-22T18:16:09Z",
"image": "",
"created": "2020-06-22",
"edited": "2020-06-22",
"status": 1,
"slug": "404"
}
},
{
"model": "blog.blog",
"pk": 10,
"fields": {
"identifier": "sample-1",
"title": "Beispielartikel 1",
"description": "<p>Jemand musste Josef K. verleumdet haben, denn ohne dass er etwas B&ouml;ses getan h&auml;tte, wurde er eines Morgens verhaftet. &raquo;Wie ein Hund!&laquo; sagte er, es war, als sollte die Scham ihn &uuml;berleben. Als Gregor Samsa eines Morgens aus unruhigen Tr&auml;umen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Und es war ihnen wie eine Best&auml;tigung ihrer neuen Tr&auml;ume und guten Absichten, als am Ziele ihrer Fahrt die Tochter als erste sich erhob und ihren jungen K&ouml;rper dehnte. &raquo;Es ist ein eigent&uuml;mlicher Apparat&laquo;, sagte der Offizier zu dem Forschungsreisenden und &uuml;berblickte mit einem gewisserma&szlig;en bewundernden Blick den ihm doch wohlbekannten Apparat. Sie h&auml;tten noch ins Boot springen k&ouml;nnen, aber der Reisende hob ein schweres, geknotetes Tau vom Boden, drohte ihnen damit und hielt sie dadurch von dem Sprunge ab. In den letzten Jahrzehnten ist das Interesse an Hungerk&uuml;nstlern sehr zur&uuml;ckgegangen. Aber sie &uuml;berwanden sich, umdr&auml;ngten den K&auml;fig und wollten sich gar nicht fortr&uuml;hren.Jemand musste Josef K. verleumdet haben, denn ohne dass er etwas B&ouml;ses getan h&auml;tte, wurde er eines Morgens verhaftet. &raquo;Wie ein Hund!&laquo; sagte er, es war, als sollte die Scham ihn &uuml;berleben. Als Gregor Samsa eines Morgens aus unruhigen Tr&auml;umen erwachte, fand er sich...</p>\r\n\r\n<h2>&Uuml;berschrift zwischendurch</h2>\r\n\r\n<p>Es gibt im Moment in diese Mannschaft, oh, einige Spieler vergessen ihnen Profi was sie sind. Ich lese nicht sehr viele Zeitungen, aber ich habe geh&ouml;rt viele Situationen. Erstens: wir haben nicht offensiv gespielt. Es gibt keine deutsche Mannschaft spielt offensiv und die Name offensiv wie Bayern. Letzte Spiel hatten wir in Platz drei Spitzen: Elber, Jancka und dann Zickler. Wir m&uuml;ssen nicht vergessen Zickler. Zickler ist eine Spitzen mehr, Mehmet eh mehr Basler. Ist klar diese W&ouml;rter, ist m&ouml;glich verstehen, was ich hab gesagt? Danke. Offensiv, offensiv ist wie machen wir in Platz. Zweitens: ich habe erkl&auml;rt mit diese zwei Spieler: nach Dortmund brauchen vielleicht Halbzeit Pause. Ich habe auch andere Mannschaften gesehen in Europa nach diese Mittwoch. Ich habe gesehen auch zwei Tage die Training. Ein Trainer ist nicht ein Idiot! Ein Trainer sei sehen was passieren in Platz. In diese Spiel es waren zwei, drei diese Spieler waren schwach wie eine Flasche leer! Haben Sie gesehen Mittwoch, welche Mannschaft hat gespielt Mittwoch? Hat gespielt Mehmet oder gespielt Basler oder hat gespielt Trapattoni? Diese Spieler beklagen mehr als sie spielen! Wissen Sie, warum die Italienmannschaften kaufen nicht diese Spieler? Weil wir haben gesehen viele Male solche Spiel!&nbsp;</p>",
"date": "2020-06-22T18:17:17Z",
"image": "",
"created": "2020-06-22",
"edited": "2020-06-22",
"status": 0,
"slug": "sample-1"
}
},
{
"model": "blog.blog",
"pk": 11,
"fields": {
"identifier": "sample-2",
"title": "Beispielartikel 2",
"description": "<p>Hier k&ouml;nnte ihr eigener Artikel stehen!</p>\r\n\r\n<hr />\r\n<div style=\"background:#eeeeee; border:1px solid #cccccc; padding:5px 10px\">//Don&#39;t leave dead comments in code!</div>\r\n\r\n<p>Kommentare k&ouml;nnen unten eingetragen werden.&nbsp;</p>",
"date": "2020-06-22T18:18:50Z",
"image": "",
"created": "2020-06-22",
"edited": "2020-06-22",
"status": 2,
"slug": "sample-2"
}
}
]
\ No newline at end of file
.details {
margin-bottom: 4em;
}
.details + .comment-area {
margin-top: -2em;
}
.comment-area {
border-top: 1px solid #0B9ED9;
border-radius: 0;
margin-bottom: 4em;
}
.comment-area article {
background-color: #eeeeee;
......
.card-body img {
max-width: 100%;
object-fit: scale-down;
display: none; /* skip images in preview, because they took too much space */
}
.blogcard {
margin-bottom: 4em;
}
a.card, a.card:hover, a.card:active, a.card:visited {
color: #212529;
}
a.card:hover {
text-decoration: none;
}
\ No newline at end of file
@import url('https://fonts.googleapis.com/css2?family=Fredericka+the+Great&display=swap');
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@500&display=swap');
.fredericka {
font-family: 'Fredericka the Great', cursive;
}
.minidate {
font-size: 0.6em;
color: #668cff;
}
.thirtypercent {
max-width: 30%;
color: #0B9ED9;
}
body {
......@@ -19,6 +11,20 @@ body {
font-family: "Raleway", serif;
}
h1, h2, h3 {
color: #F25CA2;
font-family: "EB Garamond", sans-serif;
}
h4, h5, h6 {
color: #0433BF;
}
.jumbotron .container {
background-color: initial;
}
.container{
background-color: #ffffff;
margin-top: 1em;
......@@ -27,13 +33,16 @@ body {
padding: 1em;
}
h1, h2, h3 {
.details {
margin-bottom: 4em;
}
footer a {
color: #F25CA2;
font-family: "EB Garamond", sans-serif;
}
h4, h5, h6 {
color: #0433BF;
footer a:hover {
color: #0B9ED9;
}
.btn-primary {
......@@ -46,9 +55,6 @@ h4, h5, h6 {
border-color: #1CAFEA;
}
.container > header {
border-bottom: 1px solid #021859;
}
/* Color Theme */
.color1 { color: #F25CA2; }
......
{% extends 'blog/base.html' %}
{% load bootstrap4 %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
{% bootstrap_javascript %}
{% bootstrap_css %}
<link rel="stylesheet" href="{% static 'blog/styles.css' %}">
<script src="{% static 'blog/all_blogs.js' %}"></script>
<style>
.card-body {
cursor: pointer;
}
.card-body img {
max-width: 100%;
object-fit: scale-down;
}
</style>
<meta charset="UTF-8">
<title>Portfolio-Blog</title>
</head>
<body>
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4 fredericka">Portfolio-Blog</h1>
<p class="lead">Auf diesem Blog zeigen wir unsere Projekte und unser Können</p>
<div id="startpage_button" class="btn btn-primary yeah">Dieser Button ist noch ungenutzt</div><br><br>
<h5>Anleitung: Drücke auf den Kartentext! um zur Detailansicht zu gelangen</h5>
</div>
</div>
<div class="container blogcard">
{% block css %}
<link rel="stylesheet" href="{% static 'blog/overview.css' %}">
{% endblock %}
{% block error %}
<!-- Error will go in here -->
{% endblock %}
{% block page-title %} Alle Artikel {% endblock %}
{% block heading %}
<h1 class="display-2">Personal Blog</h1>
<p class="lead">Auf diesem Blog zeigen wir unsere Projekte und unser Können</p>
{% endblock %}
<div class="card-deck">
{% block content %}
<div class="container-fluid">
<div class="row mb-4 mx-4">
{% for blog in blogs %}
<div class="col-sm-12 col-lg-4 px-2 mb-4" >
<a class="card" href="{{ blog.slug }}">
{% if blog.image and blog.image.url %}
<img class="card-img-top" src="{{ blog.image.url }}" alt="Card image cap">
{% endif %}
<div class="card-body">
<div class="text-right minidate" >{{ blog.date }}</div>
<h3 class="card-title text-center">{{ blog.title }}</h3>
{% autoescape off %}
<p class="card-text">{{ blog.description|truncatechars:200 }}</p>
{% endautoescape %}
<div id="cardid" class="text-right minidate" >{{ blog.slug }}</div>
{% if blog.url %}
<div class="card-footer">
<a href="{{ project.url }}" class="btn btn-primary">Follow URL</a>
</div>
{% endif %}
</div>
</a>
{% for blog in blogs %}
<div class="card float-left mt-3" style="width: 18rem; min-width: 18rem; max-width: 18rem" data-value="{{ blog.slug }}">
{% if blog.image and blog.image.url %}
<img class="card-img-top" src="{{ blog.image.url }}" alt="Card image cap">
{% endif %}
<div class="card-body">
<div class="text-right minidate" >{{ blog.date }}</div>
<h3 class="card-title text-center">{{ blog.title }}</h3>
{% autoescape off %}
<p class="card-text">{{ blog.description|truncatechars:200 }}</p>
{% endautoescape %}
<div id="cardid" class="text-right minidate" >{{ blog.slug }}</div>
{% if blog.url %}
<div class="card-footer">
<a href="{{ project.url }}" class="btn btn-primary">Follow URL</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
<footer class="fixed-bottom bg-light">
<ul class="nav justify-content-end">
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='impress' %}">Impressum</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='contact' %}">Kontakt</a></li>
</ul>
</footer>
</div>
</body>
</html>
\ No newline at end of file
{% endblock %}
\ No newline at end of file
{% load bootstrap4 %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
{% bootstrap_javascript %}
{% bootstrap_css %}
<link rel="stylesheet" href="{% static 'blog/styles.css' %}">
{% block css %}
{% endblock %}
<meta charset="UTF-8">
<title>
{% block page-title %}
{% endblock %}
</title>
</head>
<body>
<div class="jumbotron jumbotron-fluid">
<div class="container">
{% block heading %}
{% endblock %}
</div>
</div>
{% block content %}
{% endblock %}
<footer class="fixed-bottom bg-light">
<ul class="nav justify-content-end">
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='impress' %}">Impressum</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='contact' %}">Kontakt</a></li>
</ul>
</footer>
</body>
</html>
\ No newline at end of file
{% extends 'blog/base.html' %}
{% load bootstrap4 %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="{% static 'blog/styles.css' %}">
{% block css %}
<link rel="stylesheet" href="{% static 'blog/comment-area.css' %}">
<meta charset="UTF-8">
<title>Title</title>
{% bootstrap_javascript %}
{% bootstrap_css %}
</head>
<body>
<div class="jumbotron jumbotron-fluid bg-info">
<div class="container">
<h6 class="display-6 fredericka">Blog-ID: {{blog.id}}</h6>
<h1 class="display-2 fredericka">{{blog.title}}</h1>
</div>
</div>
{% endblock %}
{% block page-title %}
{{blog.title}}
{% endblock %}
<div class="container">
{% bootstrap_messages %}
<div class="text-right minidate" >{{ blog.date }}</div>
{% block heading %}
<h1 class="display-2">{{blog.title}}</h1>
{% endblock %}
{% autoescape off %}
<p class="card-text">{{ blog.description }}</p>
{% endautoescape %}
{% block content %}
<div class="container details">
{% bootstrap_messages %}
<div class="text-right minidate">{{ blog.date }}</div>
<a href="{% url 'blog:all_blogs' %}" class="btn btn-primary">Back to Blogs</a>
<br>
{% if blog.image and blog.image.url %}
<div class="container thirtypercent">
<img class="card-img-top" src="{{ blog.image.url }}" alt="Card image cap">
<img class="card-img-top" src="{{ blog.image.url }}" alt="Card image cap">
</div>
{% endif %}
{% autoescape off %}
<p class="card-text">{{ blog.description }}</p>
{% endautoescape %}
<hr>
<a href="{% url 'blog:all_blogs' %}" class="btn btn-primary">Zurück zur Übersicht</a>
</div>
{% block comments %}
{% block comments %}
<!-- Comments will go in here -->
{% endblock %}
<footer class="fixed-bottom bg-light">
<ul class="nav justify-content-end">
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='impress' %}">Impressum</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'blog:blog-detail' slug='contact' %}">Kontakt</a></li>
</ul>
</footer>
</body>
</html>
\ No newline at end of file
{% endblock %}
{% endblock %}
\ No newline at end of file
"""personal_portfolio URL Configuration
"""personal_blog URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
......
......@@ -10,6 +10,9 @@ from django.views.generic import DetailView
# Create your views here.
def all_blogs(request):
""" Show all blog articles in an overview list.
Will exclude those which should be hidden in this list (e.g. impress)
"""
# Exclude those with status hidden from the overall list
query = f'status & {BLOG_STATE_HIDE_FROM_LIST} <> {BLOG_STATE_HIDE_FROM_LIST}'
blogs = Blog.objects.extra(where=[query]).order_by('created')
......@@ -18,6 +21,9 @@ def all_blogs(request):
def detail(request, slug):
""" Shows one complete article, based on the slug (url)
If comments are allowed, they will be shown as well (uses an extended template)
"""
blogResult = Blog.objects.filter(slug=slug)
print(blogResult)
if blogResult.count() == 0:
......@@ -37,6 +43,9 @@ def detail(request, slug):
def save_comment(request):
"""The Request to save a comment is sent to here.
Only post is allowed. Will send back to details page with success/error message.
"""
if request.method != 'POST':
# no form submit - return 405
return HttpResponseNotAllowed(['POST'])
......@@ -48,7 +57,7 @@ def save_comment(request):
else:
messages.error(request, 'Ups! Da ist etwas schief gelaufen. Wir konnten deinen Kommentar nicht speichern!')
print(form.data.get('related_blog'))
# read origin form from the related blog id
blog = get_object_or_404(Blog, pk=form.data.get('related_blog'))
# back to the details page
return redirect(reverse_lazy('blog:blog-detail', kwargs={'slug': blog.slug}))
......@@ -5,7 +5,7 @@ import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'personal_portfolio.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'personal_blog.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
......
"""
ASGI config for personal_portfolio project.
ASGI config for personal_blog project.
It exposes the ASGI callable as a module-level variable named ``application``.
......@@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'personal_portfolio.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'personal_blog.settings')
application = get_asgi_application()
"""
Django settings for personal_portfolio project.
Django settings for personal_blog project.
Generated by 'django-admin startproject' using Django 3.0.7.
......@@ -38,7 +38,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
'portfolio',
'startpage',
'bootstrap4',
'ckeditor',
'ckeditor_uploader',
......@@ -54,7 +54,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'personal_portfolio.urls'