Compare commits
73 Commits
master
...
translatio
Author | SHA1 | Date |
---|---|---|
Christoph J. Scherr | e0b6a2885b | |
Christoph J. Scherr | 57174c88a1 | |
Christoph J. Scherr | 57dbd03371 | |
Christoph J. Scherr | 3fc146f1c5 | |
Christoph J. Scherr | 3d055592fc | |
Christoph J. Scherr | f6dfb7b0b7 | |
Christoph J. Scherr | 247fe8e206 | |
Christoph J. Scherr | 917e6b6167 | |
Christoph J. Scherr | 1f2e66b1ef | |
Christoph J. Scherr | 260005dcf0 | |
Christoph J. Scherr | 21f3b19111 | |
Christoph J. Scherr | f76c18efaf | |
Christoph J. Scherr | 559eb3f9fa | |
Christoph J. Scherr | 06e3172234 | |
Christoph J. Scherr | 8ef158c034 | |
Christoph J. Scherr | 62d7fbe57b | |
Christoph J. Scherr | fb438c26b1 | |
Christoph J. Scherr | bf30427679 | |
Christoph J. Scherr | a613786c7b | |
Christoph J. Scherr | 72407c1a3e | |
Christoph J. Scherr | 0d8afc96f2 | |
Christoph J. Scherr | 448c54aeb3 | |
Christoph J. Scherr | ba27629036 | |
Christoph J. Scherr | 64f84d58bc | |
Christoph J. Scherr | 9b7c73a977 | |
Christoph J. Scherr | 83d75823a4 | |
Christoph J. Scherr | c22807921d | |
Christoph J. Scherr | 3534f5399e | |
Christoph J. Scherr | f7f0675d26 | |
Christoph J. Scherr | 48a0d26745 | |
Christoph J. Scherr | c0750fdb30 | |
Christoph J. Scherr | 1ce0d52302 | |
Christoph J. Scherr | 7e3f33a824 | |
Christoph J. Scherr | c59d243cc0 | |
Christoph J. Scherr | d287b3291d | |
Christoph J. Scherr | e8c332fce4 | |
Christoph J. Scherr | 6f2267e18a | |
Christoph J. Scherr | 50db49911f | |
Christoph J. Scherr | 05497e10e1 | |
Christoph J. Scherr | a9a90e9bd4 | |
Christoph J. Scherr | 3bf0dd42c7 | |
Christoph J. Scherr | 3be2f095c0 | |
Christoph J. Scherr | b269b452fd | |
Christoph J. Scherr | adac986018 | |
Christoph J. Scherr | a2fc4eb8b8 | |
Christoph J. Scherr | 6ad7f0cfbd | |
Christoph J. Scherr | c42653df80 | |
Christoph J. Scherr | 37c3104bc5 | |
Christoph J. Scherr | 6d9660f44c | |
Christoph J. Scherr | f8c3577a54 | |
Christoph J. Scherr | 05ade38f2f | |
Christoph J. Scherr | ed75a20862 | |
Christoph J. Scherr | a027fd7582 | |
Christoph J. Scherr | 42ea8d14d4 | |
Christoph J. Scherr | d81b20e391 | |
Christoph J. Scherr | a687700c85 | |
Christoph J. Scherr | 2c8b562601 | |
Christoph J. Scherr | 56cd5943d2 | |
Christoph J. Scherr | ff231dfbc1 | |
Christoph J. Scherr | 7e7db5a480 | |
Christoph J. Scherr | 7d03b80f7a | |
Christoph J. Scherr | 50d17032af | |
Christoph J. Scherr | eb1d4e85ed | |
Christoph J. Scherr | d3c2c22e82 | |
Christoph J. Scherr | 8a99e38c74 | |
Christoph J. Scherr | e561153284 | |
Christoph J. Scherr | ab7ea1b17b | |
Christoph J. Scherr | 6de4f905d8 | |
Christoph J. Scherr | e87f6648cf | |
Christoph J. Scherr | edf5f7e190 | |
Christoph J. Scherr | 002656ea72 | |
Christoph J. Scherr | 6016a864d9 | |
Christoph J. Scherr | f844fab25e |
|
@ -17,4 +17,5 @@ db/data/ibdata1
|
|||
db/data/ibtmp1
|
||||
db/data/multi-master.info
|
||||
db/data/mysql_upgrade_info
|
||||
gawa/media/CACHE
|
||||
gawa/static/CACHE
|
||||
gawa/media/img/links/favicons
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "blog"]
|
||||
path = site/blog
|
||||
url = https://github.com/xenocrat/chyrp-lite
|
||||
[submodule "gawa/static/tagify"]
|
||||
path = gawa/static/tagify
|
||||
url = https://github.com/yairEO/tagify
|
33
README.md
33
README.md
|
@ -1,8 +1,35 @@
|
|||
# gawa
|
||||
Gawa is my personal website. I've personally written it using the django framework.
|
||||
# Gawa
|
||||
|
||||
Gawa is my personal website. I've personally written it using the Django framework.
|
||||
|
||||
## Credentials
|
||||
|
||||
These are the Credentials for logging into the admin panel:
|
||||
| Username | Password |
|
||||
|----------|----------|
|
||||
| `root` | `root` |
|
||||
|
||||
### Blog
|
||||
|
||||
| Username | Password |
|
||||
|--------------------------------------------------|----------------|
|
||||
| [`contact@cscherr.de`](mailto:contact@cscherr.de) | `hrCcDa0jBspG` |
|
||||
|
||||
## License
|
||||
|
||||
Bootstrap: MIT Licensed
|
||||
Django: BSD 3-Clause "New" or "Revised" License
|
||||
|
||||
###### Gawa: MIT Licensed, see LICENSE
|
||||
__Gawa: MIT Licensed, see LICENSE__
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Description | Package (fedora) |
|
||||
|----------------|------------------|
|
||||
| Database stuff | `libpq-devel` |
|
||||
| Database stuff | `mariadb` |
|
||||
|
||||
## Security
|
||||
|
||||
- [ ] Do something about the files in the blog dir
|
||||
(.git dir for example)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,2 +0,0 @@
|
|||
default-character-set=utf8mb4
|
||||
default-collation=utf8mb4_general_ci
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,66 +1,67 @@
|
|||
services:
|
||||
db:
|
||||
image: mariadb
|
||||
container_name: gawa-db
|
||||
networks:
|
||||
- internal
|
||||
ports:
|
||||
- 3306:3306
|
||||
environment:
|
||||
MYSQL_DATABASE: gawa
|
||||
MYSQL_USER: gawa
|
||||
MYSQL_PASSWORD: changethisforprod
|
||||
# MYSQL_DATABASE: gawa
|
||||
# MYSQL_USER: gawa
|
||||
# MYSQL_PASSWORD: changethisforprod
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
volumes:
|
||||
- ./db/data:/var/lib/mysql
|
||||
# - db_data:/var/lib/mysql
|
||||
- ./docker/db/scripts:/docker-entrypoint-initdb.d/
|
||||
|
||||
gawa:
|
||||
build: ./web
|
||||
container_name: gawa
|
||||
command: python manage.py runserver 0.0.0.0:80
|
||||
main:
|
||||
build: ./docker/main
|
||||
command: bash -c "echo 'setting django up'
|
||||
&& python manage.py makemessages --all
|
||||
&& python manage.py compilemessages
|
||||
&& python manage.py collectstatic --noinput
|
||||
&& python manage.py check
|
||||
&& echo 'waiting a few seconds for database to start'
|
||||
&& sleep 1
|
||||
&& python manage.py migrate
|
||||
&& python manage.py createcachetable
|
||||
&& DJANGO_SUPERUSER_PASSWORD='root' python manage.py createsuperuser\
|
||||
--username root --noinput --email software@cscherr.de || true
|
||||
&& python manage.py runserver 0.0.0.0:80
|
||||
"
|
||||
volumes:
|
||||
- ./gawa:/app
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
- MARIADB_NAME=gawa
|
||||
- MARIADB_USER=gawa
|
||||
- MARIADB_PASSWORD=changethisforprod
|
||||
networks:
|
||||
- internal
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
nginx:
|
||||
# only for developement. Use dedicated static container in prod
|
||||
image: nginx
|
||||
container_name: gawa-web
|
||||
caddy:
|
||||
image: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8081:8081"
|
||||
# - "443:443"
|
||||
# - "443:443/udp"
|
||||
volumes:
|
||||
- ./web/templates:/etc/nginx/templates
|
||||
- ./gawa/static:/var/www/static
|
||||
- ./gawa/media:/var/www/media
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- NGINX_HOST=0.0.0.0
|
||||
- NGINX_PORT=80
|
||||
networks:
|
||||
- internal
|
||||
depends_on:
|
||||
- gawa
|
||||
- $PWD/docker/caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
- ./gawa/static:/srv/static
|
||||
- ./gawa/media:/srv/media
|
||||
|
||||
phpmyadmin:
|
||||
db-admin:
|
||||
image: phpmyadmin
|
||||
container_name: gawa-db-admin
|
||||
networks:
|
||||
- internal
|
||||
ports:
|
||||
- "127.0.0.1:8082:80"
|
||||
- 8080:80
|
||||
environment:
|
||||
- PMA_HOST=db
|
||||
- PMA_ABSOLUTE_URI=http://localhost:8080
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
# db_data:
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
FROM3
|
||||
Run \
|
||||
wget 'https://github.com/writefreely/writefreely/releases/download/v0.14.0/writefreely_0.14.0_linux_amd64.tar.gz'
|
|
@ -0,0 +1,26 @@
|
|||
http://localhost {
|
||||
handle_path /static/* {
|
||||
root * /srv/static
|
||||
file_server {
|
||||
hide .git
|
||||
precompressed zstd br gzip
|
||||
}
|
||||
}
|
||||
handle_path /media/* {
|
||||
root * /srv/media
|
||||
file_server {
|
||||
hide .git
|
||||
precompressed zstd br gzip
|
||||
}
|
||||
}
|
||||
|
||||
reverse_proxy http://main
|
||||
}
|
||||
|
||||
# http://localhost:8080 {
|
||||
# reverse_proxy http://db-admin
|
||||
# }
|
||||
|
||||
http://localhost:8081 {
|
||||
reverse_proxy http://blog:2368
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-- CREATE USER root@'%' IDENTIFIED BY 'root';
|
||||
CREATE USER gawa@'%' IDENTIFIED BY 'changethisforprod';
|
||||
GRANT ALL PRIVILEGES ON `gawa`.* TO 'gawa'@'%';
|
||||
CREATE USER blog@'%' IDENTIFIED BY 'blogpass';
|
||||
GRANT ALL PRIVILEGES ON `blog`.* TO 'blog'@'%';
|
|
@ -0,0 +1,24 @@
|
|||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.2.1
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- Host: db
|
||||
-- Erstellungszeit: 27. Sep 2023 um 18:44
|
||||
-- Server-Version: 10.11.3-MariaDB-1:10.11.3+maria~ubu2204
|
||||
-- PHP-Version: 8.1.19
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- Datenbank: `gawa`
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `gawa` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
USE `gawa`;
|
|
@ -8,3 +8,5 @@ RUN apt update && apt install -y gettext && rm -rf /var/lib/apt/lists/*
|
|||
COPY requirements.txt /app/
|
||||
RUN pip install -r requirements.txt
|
||||
COPY . /app/
|
||||
RUN mkdir -p /app/static
|
||||
RUN pygmentize -S nord -f html -a .codehilite > /app/static/codehighlight.css
|
|
@ -0,0 +1,13 @@
|
|||
Django>=4.0,<5.0
|
||||
psycopg2>=2.8
|
||||
mysqlclient>=1.4.3
|
||||
django_compressor>=2.2
|
||||
django-libsass>=0.7
|
||||
pillow>=9.0.0
|
||||
colorlog>=6.7.0
|
||||
favicon>=0.7.0
|
||||
markdown>=3.4.4
|
||||
Pygments>=2.16.1
|
||||
toml>=0.10
|
||||
beautifulsoup4>=4.12.2
|
||||
django-rosetta>=0.9.9
|
|
@ -1,5 +1,7 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.utils.translation import gettext as _
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from .models import *
|
||||
|
||||
@admin.register(Category)
|
||||
|
@ -15,6 +17,7 @@ def regenerate(modeladmin, request, queryset):
|
|||
for obj in queryset:
|
||||
obj.regenerate()
|
||||
|
||||
|
||||
@admin.register(BlogPost)
|
||||
class BlogPostAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
|
@ -24,3 +27,16 @@ class BlogPostAdmin(admin.ModelAdmin):
|
|||
date_hierarchy = "date"
|
||||
ordering = ['title_de', 'title_en']
|
||||
actions = [regenerate]
|
||||
|
||||
change_list_template = "admin/blogpost.html"
|
||||
|
||||
def get_urls(self):
|
||||
urls = super().get_urls()
|
||||
my_urls = [
|
||||
path('sync/', self.sync_with_fs),
|
||||
]
|
||||
return my_urls + urls
|
||||
|
||||
def sync_with_fs(self, request):
|
||||
BlogPost.sync_with_fs()
|
||||
return HttpResponseRedirect("../")
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,23 @@
|
|||
date = "2023-10-02 09:59:00.127936"
|
||||
update = "2023-10-02 10:59:00.127936"
|
||||
keywords = ["bash"]
|
||||
category = "Guide"
|
||||
featured = true
|
||||
public = true
|
||||
thumbnail = "img/thumbnails/bash.png"
|
||||
|
||||
[lang.en]
|
||||
title = "bash arrays"
|
||||
subtitle = "how to work with bash arrays"
|
||||
desc = """
|
||||
bash scripting can be kind of weird.
|
||||
This guide explains how they work without any fuzz.
|
||||
"""
|
||||
|
||||
[lang.de]
|
||||
title = "Bash Arrays"
|
||||
subtitle = "Wie man mit Bash Arrays arbeitet"
|
||||
desc = """
|
||||
Bash Skripte sind manchmal etwas seltsam.
|
||||
Hier wird erklärt, wie man mit Bash Arrays arbeitet.
|
||||
"""
|
|
@ -0,0 +1,99 @@
|
|||
**NOTE**
|
||||
|
||||
This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays)
|
||||
about bash scripting. It's a good article and I've decided to use it to test my
|
||||
markdown rendering.
|
||||
|
||||
# GERMAN VERY YES YES
|
||||
# Bash scripting
|
||||
|
||||
[TOC]
|
||||
|
||||
| very | important | table |
|
||||
|--------|-----------|-----------|
|
||||
| v | i | t |
|
||||
| yes | super | important |
|
||||
| really | really | really |
|
||||
|
||||
cool math: $$1+2 \le \frac{1}{2}$$
|
||||
cool math: $1+2 \le \frac{1}{2}$
|
||||
|
||||
## Wait, but why?
|
||||
|
||||
Writing about Bash is challenging because it's remarkably easy for an article
|
||||
to devolve into a manual that focuses on syntax oddities. Rest assured,
|
||||
however, the intent of this article is to avoid having you RTFM.
|
||||
|
||||
## A real (actually useful) example
|
||||
|
||||
To that end, let's consider a real-world scenario and how Bash can help:
|
||||
You are leading a new effort at your company to evaluate and optimize the
|
||||
runtime of your internal data pipeline. As a first step, you want to do a
|
||||
parameter sweep to evaluate how well the pipeline makes use of threads. For
|
||||
the sake of simplicity, we'll treat the pipeline as a compiled C++ black box
|
||||
where the only parameter we can tweak is the number of threads reserved for
|
||||
data processing: `./pipeline --threads 4.`
|
||||
|
||||
## The basics
|
||||
|
||||
The first thing we'll do is define an array containing the values of the
|
||||
`--threads` parameter that we want to test:
|
||||
|
||||
```bash
|
||||
allThreads=(1 2 4 8 16 32 64 128)
|
||||
```
|
||||
|
||||
In this example, all the elements are numbers, but it need not be the
|
||||
case—arrays in Bash can contain both numbers and strings, e.g., `myArray=(1
|
||||
2 "three" 4 "five")` is a valid expression. And just as with any other Bash
|
||||
variable, make sure to leave no spaces around the equal sign. Otherwise,
|
||||
Bash will treat the variable name as a program to execute, and the `=` as its
|
||||
first parameter!
|
||||
|
||||
Now that we've initialized the array, let's retrieve a few of its
|
||||
elements. You'll notice that simply doing `echo $allThreads` will output only
|
||||
the first element.
|
||||
|
||||
To understand why that is, let's take a step back and revisit how we usually
|
||||
output variables in Bash. Consider the following scenario:
|
||||
|
||||
```bash
|
||||
type="article" echo "Found 42 $type"
|
||||
```
|
||||
|
||||
Say the variable $type is given to us as a singular noun and we want to add
|
||||
an `s` at the end of our sentence. We can't simply add an s to `$type` since
|
||||
that would turn it into a different variable, `$types`. And although we could
|
||||
utilize code contortions such as `echo "Found 42 "$type"s"`, the best way
|
||||
to solve this problem is to use curly braces: `echo "Found 42 ${type}s"`,
|
||||
which allows us to tell Bash where the name of a variable starts and ends
|
||||
(interestingly, this is the same syntax used in JavaScript/ES6 to inject
|
||||
variables and expressions in template literals).
|
||||
|
||||
So as it turns out, although Bash variables don't generally require curly
|
||||
brackets, they are required for arrays. In turn, this allows us to specify
|
||||
the index to access, e.g., `echo ${allThreads[1]}` returns the second element
|
||||
of the array. Not including brackets, e.g.,`echo $allThreads[1]`, leads Bash
|
||||
to treat `[1]` as a string and output it as such.
|
||||
|
||||
Yes, Bash arrays have odd syntax, but at least they are zero-indexed, unlike
|
||||
some other languages (I'm looking at you, R).[^1]
|
||||
|
||||
## Looping through arrays
|
||||
|
||||
Although in the examples above we used integer indices in our arrays, let's
|
||||
consider two occasions when that won't be the case: First, if we wanted the
|
||||
$i-th element of the array, where $i is a variable containing the index of
|
||||
interest, we can retrieve that element using: echo ${allThreads[$i]}. Second,
|
||||
to output all the elements of an array, we replace the numeric index with
|
||||
the @ symbol (you can think of @ as standing for all):
|
||||
|
||||
```bash
|
||||
type="article"
|
||||
echo "Found 42 $type"
|
||||
```
|
||||
|
||||
*[RTFM]: Read the Fucking Manual
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
|
||||
[^1]: Example Footnote
|
|
@ -0,0 +1,98 @@
|
|||
**NOTE**
|
||||
|
||||
This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays)
|
||||
about bash scripting. It's a good article and I've decided to use it to test my
|
||||
markdown rendering.
|
||||
|
||||
# Bash scripting
|
||||
|
||||
[TOC]
|
||||
|
||||
| very | important | table |
|
||||
|--------|-----------|-----------|
|
||||
| v | i | t |
|
||||
| yes | super | important |
|
||||
| really | really | really |
|
||||
|
||||
cool math: $$1+2 \le \frac{1}{2}$$
|
||||
cool math: $1+2 \le \frac{1}{2}$
|
||||
|
||||
## Wait, but why?
|
||||
|
||||
Writing about Bash is challenging because it's remarkably easy for an article
|
||||
to devolve into a manual that focuses on syntax oddities. Rest assured,
|
||||
however, the intent of this article is to avoid having you RTFM.
|
||||
|
||||
## A real (actually useful) example
|
||||
|
||||
To that end, let's consider a real-world scenario and how Bash can help:
|
||||
You are leading a new effort at your company to evaluate and optimize the
|
||||
runtime of your internal data pipeline. As a first step, you want to do a
|
||||
parameter sweep to evaluate how well the pipeline makes use of threads. For
|
||||
the sake of simplicity, we'll treat the pipeline as a compiled C++ black box
|
||||
where the only parameter we can tweak is the number of threads reserved for
|
||||
data processing: `./pipeline --threads 4.`
|
||||
|
||||
## The basics
|
||||
|
||||
The first thing we'll do is define an array containing the values of the
|
||||
`--threads` parameter that we want to test:
|
||||
|
||||
```bash
|
||||
allThreads=(1 2 4 8 16 32 64 128)
|
||||
```
|
||||
|
||||
In this example, all the elements are numbers, but it need not be the
|
||||
case—arrays in Bash can contain both numbers and strings, e.g., `myArray=(1
|
||||
2 "three" 4 "five")` is a valid expression. And just as with any other Bash
|
||||
variable, make sure to leave no spaces around the equal sign. Otherwise,
|
||||
Bash will treat the variable name as a program to execute, and the `=` as its
|
||||
first parameter!
|
||||
|
||||
Now that we've initialized the array, let's retrieve a few of its
|
||||
elements. You'll notice that simply doing `echo $allThreads` will output only
|
||||
the first element.
|
||||
|
||||
To understand why that is, let's take a step back and revisit how we usually
|
||||
output variables in Bash. Consider the following scenario:
|
||||
|
||||
```bash
|
||||
type="article" echo "Found 42 $type"
|
||||
```
|
||||
|
||||
Say the variable $type is given to us as a singular noun and we want to add
|
||||
an `s` at the end of our sentence. We can't simply add an s to `$type` since
|
||||
that would turn it into a different variable, `$types`. And although we could
|
||||
utilize code contortions such as `echo "Found 42 "$type"s"`, the best way
|
||||
to solve this problem is to use curly braces: `echo "Found 42 ${type}s"`,
|
||||
which allows us to tell Bash where the name of a variable starts and ends
|
||||
(interestingly, this is the same syntax used in JavaScript/ES6 to inject
|
||||
variables and expressions in template literals).
|
||||
|
||||
So as it turns out, although Bash variables don't generally require curly
|
||||
brackets, they are required for arrays. In turn, this allows us to specify
|
||||
the index to access, e.g., `echo ${allThreads[1]}` returns the second element
|
||||
of the array. Not including brackets, e.g.,`echo $allThreads[1]`, leads Bash
|
||||
to treat `[1]` as a string and output it as such.
|
||||
|
||||
Yes, Bash arrays have odd syntax, but at least they are zero-indexed, unlike
|
||||
some other languages (I'm looking at you, R).[^1]
|
||||
|
||||
## Looping through arrays
|
||||
|
||||
Although in the examples above we used integer indices in our arrays, let's
|
||||
consider two occasions when that won't be the case: First, if we wanted the
|
||||
$i-th element of the array, where $i is a variable containing the index of
|
||||
interest, we can retrieve that element using: echo ${allThreads[$i]}. Second,
|
||||
to output all the elements of an array, we replace the numeric index with
|
||||
the @ symbol (you can think of @ as standing for all):
|
||||
|
||||
```bash
|
||||
type="article"
|
||||
echo "Found 42 $type"
|
||||
```
|
||||
|
||||
*[RTFM]: Read the Fucking Manual
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
|
||||
[^1]: Example Footnote
|
|
@ -1,12 +1,47 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 12:03
|
||||
# Generated by Django 3.2.21 on 2023-10-02 08:14
|
||||
|
||||
from django.db import migrations
|
||||
import blog.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('start', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Category',
|
||||
'verbose_name_plural': 'Categories',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BlogPost',
|
||||
fields=[
|
||||
('searchable_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='start.searchable')),
|
||||
('body_en', models.TextField(blank=True, default='Dieser Artikel ist nicht auf deutsch verfügbar.')),
|
||||
('body_de', models.TextField(blank=True, default='This aritcle is not available in english.')),
|
||||
('thumbnail', models.ImageField(blank=True, default='img/thumbnails/default.jpg', upload_to='img/thumbnails')),
|
||||
('featured', models.BooleanField(default=False)),
|
||||
('langs', models.CharField(default="{'en': False, 'de': False}", max_length=64)),
|
||||
('slug', models.SlugField()),
|
||||
('category', models.ForeignKey(default=blog.models.Category.get_or_create_uncategorized, on_delete=django.db.models.deletion.SET_DEFAULT, to='blog.category')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'blog post',
|
||||
'verbose_name_plural': 'blog posts',
|
||||
},
|
||||
bases=('start.searchable',),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 19:06
|
||||
# Generated by Django 4.2.5 on 2023-10-02 19:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
@ -6,14 +6,13 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0003_blogpost_public'),
|
||||
('blog', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
migrations.AlterField(
|
||||
model_name='blogpost',
|
||||
name='slug',
|
||||
field=models.SlugField(default='test'),
|
||||
preserve_default=False,
|
||||
field=models.SlugField(unique=True),
|
||||
),
|
||||
]
|
|
@ -1,35 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 12:03
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('start', '0002_keyword_searchable_staticsite'),
|
||||
('blog', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('slug', models.SlugField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BlogPost',
|
||||
fields=[
|
||||
('searchable_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='start.searchable')),
|
||||
('body', models.TextField()),
|
||||
('thumbnail', models.ImageField(blank=True, upload_to='')),
|
||||
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='blog.category')),
|
||||
],
|
||||
bases=('start.searchable',),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 18:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0002_blogpost_category'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='public',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 22:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0004_blogpost_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='featured',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='blogpost',
|
||||
name='thumbnail',
|
||||
field=models.ImageField(blank=True, upload_to='img/thumbnails'),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 22:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0005_auto_20230604_0050'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='markdown',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 23:13
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0006_blogpost_markdown'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='blogpost',
|
||||
name='public',
|
||||
),
|
||||
]
|
|
@ -1,21 +0,0 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-03 23:56
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0007_remove_blogpost_public'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='blogpost',
|
||||
options={'verbose_name': 'blog post', 'verbose_name_plural': 'blog posts'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='category',
|
||||
options={'verbose_name': 'Category', 'verbose_name_plural': 'Categories'},
|
||||
),
|
||||
]
|
|
@ -1,10 +1,35 @@
|
|||
import toml
|
||||
import ast
|
||||
import re
|
||||
import os
|
||||
import pathlib
|
||||
import markdown
|
||||
from bs4 import BeautifulSoup
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext as _
|
||||
from start.models import Searchable
|
||||
from start.models import Keyword, Searchable
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
EXTENSIONS = [
|
||||
"extra",
|
||||
"admonition",
|
||||
"codehilite",
|
||||
"meta",
|
||||
"toc"
|
||||
]
|
||||
EXTENSION_CONFIGS = {
|
||||
'codehilite': {
|
||||
'linenums': True,
|
||||
'pygments_style': 'monokai'
|
||||
},
|
||||
}
|
||||
|
||||
MD = markdown.Markdown(extensions=EXTENSIONS,
|
||||
extension_configs=EXTENSION_CONFIGS)
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
"""
|
||||
A category of blog posts
|
||||
|
@ -12,8 +37,8 @@ class Category(models.Model):
|
|||
Name not translated because it would make i18n in urls and Searchables specifically a pain.
|
||||
Maybe some day it would be cool if these were Searchable
|
||||
"""
|
||||
name= models.CharField(max_length=50)
|
||||
slug = models.SlugField()
|
||||
name = models.CharField(max_length=50)
|
||||
slug = models.SlugField(unique=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Category")
|
||||
|
@ -22,17 +47,54 @@ class Category(models.Model):
|
|||
def __str__(self):
|
||||
return f"{{<{self.__class__.__name__}>\"{self.name}\"}}"
|
||||
|
||||
@staticmethod
|
||||
def get_or_create_uncategorized():
|
||||
try:
|
||||
return Category.objects.get(slug="uncategorized")
|
||||
except Category.DoesNotExist:
|
||||
return Category.objects.create(
|
||||
slug="uncategorized", name="uncategorized")
|
||||
|
||||
|
||||
class BlogPost(Searchable):
|
||||
"""
|
||||
Should contain a blogpost
|
||||
"""
|
||||
body = models.TextField()
|
||||
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
|
||||
thumbnail = models.ImageField(blank=True, upload_to="img/thumbnails")
|
||||
featured = models.BooleanField(default=False)
|
||||
markdown = models.BooleanField(default=False)
|
||||
slug = models.SlugField()
|
||||
DATA_DIR = "/app/blog/data/articles"
|
||||
DEFAULT_LANGS = {'en': False, 'de': False}
|
||||
META_TOP_KEYS = [
|
||||
"date",
|
||||
"update",
|
||||
"keywords",
|
||||
"category",
|
||||
"featured",
|
||||
"public",
|
||||
"lang"]
|
||||
META_LANG_KEYS = ["title", "subtitle", "desc"]
|
||||
|
||||
body_en = models.TextField(blank=True,
|
||||
default="""Dieser Artikel ist nicht auf deutsch verfügbar.""")
|
||||
body_de = models.TextField(blank=True,
|
||||
default="""This aritcle is not available in english.""")
|
||||
category = models.ForeignKey(
|
||||
Category, on_delete=models.SET_DEFAULT, blank=False,
|
||||
default=Category.get_or_create_uncategorized)
|
||||
thumbnail = models.ImageField(
|
||||
blank=True,
|
||||
upload_to="img/thumbnails",
|
||||
default="img/thumbnails/default.jpg")
|
||||
featured = models.BooleanField(default=False)
|
||||
langs = models.CharField(
|
||||
default=DEFAULT_LANGS.__repr__(), max_length=64)
|
||||
slug = models.SlugField(unique=True, blank=False)
|
||||
|
||||
def save(self):
|
||||
# check if the slug is empty if we remove whitespaces
|
||||
if len(self.slug.strip()) == 0:
|
||||
logger.error(
|
||||
f"trying to save '{self.__class__}' with empty slug: {self}")
|
||||
raise ValueError(f"trying to save '{self.__class__}' with empty slug: {self}")
|
||||
super().save()
|
||||
|
||||
def regenerate(self):
|
||||
"""
|
||||
|
@ -41,9 +103,181 @@ class BlogPost(Searchable):
|
|||
Implements the abstract method of Searchable
|
||||
"""
|
||||
logger.info(f"regenerating {self.__class__.__name__} object: {self}")
|
||||
# url stuff
|
||||
self.suburl = f"/blog/{self.category.name}/{self.slug}"
|
||||
|
||||
# load from markdown
|
||||
# self.sync_file()
|
||||
|
||||
# redundand vvvv
|
||||
self.save()
|
||||
|
||||
@staticmethod
|
||||
def __patch_html(html: str) -> str:
|
||||
soup = BeautifulSoup(html)
|
||||
|
||||
# add bootstrap classes to regular tables
|
||||
tables = soup.select("table")
|
||||
for table in tables:
|
||||
if 'class' in table.attrs and "codehilitetable" in table.attrs['class']:
|
||||
# table has at least one class already AND
|
||||
# this table is a codehighlighting table,
|
||||
# not a regular one
|
||||
continue
|
||||
# set the bootstrap classes for tables
|
||||
table.attrs['class'] = 'table table-striped border'
|
||||
return soup.prettify()
|
||||
|
||||
def sync_file(self):
|
||||
"""
|
||||
generate an article fromm it's original markdown file
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(f"syncing article to markdown for: {self}")
|
||||
# read metadata
|
||||
try:
|
||||
fmeta = open(f"{self.DATA_DIR}/{self.slug}.toml", "r")
|
||||
except Exception as e:
|
||||
logger.error(f"could not find meta file for '{self}'")
|
||||
return
|
||||
data = toml.load(fmeta)
|
||||
langs = self.get_langs()
|
||||
for key in self.META_TOP_KEYS:
|
||||
if key not in data:
|
||||
logger.error(f"Key '{key}' missing in meta file for '{self}'")
|
||||
raise ValueError(
|
||||
f"Key '{key}' missing in meta file for '{self}'")
|
||||
for lang in self.DEFAULT_LANGS.keys():
|
||||
if lang not in data['lang']:
|
||||
langs[lang] = False
|
||||
else:
|
||||
langs[lang] = True
|
||||
self.set_langs(langs)
|
||||
for lang in data['lang']:
|
||||
for key in self.META_LANG_KEYS:
|
||||
if key not in data['lang'][lang]:
|
||||
logger.warning(
|
||||
f"Key '{key}' ('{lang}') missing in meta file for '{self}'")
|
||||
langs[lang] = False
|
||||
else:
|
||||
langs[lang] = True
|
||||
if not langs[lang]:
|
||||
# no translation for this language
|
||||
continue
|
||||
with open(f"{self.DATA_DIR}/{lang}-{self.slug}.md") as f_en:
|
||||
MD.reset()
|
||||
body: str = f_en.read()
|
||||
match lang:
|
||||
case "en":
|
||||
self.title_en = data['lang'][lang]["title"]
|
||||
self.subtitle_en = data['lang'][lang]["subtitle"]
|
||||
self.desc_en = data['lang'][lang]["desc"]
|
||||
self.body_en = BlogPost.__patch_html(MD.convert(body))
|
||||
case "de":
|
||||
self.title_de = data['lang'][lang]["title"]
|
||||
self.subtitle_de = data['lang'][lang]["subtitle"]
|
||||
self.desc_de = data['lang'][lang]["desc"]
|
||||
self.body_de = BlogPost.__patch_html(MD.convert(body))
|
||||
case _:
|
||||
logger.error(
|
||||
f"unknown language '{lang}' in meta file for '{self}'")
|
||||
self.date = data["date"]
|
||||
self.update = data["update"]
|
||||
self.featured = data["featured"]
|
||||
self.public = data["public"]
|
||||
# NOTE: thumbnail is optional
|
||||
if "thumbnail" in data:
|
||||
self.thumbnail = data["thumbnail"]
|
||||
# NOTE: category is optional
|
||||
if "category" in data:
|
||||
try:
|
||||
category: Category = Category.objects.get(
|
||||
slug=data['category'])
|
||||
except Category.DoesNotExist:
|
||||
category = Category.objects.create(
|
||||
name=data['category'], slug=data['category'])
|
||||
self.category = category
|
||||
self.save()
|
||||
for keyword in data["keywords"]:
|
||||
try:
|
||||
self.keywords.add(Keyword.objects.get(slug=keyword))
|
||||
except Keyword.DoesNotExist:
|
||||
self.keywords.create(
|
||||
slug=keyword, text_en=keyword, text_de=keyword)
|
||||
self.save()
|
||||
|
||||
def get_langs(self) -> dict[str, bool]:
|
||||
"""
|
||||
get available languages
|
||||
"""
|
||||
# SECURITY:
|
||||
# If someone could inject the langs field, literal_eval will evaluate to
|
||||
# any literal Python value. This should not be a concern.
|
||||
# If we would use a regular eval here, an attacker who controls one of
|
||||
# the `langs` of a BlogPost could run arbitrary Python code.
|
||||
try:
|
||||
# literal_eval is safe because it only parses literals, not any kind
|
||||
# of python expression. If the given str is not a valid literal,
|
||||
# ValueError will be raised
|
||||
langs = ast.literal_eval(str(self.langs))
|
||||
return langs
|
||||
except ValueError as e:
|
||||
logger.error(
|
||||
f"could not safely evaluate 'langs' for '{self}': {e}")
|
||||
raise e
|
||||
|
||||
def set_langs(self, langs: dict[str, bool]):
|
||||
"""
|
||||
set available languages
|
||||
"""
|
||||
self.langs = langs.__repr__()
|
||||
|
||||
@ classmethod
|
||||
def sync_with_fs(cls):
|
||||
"""
|
||||
Sync all Blog Posts with the filesystem.
|
||||
|
||||
Caution: Will delete all Blog Posts
|
||||
"""
|
||||
# logger.name = logger.name + ".sync_with_fs"
|
||||
|
||||
# delete all existing objects
|
||||
BlogPost.objects.all().delete()
|
||||
|
||||
# check if the DATA_DIR is OK
|
||||
data_dir = pathlib.Path(cls.DATA_DIR)
|
||||
if not data_dir.exists():
|
||||
logger.error(f"'{cls.DATA_DIR} does not exist'")
|
||||
if not data_dir.is_dir():
|
||||
logger.error(f"'{cls.DATA_DIR} is not a directory'")
|
||||
|
||||
files = [f for f in os.listdir(data_dir) if (
|
||||
data_dir.joinpath(f)).is_file()]
|
||||
|
||||
# find the meta file
|
||||
regex = r"^(.*)\.toml"
|
||||
|
||||
# filepath, slug
|
||||
files = [[f, ""] for f in files]
|
||||
for file in files:
|
||||
# parse file name
|
||||
try:
|
||||
matches = re.match(regex, file[0])
|
||||
if matches is None:
|
||||
# file is not a toml / meta file
|
||||
files.remove(file)
|
||||
continue
|
||||
else:
|
||||
current_lang = matches.group(1)
|
||||
file[1] = matches.group(1)
|
||||
|
||||
obj = BlogPost(slug=file[1])
|
||||
obj.sync_file()
|
||||
obj.regenerate()
|
||||
except Exception as e:
|
||||
logger.error(f"Could not create BlogPost for '{file[1]}': {e}")
|
||||
files.remove(file)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("blog post")
|
||||
verbose_name_plural = _("blog posts")
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{% extends 'admin/change_list.html' %} {% block object-tools %}
|
||||
<form action="sync" method="POST">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button" style="padding: 4px">Sync with FS</button>
|
||||
</form>
|
||||
{{ block.super }} {% endblock %}
|
|
@ -1,26 +1,96 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load helper_tags %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% block languagecode %}{{ LANGUAGE_CODE }}{% endblock languagecode %}
|
||||
{% block title %}{% translate "cscherr.de" %} - {% translate "Blog" %}{% endblock title %}
|
||||
{% block languagecode %}
|
||||
{{ LANGUAGE_CODE }}
|
||||
{% endblock languagecode %}
|
||||
{% block title %}
|
||||
{% translate "cscherr.de" %} - {% translate "Blog" %}
|
||||
{% endblock title %}
|
||||
{% block nav %}
|
||||
{% include 'nav.html' %}
|
||||
{% include 'nav.html' %}
|
||||
{% endblock nav %}
|
||||
{% block headscripts %}
|
||||
<script type="text/javascript" id="MathJax-script" async
|
||||
src="https://cdn.jsdelivr.net/npm/mathjax@3.0.0/es5/tex-mml-chtml.js">
|
||||
</script>
|
||||
{% endblock headscripts %}
|
||||
{% block head %}
|
||||
<script type="text/javascript"
|
||||
id="MathJax-script"
|
||||
async
|
||||
src="https://cdn.jsdelivr.net/npm/mathjax@3.0.0/es5/tex-mml-chtml.js"></script>
|
||||
{% endblock head %}
|
||||
{% block main %}
|
||||
<div class="container-xl">
|
||||
<div class="jumbotron text-center">
|
||||
<h1>{{ post.title }} <small class="">{{ post.subtitle }}</small></h1>
|
||||
<img src="{{ post.thumbnail.url }}" alt="thumbnail">
|
||||
<div class="container-xl">
|
||||
<article>
|
||||
<div class="jumbotron my-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<picture>
|
||||
<img src="{{ post.thumbnail.url }}" alt="thumbnail" class="img-fluid">
|
||||
</picture>
|
||||
</div>
|
||||
<div class="container">
|
||||
$$x=\frac{-b+\sqrt{b^2-4ac}}{2a}$$
|
||||
{{ post.body | safe }}
|
||||
<div class="col">
|
||||
<div class="row py-5">
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<h1 class="">
|
||||
{{ post.title_de }}
|
||||
<br>
|
||||
<small class="fs-4">{{ post.subtitle_de }}</small>
|
||||
</h1>
|
||||
<br>
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
<h1 class="">
|
||||
{{ post.title_en }}
|
||||
<br>
|
||||
<small class="fs-4">{{ post.subtitle_en }}</small>
|
||||
</h1>
|
||||
<br>
|
||||
{% else %}
|
||||
<h1 class="">
|
||||
{{ post.title_en }}
|
||||
<br>
|
||||
<small class="fs-4">{{ post.subtitle_en }}</small>
|
||||
</h1>
|
||||
<br>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row py-5">
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<p class="lead">{{ post.desc_de }}</p>
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
<p class="lead">{{ post.desc_en }}</p>
|
||||
{% else %}
|
||||
<p class="lead">{{ post.desc_en }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row py-5">
|
||||
<p>
|
||||
<b>{{ post.category.name }}<b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="my-5">
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
{{ post.body_de | safe }}
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
{{ post.body_en | safe }}
|
||||
{% else %}
|
||||
{{ post.body_en | safe }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row text-center">
|
||||
<div class="col">
|
||||
{% format_time post.date as date %}
|
||||
<p>{% trans "published" %}: {{ date }}</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
{% format_time post.update as update %}
|
||||
<p>{% trans "updated" %}: {{ update }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
{% include 'blog/featured.html' %}
|
||||
</div>
|
||||
{% endblock main %}
|
||||
{% endblock main %}
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load helper_tags %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% block languagecode %}
|
||||
{{ LANGUAGE_CODE }}
|
||||
{% endblock languagecode %}
|
||||
{% block title %}
|
||||
{% translate "cscherr.de" %} - {% translate "Blog" %}
|
||||
{% endblock title %}
|
||||
{% block blockname %}
|
||||
{% endblock blockname %}
|
||||
{% block nav %}
|
||||
{% include 'nav.html' %}
|
||||
{% endblock nav %}
|
||||
{% block main %}
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-5">
|
||||
<div class="col col-xxl mb-5" id="headline">
|
||||
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
|
||||
href="{% url "blog:browse" %}"
|
||||
style="display: inline-block">
|
||||
<h1 class="display-1 w-0">Browse</h1>
|
||||
</a>
|
||||
{# center headline on small screens #}
|
||||
<script>
|
||||
if (screen.width < 480) {
|
||||
col = document.getElementById("headline")
|
||||
col.classList.add("text-center")
|
||||
};
|
||||
</script>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="container-fluid w-100 h-100 p-2">
|
||||
<form class="w-100 h-100"
|
||||
role="search"
|
||||
action=""
|
||||
method="GET"
|
||||
novalidate
|
||||
id="filter-form">
|
||||
<div class="py-2">
|
||||
<input type="search"
|
||||
name="search"
|
||||
class="form-control flex-fill py-2"
|
||||
defaultValue=""
|
||||
aria-label="Search"
|
||||
placeholder="{% trans "Search" %}"
|
||||
required=""
|
||||
{% if filters.search %}value="{{ filters.search }}"{% endif %}
|
||||
id="id_search">
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<select class="form-select py-2"
|
||||
aria-label="Large select example"
|
||||
defaultValue=""
|
||||
name="category">
|
||||
<option value="">{% trans "select category" %}</option>
|
||||
{% for category in categories %}
|
||||
<option {% if filters.category.slug == category.slug %}selected{% endif %}
|
||||
value="{{ category.slug }}">{{ category.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<input class="tagify"
|
||||
name="keywords"
|
||||
defaultValue=""
|
||||
placeholder="{% trans "Keywords" %}"
|
||||
{% if filters.keywords %} value="{% for keyword in filters.keywords %}{{ keyword }} {% endfor %}
|
||||
"
|
||||
{% endif %}>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="py-2">
|
||||
<!-- will reset to the filters encoded in
|
||||
the url by GET attributes, not to the empty
|
||||
form. -->
|
||||
<input type="reset"
|
||||
class="btn bg-primary fw-bold py-2 float-end w-100 reset-empty-button">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="py-2">
|
||||
<button id="filter-button"
|
||||
class="btn bg-primary fw-bold py-2 float-end w-100"
|
||||
type="submit">{% trans "Filter" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var input = document.querySelector('input[class=tagify]');
|
||||
new Tagify(input, {
|
||||
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(','),
|
||||
enforceWhitelist: true,
|
||||
whitelist : [{% for keyword in keywords %}'{{ keyword.slug | safe }}', {% endfor %}],
|
||||
dropdown : {
|
||||
classname : "tagify-dropdown", // TODO: style better
|
||||
enabled : 1, // show the dropdown immediately on focus
|
||||
maxItems : 5,
|
||||
position : "text", // place the dropdown near the typed text
|
||||
closeOnSelect : false, // keep the dropdown open after selecting a suggestion
|
||||
highlightFirst: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="container-fluid p-0 w-100 h-100">
|
||||
{% if not posts %}
|
||||
<div class="text-center">
|
||||
<img src="/media/img/http/404.svg"
|
||||
class="img-fluid pb-5 pt-2 darkmode-invert"
|
||||
style="max-height: 650px"
|
||||
alt="404" />
|
||||
<h2 class="display-5">{% trans "No posts found for your filters." %}</h2>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row gap-3 cardrow">
|
||||
{% for post in posts %}
|
||||
<div class="card col mx-auto my-2 py-2">
|
||||
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
|
||||
href=" {% url 'blog:post' post.category.slug post.slug %}">
|
||||
<img src="{{ post.thumbnail.url }}"
|
||||
class="card-img-top img-fluid mx-auto d-block"
|
||||
style="max-height: 150px;
|
||||
width: auto;
|
||||
padding-top: 8px"
|
||||
alt="thumbnail" />
|
||||
<div class="card-body" style="height: 100px">
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<h5 class="card-title">
|
||||
{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_de }}</p>
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
<h5 class="card-title">
|
||||
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_en }}</p>
|
||||
{% else %}
|
||||
<h5 class="card-title">
|
||||
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_en }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="container pt-5">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
{% translate "category" %}: <b>{{ post.category.name }}</b>
|
||||
</li>
|
||||
{% for keyword in post.keywords.all %}
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<li class="list-group-item">{{ keyword.text_de }}</li>
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
<li class="list-group-item">{{ keyword.text_en }}</li>
|
||||
{% else %}
|
||||
<li class="list-group-item">{{ keyword.text_en }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container p-1 text-center" style="border-top: solid">
|
||||
<li class="list-group-item">
|
||||
{% format_time post.date "%F" as date %}
|
||||
{% trans "published" %}: {{ date }}
|
||||
</li>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% empty %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<nav aria-label="Page navigation example" class="mt-5">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?{% query_transform page=1 %}">First</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="?{% query_transform page=page_obj.previous_page_number %}">{{ page_obj.previous_page_number }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="">{{ page_obj.number }}</a>
|
||||
</li>
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="?{% query_transform page=page_obj.next_page_number %}">{{ page_obj.next_page_number }}</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="?{% query_transform page=page_obj.paginator.num_pages %}">Last</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock main %}
|
|
@ -1,27 +1,40 @@
|
|||
{% load i18n %}
|
||||
{% load helper_tags %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<div class="container-lg mt-5">
|
||||
<h4 class="">{% trans "Featured" %}</h4>
|
||||
<div class="row row-cols-1 row-cols-md-5 my-4">
|
||||
<div class="row gap-1 cardrow">
|
||||
{% for post in featured_posts %}
|
||||
<div class="card col m-2 p-0">
|
||||
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0" href=" {% url 'blog:post' post.category.slug post.slug %}">
|
||||
<img src="{{ post.thumbnail.url }}" class="card-img-top img-fluid" style="max-height: 150px;" alt="thumbnail">
|
||||
<div class="card-body" style="height: 100px;">
|
||||
<div class="card col mx-auto my-2">
|
||||
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
|
||||
href=" {% url 'blog:post' post.category.slug post.slug %}">
|
||||
<img src="{{ post.thumbnail.url }}"
|
||||
class="card-img-top img-fluid mx-auto d-block"
|
||||
style="max-height: 150px; width: auto; padding-top: 8px;"
|
||||
alt="thumbnail" />
|
||||
<div class="card-body" style="height: 100px">
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<h5 class="card-title">{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small></h5>
|
||||
<h5 class="card-title">
|
||||
{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_de }}</p>
|
||||
{% elif LANGUAGE_CODE == "en" %}
|
||||
<h5 class="card-title">{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small></h5>
|
||||
<h5 class="card-title">
|
||||
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_en }}</p>
|
||||
{% else %}
|
||||
<h5 class="card-title">{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small></h5>
|
||||
<h5 class="card-title">
|
||||
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
|
||||
</h5>
|
||||
<p class="card-text">{{ post.desc_en }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="container pt-5">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">{% translate "category" %}: <b>{{ post.category.name }}</b></li>
|
||||
<li class="list-group-item">
|
||||
{% translate "category" %}: <b>{{ post.category.name }}</b>
|
||||
</li>
|
||||
{% for keyword in post.keywords.all %}
|
||||
{% if LANGUAGE_CODE == "de" %}
|
||||
<li class="list-group-item">{{ keyword.text_de }}</li>
|
||||
|
@ -33,6 +46,12 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container p-1 text-center" style="border-top: solid">
|
||||
<li class="list-group-item">
|
||||
{% format_time post.date "%F" as date %}
|
||||
{% trans "published" %}: {{ date }}
|
||||
</li>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% block languagecode %}{{ LANGUAGE_CODE }}{% endblock languagecode %}
|
||||
{% block title %}{% translate "cscherr.de" %} - {% translate "Blog" %}{% endblock title %}
|
||||
{% block languagecode %}
|
||||
{{ LANGUAGE_CODE }}
|
||||
{% endblock languagecode %}
|
||||
{% block title %}
|
||||
{% translate "cscherr.de" %} - {% translate "Blog" %}
|
||||
{% endblock title %}
|
||||
{% block main %}
|
||||
<div class="container-xl">
|
||||
<div class="container-xl">
|
||||
<div class="container text-center jumbotron my-3"></div>
|
||||
<div class="jumbotron text-center">
|
||||
<h1>{% translate "Was gibt es hier?" %}</h1>
|
||||
<h1>{% translate "What can be found here?" %}</h1>
|
||||
<p>{% translate "Blog" %}</p>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
|
@ -40,7 +45,13 @@
|
|||
<div class="container text-center jumbotron my-3">
|
||||
<h1 class="my-4">{% translate "Looking for anything specific?" %}</h1>
|
||||
{% include 'main_search_form.html' %}
|
||||
<h4 class="my-5">
|
||||
<a href="{% url 'blog:browse' %}"
|
||||
class="link-offset-2 link-underline link-underline-opacity-0">
|
||||
{% translate "Browse posts" %}
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
{% include 'blog/featured.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock main %}
|
||||
|
|
|
@ -5,7 +5,6 @@ from . import views
|
|||
app_name: str = "blog"
|
||||
urlpatterns = [
|
||||
path("", views.Index.as_view(), name="index"),
|
||||
path("categories", views.CategoryList.as_view(), name="category_list"),
|
||||
path("<slug:slug>", views.ArticleList.as_view(), name="article_list"),
|
||||
path("browse", views.Browse.as_view(), name="browse"),
|
||||
path("<slug:category>/<slug:slug>", views.Post.as_view(), name="post"),
|
||||
]
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
from django.shortcuts import Http404, HttpResponse, get_object_or_404, render
|
||||
import json
|
||||
import ast
|
||||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http.response import Http404, HttpResponse
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils.translation import get_language
|
||||
from django.views.generic import TemplateView, DetailView, ListView, View
|
||||
from .models import BlogPost, Category
|
||||
from django.db.models import Q
|
||||
from .models import BlogPost, Category, Keyword
|
||||
|
||||
from start.views import SearchableView
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Index(TemplateView, SearchableView):
|
||||
"""
|
||||
The index page of the gawa/blog app.
|
||||
|
@ -20,65 +27,26 @@ class Index(TemplateView, SearchableView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['featured_posts'] = BlogPost.objects.filter(featured=True, public=True)
|
||||
logger.debug(f"loaded featured posts: {context['featured_posts']}")
|
||||
context['featured_posts'] = BlogPost.objects.filter(
|
||||
featured=True, public=True)
|
||||
return context
|
||||
|
||||
|
||||
class Post(DetailView):
|
||||
"""
|
||||
Main page of a blog post
|
||||
"""
|
||||
|
||||
model=BlogPost
|
||||
model = BlogPost
|
||||
template_name = "blog/blogpost.html"
|
||||
context_object_name = "post"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['featured_posts'] = BlogPost.objects.filter(featured=True)
|
||||
logger.debug(f"loaded featured posts: {context['featured_posts']}")
|
||||
return context
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = get_object_or_404(
|
||||
BlogPost,
|
||||
category__slug=self.kwargs['category'], # first slug is category
|
||||
slug=self.kwargs['slug'] # second slug is article itself
|
||||
)
|
||||
match get_language():
|
||||
case 'de':
|
||||
logger.debug("setting language unspecific attributes for language: de")
|
||||
obj.title = obj.title_de
|
||||
obj.subtitle = obj.subtitle_de
|
||||
obj.desc = obj.desc_de
|
||||
|
||||
case 'en':
|
||||
logger.debug("setting language unspecific attributes for language: en")
|
||||
obj.title = obj.title_en
|
||||
obj.subtitle = obj.subtitle_en
|
||||
obj.desc = obj.desc_en
|
||||
|
||||
case _:
|
||||
# this should not happen, but who knows what dumb stuff users will come up with
|
||||
logger.warning("article for unsupported language was requested")
|
||||
return obj
|
||||
|
||||
class CategoryList(ListView):
|
||||
"""
|
||||
Scroll through a list of blog Categories
|
||||
"""
|
||||
|
||||
model=Category
|
||||
template_name = "blog/categories.html"
|
||||
context_object_name = "categories"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['featured_posts'] = BlogPost.objects.filter(featured=True)
|
||||
logger.debug(f"loaded featured posts: {context['featured_posts']}")
|
||||
return context
|
||||
|
||||
class ArticleList(ListView):
|
||||
class Browse(ListView):
|
||||
"""
|
||||
Scroll through a list of blog posts
|
||||
|
||||
|
@ -86,12 +54,101 @@ class ArticleList(ListView):
|
|||
Of course, Articles will also show up in the MainSearchView
|
||||
"""
|
||||
|
||||
model=BlogPost
|
||||
template_name = "blog/posts.html"
|
||||
model = BlogPost
|
||||
template_name = "blog/browse.html"
|
||||
context_object_name = "posts"
|
||||
paginate_by = 2
|
||||
allow_empty = False # but we have a special get method
|
||||
allow_empty = True
|
||||
|
||||
def get_queryset(self):
|
||||
objects = BlogPost.objects.all()
|
||||
if "category" in self.request.GET and len(
|
||||
self.request.GET["category"].strip()) > 0:
|
||||
category = self.request.GET["category"]
|
||||
try:
|
||||
category = Category.objects.get(slug=category)
|
||||
objects = objects.filter(category=category)
|
||||
except Category.DoesNotExist:
|
||||
objects = objects.none()
|
||||
if "search" in self.request.GET and len(
|
||||
self.request.GET["search"].strip()) > 0:
|
||||
search = self.request.GET["search"]
|
||||
# __icontains matches those attributes that contain the
|
||||
# search string without caring about lower/upper cases
|
||||
objects = objects.filter(
|
||||
Q(title_en__icontains=search) |
|
||||
Q(title_de__icontains=search) |
|
||||
Q(subtitle_en__icontains=search) |
|
||||
Q(subtitle_de__icontains=search) |
|
||||
Q(desc_en__icontains=search) |
|
||||
Q(desc_de__icontains=search) |
|
||||
# Q(body_en__icontains=search) |
|
||||
# Q(body_de__icontains=search) |
|
||||
Q(slug__icontains=search)
|
||||
)
|
||||
|
||||
if "keywords" in self.request.GET and len(
|
||||
self.request.GET["keywords"].strip()) > 0:
|
||||
raw_keywords = self.request.GET["keywords"]
|
||||
raw_keywords = raw_keywords.split('+')
|
||||
keywords: list[Keyword] = []
|
||||
for raw_keyword in raw_keywords:
|
||||
try:
|
||||
keywords.append(Keyword.objects.get(slug=raw_keyword))
|
||||
except Keyword.DoesNotExist:
|
||||
pass
|
||||
objects = objects.filter(keywords__in=keywords)
|
||||
return objects
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['featured_posts'] = BlogPost.objects.filter(featured=True)
|
||||
logger.debug(f"loaded featured posts: {context['featured_posts']}")
|
||||
context['categories'] = Category.objects.all()
|
||||
context['keywords'] = Keyword.objects.all()
|
||||
|
||||
context["filters"] = {}
|
||||
if "category" in self.request.GET and len(
|
||||
self.request.GET["category"].strip()) > 0:
|
||||
category = self.request.GET["category"]
|
||||
try:
|
||||
category = Category.objects.get(slug=category)
|
||||
context["filters"]["category"] = category
|
||||
except Category.DoesNotExist:
|
||||
context["filters"]["category"] = None
|
||||
|
||||
if "search" in self.request.GET and len(
|
||||
self.request.GET["search"].strip()) > 0:
|
||||
search = self.request.GET["search"]
|
||||
context["filters"]["search"] = search
|
||||
|
||||
if "keywords" in self.request.GET and len(
|
||||
self.request.GET["keywords"].strip()) > 0:
|
||||
keywords = self.request.GET["keywords"]
|
||||
keywords = keywords.split('+')
|
||||
context["filters"]["keywords"] = keywords
|
||||
return context
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
self.object_list = self.get_queryset()
|
||||
allow_empty = self.get_allow_empty()
|
||||
|
||||
if not allow_empty:
|
||||
# When pagination is enabled and object_list is a queryset,
|
||||
# it's better to do a cheap query than to load the unpaginated
|
||||
# queryset in memory.
|
||||
if self.get_paginate_by(self.object_list) is not None and hasattr(
|
||||
self.object_list, 'exists'):
|
||||
is_empty = not self.object_list.exists()
|
||||
else:
|
||||
is_empty = not self.object_list
|
||||
else:
|
||||
is_empty = False
|
||||
context = self.get_context_data()
|
||||
|
||||
context["is_empty"] = is_empty
|
||||
|
||||
response = self.render_to_response(context)
|
||||
if is_empty:
|
||||
response.status_code = 404
|
||||
return response
|
||||
|
|
|
@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/3.2/ref/settings/
|
|||
"""
|
||||
|
||||
# for getting envvars
|
||||
import logging
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import os
|
||||
|
||||
# default django
|
||||
|
@ -21,7 +23,6 @@ from django.utils.log import ServerFormatter
|
|||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
|
@ -29,7 +30,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||
SECRET_KEY = 'django-insecure-z_t5-iawtas&1np9)01*4_z_&hy*7wgy1!o$3bnnniux3f1ds-'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
|
@ -50,6 +51,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'compressor', # compiles bs5
|
||||
'rosetta', # translation from admin interface
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -61,7 +63,6 @@ MIDDLEWARE = [
|
|||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'start.middleware.LangBasedOnUrlMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'gawa.urls'
|
||||
|
@ -100,7 +101,6 @@ DATABASES = {
|
|||
}
|
||||
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
|
@ -123,16 +123,16 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
LANGUAGES = [
|
||||
("de", _("German")),
|
||||
("en", _("English")),
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = 'de'
|
||||
LANGUAGE_CODE = 'en'
|
||||
# treat this ^^^ as the default
|
||||
prefix_default_language = False
|
||||
LOCALE_PATHS = ["/app/locale"]
|
||||
|
||||
TIME_ZONE = 'CET'
|
||||
|
||||
|
@ -158,7 +158,7 @@ STATICFILES_FINDERS = [
|
|||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'compressor.finders.CompressorFinder',
|
||||
]
|
||||
]
|
||||
|
||||
COMPRESS_PRECOMPILERS = (
|
||||
('text/x-scss', 'django_libsass.SassCompiler'),
|
||||
|
@ -173,7 +173,6 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|||
|
||||
# Logging configs
|
||||
|
||||
import logging
|
||||
|
||||
myServerFormatter = ServerFormatter
|
||||
myServerFormatter.default_time_format = "%Y-%M-%d %H:%M:%S"
|
||||
|
@ -290,11 +289,5 @@ LOGGING = {
|
|||
},
|
||||
}
|
||||
|
||||
|
||||
# Media stuff
|
||||
# this is where user uploaded files will go.
|
||||
# TODO change this for prod
|
||||
#MEDIA_ROOT = "/home/plex/Documents/code/python/gawa/media"
|
||||
MEDIA_ROOT = "/app/media"
|
||||
MEDIA_URL = "/media/"
|
||||
FILE_UPLOAD_TEMP_DIR = "/tmp/gawa/upload"
|
||||
|
|
|
@ -14,18 +14,25 @@ Including another URLconf
|
|||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin
|
||||
from django.http import HttpResponsePermanentRedirect
|
||||
from django.urls import include, re_path
|
||||
from django.urls import include, path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
re_path('i18n/', include('django.conf.urls.i18n')),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
urlpatterns += i18n_patterns(
|
||||
path("", include("start.urls")),
|
||||
path("blog/", include("blog.urls")),
|
||||
path('admin/', admin.site.urls, name="admin"),
|
||||
path("accounts/", include("django.contrib.auth.urls")),
|
||||
)
|
||||
|
||||
urlpatterns += i18n_patterns(
|
||||
path('translation/', include('rosetta.urls'), name="translation")
|
||||
)
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,490 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-08 20:49+0200\n"
|
||||
"PO-Revision-Date: 2023-10-08 21:03+0200\n"
|
||||
"Last-Translator: <software@cscherr.de>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Translated-Using: django-rosetta 0.9.9\n"
|
||||
|
||||
#: blog/admin.py:15 start/admin.py:8
|
||||
msgid "Regenerate traits"
|
||||
msgstr "Eigenschaften regenerieren"
|
||||
|
||||
#: blog/models.py:44
|
||||
msgid "Category"
|
||||
msgstr "Kategorie"
|
||||
|
||||
#: blog/models.py:45
|
||||
msgid "Categories"
|
||||
msgstr "Kategorien"
|
||||
|
||||
#: blog/models.py:282
|
||||
msgid "blog post"
|
||||
msgstr "Blog Post"
|
||||
|
||||
#: blog/models.py:283
|
||||
msgid "blog posts"
|
||||
msgstr "Blog Posts"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:9 blog/templates/blog/browse.html:9
|
||||
#: blog/templates/blog/index.html:8 start/templates/400.html:5
|
||||
#: start/templates/403.html:5 start/templates/404.html:5
|
||||
#: start/templates/500.html:5 start/templates/nav.html:9
|
||||
#: start/templates/start/index.html:8 start/templates/start/index.html:14
|
||||
#: start/templates/start/legalinfo.html:8 start/templates/start/links.html:5
|
||||
#: start/templates/start/search.html:5
|
||||
msgid "cscherr.de"
|
||||
msgstr "cscherr.de"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:9 blog/templates/blog/browse.html:9
|
||||
#: blog/templates/blog/index.html:8 blog/templates/blog/index.html:15
|
||||
#: start/templates/nav.html:51 start/templates/start/index.html:115
|
||||
msgid "Blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:86 blog/templates/blog/browse.html:170
|
||||
#: blog/templates/blog/featured.html:52
|
||||
msgid "published"
|
||||
msgstr "veröffentlicht"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:90
|
||||
msgid "updated"
|
||||
msgstr "aktualisiert"
|
||||
|
||||
#: blog/templates/blog/browse.html:47 start/forms.py:15
|
||||
#: start/templates/main_search_form.html:3
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: blog/templates/blog/browse.html:57
|
||||
msgid "select category"
|
||||
msgstr "ausgewählte Kategorie"
|
||||
|
||||
#: blog/templates/blog/browse.html:68
|
||||
msgid "Keywords"
|
||||
msgstr "Schlüsselwörter"
|
||||
|
||||
#: blog/templates/blog/browse.html:87
|
||||
msgid "Filter"
|
||||
msgstr "Filter"
|
||||
|
||||
#: blog/templates/blog/browse.html:119
|
||||
msgid "No posts found for your filters."
|
||||
msgstr "Keine Posts für diese Filter gefunden."
|
||||
|
||||
#: blog/templates/blog/browse.html:154 blog/templates/blog/featured.html:36
|
||||
msgid "category"
|
||||
msgstr "Kategorie"
|
||||
|
||||
#: blog/templates/blog/featured.html:5
|
||||
msgid "Featured"
|
||||
msgstr "Vorgestellt"
|
||||
|
||||
#: blog/templates/blog/index.html:14
|
||||
msgid "What can be found here?"
|
||||
msgstr "Was gibt es hier?"
|
||||
|
||||
#: blog/templates/blog/index.html:19
|
||||
msgid "Writeups"
|
||||
msgstr "Writeups"
|
||||
|
||||
#: blog/templates/blog/index.html:21
|
||||
msgid ""
|
||||
"\n"
|
||||
" Whenever I discover some interesting security thing, I will post a writeup here.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Falls ich etwas Interessantes zum Thema Sicherheit entdecke, wird es hier einen Writeup dazu geben."
|
||||
|
||||
#: blog/templates/blog/index.html:27
|
||||
msgid "Open Source"
|
||||
msgstr "OpenSource"
|
||||
|
||||
#: blog/templates/blog/index.html:28
|
||||
msgid ""
|
||||
"\n"
|
||||
" If something comes up, I may post Linux guides or my thoughts on current processes here.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Falls etwas aufkommt, poste ich hier womöglich Linux Guides oder meine Gedanken über allesmögliche."
|
||||
|
||||
#: blog/templates/blog/index.html:33 start/templates/start/index.html:44
|
||||
msgid "Selfhosting"
|
||||
msgstr "Selfhosting"
|
||||
|
||||
#: blog/templates/blog/index.html:34
|
||||
msgid ""
|
||||
"\n"
|
||||
" Selfhosting is something that I'm really fond of. There will be guides and thoughts about that too\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Selfhosting liegt mir sehr am Herzen, auch dazu wird es Anleitungen und Gedanken geben."
|
||||
|
||||
#: blog/templates/blog/index.html:39
|
||||
msgid "Anything Really"
|
||||
msgstr "Irgendwas"
|
||||
|
||||
#: blog/templates/blog/index.html:40
|
||||
msgid ""
|
||||
"\n"
|
||||
" This is my personal Blog after all, I will put here whatever I want and you can't stop me.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Das ist mein persönlicher Blog. Ich lade hier hoch was auch immer ich möchte."
|
||||
|
||||
#: blog/templates/blog/index.html:46
|
||||
msgid "Looking for anything specific?"
|
||||
msgstr "Suchst du nach etwas speziellem?"
|
||||
|
||||
#: blog/templates/blog/index.html:51
|
||||
msgid "Browse posts"
|
||||
msgstr "Posts durchsuchen"
|
||||
|
||||
#: gawa/settings.py:128
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
#: gawa/settings.py:129
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
||||
|
||||
#: start/models.py:27
|
||||
msgid "Keyword"
|
||||
msgstr "Schlüsselwort"
|
||||
|
||||
#: start/models.py:28
|
||||
msgid "keywords"
|
||||
msgstr "Schlüsselwörter"
|
||||
|
||||
#: start/models.py:72
|
||||
msgid "Searchable"
|
||||
msgstr "suchbares Objekt"
|
||||
|
||||
#: start/models.py:73
|
||||
msgid "Searchables"
|
||||
msgstr "suchbare Objekte"
|
||||
|
||||
#: start/models.py:95
|
||||
msgid "static site"
|
||||
msgstr "statische Seite"
|
||||
|
||||
#: start/models.py:96
|
||||
msgid "static sites"
|
||||
msgstr "statische Seiten"
|
||||
|
||||
#: start/models.py:165
|
||||
msgid "Link"
|
||||
msgstr "Link"
|
||||
|
||||
#: start/models.py:166 start/templates/nav.html:36
|
||||
#: start/templates/start/legalinfo.html:8 start/templates/start/links.html:5
|
||||
msgid "Links"
|
||||
msgstr "Links"
|
||||
|
||||
#: start/templates/400.html:5 start/templates/403.html:5
|
||||
#: start/templates/404.html:5 start/templates/500.html:5
|
||||
#: start/templates/start/index.html:8 start/templates/start/search.html:5
|
||||
msgid "startpage"
|
||||
msgstr "Startseite"
|
||||
|
||||
#: start/templates/400.html:8
|
||||
msgid "Bad request"
|
||||
msgstr "Fehlerhafte Anfrage"
|
||||
|
||||
#: start/templates/400.html:9
|
||||
msgid "You sent bad data to the server."
|
||||
msgstr "Du hast eine fehlerhafte Anfrage an den Server gesendet."
|
||||
|
||||
#: start/templates/403.html:8
|
||||
msgid "Permission denied"
|
||||
msgstr "Zugriff verweigert"
|
||||
|
||||
#: start/templates/403.html:9
|
||||
msgid "You are not allowed to access this page."
|
||||
msgstr "Du hast nicht die Berechtigung auf diese Seite zuzugreifen."
|
||||
|
||||
#: start/templates/404.html:8
|
||||
msgid "Not found"
|
||||
msgstr "Nicht gefunden"
|
||||
|
||||
#: start/templates/404.html:9
|
||||
msgid "The resource you are looking for does not exist."
|
||||
msgstr "Die Ressource die du suchst existiert nicht."
|
||||
|
||||
#: start/templates/500.html:8
|
||||
msgid "Internal Server Error"
|
||||
msgstr "Interner Server Fehler"
|
||||
|
||||
#: start/templates/500.html:9
|
||||
msgid "Something went wrong."
|
||||
msgstr "Etwas ist schief gelaufen."
|
||||
|
||||
#: start/templates/base.html:73
|
||||
msgid ""
|
||||
"\n"
|
||||
" This website is developed by myself and is\n"
|
||||
" <a href=\"https://git.cscherr.de/PlexSheep/gawa\">OpenSource</a>.\n"
|
||||
" <br><br>\n"
|
||||
" I encourage you to search for and report usuability and security issues\n"
|
||||
" on this site, aswell as server. For more information, see <a href=\"\">Reporting</a>.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Diese Website wird von mir selbst entwickelt und ist\n"
|
||||
"<a href=\"https://git.cscherr.de/PlexSheep/gawa\">OpenSource</a>.\n"
|
||||
"<br><br>\n"
|
||||
"Ich ermutige meine Besucher nach Sicherheits- und Nutzbarkeitsproblemen auf dieser Seite und diesem Server zu suchen und melden. Für weitere Informationen, siehe <a href=\"\">Reporting</a>. "
|
||||
|
||||
#: start/templates/base.html:87 start/templates/nav.html:42
|
||||
#: start/templates/start/legalinfo.html:12
|
||||
msgid "Legal Info"
|
||||
msgstr "rechtliche Informationen"
|
||||
|
||||
#: start/templates/base.html:96
|
||||
msgid "Quellcode dieser Website"
|
||||
msgstr "Quellcode dieser Website"
|
||||
|
||||
#: start/templates/base.html:100
|
||||
msgid "Contact"
|
||||
msgstr "Kontakt"
|
||||
|
||||
#: start/templates/base.html:108
|
||||
msgid "Deutschland"
|
||||
msgstr "Deutschland"
|
||||
|
||||
#: start/templates/main_search_form.html:4
|
||||
msgid "Go"
|
||||
msgstr "Los"
|
||||
|
||||
#: start/templates/nav.html:27 start/templates/nav.html:30
|
||||
#: start/templates/nav.html:54
|
||||
msgid "Start"
|
||||
msgstr "Start"
|
||||
|
||||
#: start/templates/nav.html:33 start/templates/start/index.html:25
|
||||
msgid "Professional"
|
||||
msgstr "Professionell"
|
||||
|
||||
#: start/templates/nav.html:57
|
||||
msgid "Browse"
|
||||
msgstr "Durchsuchen"
|
||||
|
||||
#: start/templates/nav.html:99
|
||||
msgid "Anonym"
|
||||
msgstr "Anonymous"
|
||||
|
||||
#: start/templates/nav.html:113
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
||||
#: start/templates/nav.html:117
|
||||
msgid "Login"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: start/templates/start/index.html:15
|
||||
msgid "Personal Website"
|
||||
msgstr "Persönliche Website"
|
||||
|
||||
#: start/templates/start/index.html:24 start/templates/start/index.html:78
|
||||
msgid "Who am I?"
|
||||
msgstr "Wer bin Ich?"
|
||||
|
||||
#: start/templates/start/index.html:29
|
||||
msgid ""
|
||||
"\n"
|
||||
" I am <em>Christoph J. Scherr</em>, studying Cybersecurity at <a href=\"https://mannheim.dhbw.de\">\n"
|
||||
" <abbr title=\"Duale Hochschule Baden-Württemberg\">DHBW</abbr>Mannheim, Germany</a> and passionate about computers.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Ich bin <em>Christoph J. Scherr</em>, studiere Cybersecurity an der <a href=\"https://mannheim.dhbw.de\">\n"
|
||||
"<abbr title=\"Duale Hochschule Baden-Württemberg\">DHBW</abbr>Mannheim, Deutschland</a> und habe eine Leidenschaft für Computer."
|
||||
|
||||
#: start/templates/start/index.html:35
|
||||
msgid ""
|
||||
"\n"
|
||||
" <h4>Work</h4>\n"
|
||||
" My form of study means that I often change between studying and working with my partner\n"
|
||||
" company <a href=\"https://newtec.de\">NewTec</a>, which is a medium sized company with headquaters\n"
|
||||
" in Pfaffenhofen, Bavaria, Germany. I work mainly on programming on a <abbr title=\"System On a Chip\">\n"
|
||||
" SOC</abbr> in <a href=\"https://rust-lang.org\">Rust</a> for industrial contexts.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"<h4>Arbeit</h4>\n"
|
||||
"Als dualer Student wechsle ich alle paar Monate zwischen theoretischen Vorlesungen und der Arbeit in meinem Partnerbetrieb, der <a href=\"https://newtec.de\">NewTec</a>, einem mittelständischem Unternehmen\n"
|
||||
"aus Pfaffenhofen, Bayern, Deutschland. Ich arbeite viel an der Programmierung eines <abbr title=\"System On a Chip\">\n"
|
||||
"SOC</abbr>s mit <a href=\"https://rust-lang.org\">Rust</a> für die industrielle Anwendung."
|
||||
|
||||
#: start/templates/start/index.html:45
|
||||
msgid ""
|
||||
"\n"
|
||||
" I have a fascination for Computers. As a hobby, I run my own computer networks (like the one this website\n"
|
||||
" is served from) running various services, most of which would probably be considered overkill by most.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Ich habe eine Faszination für Computer. Als Hobby betreibe ich mein eigenes Computernetzwerk (von dem unter anderem diese Website zur Verfügung gestellt wird). Die meisten meiner Dienste würden wohl von den Meisten als übertrieben angesehen werden."
|
||||
|
||||
#: start/templates/start/index.html:51
|
||||
msgid "Hacking"
|
||||
msgstr "Hacking"
|
||||
|
||||
#: start/templates/start/index.html:52
|
||||
msgid ""
|
||||
"\n"
|
||||
" It's not a coincidence that I study Cybersecurity. While I like most fields of computer science,\n"
|
||||
" computer security is the field that is most interesting to me. It seems to have the highest impact\n"
|
||||
" on the real world (everyone probably knows of some company that got hacked and leaked a ton of user data\n"
|
||||
" because their security was bad) and is a giant, complex puzzle.\n"
|
||||
" <br><br>\n"
|
||||
" <abbr title=\"Capture the Flag\">CTF</abbr>s make the very serious field of Security into a playing field,\n"
|
||||
" but one that can actually help discover real vulnerabilities and educate the players. I haven't done that\n"
|
||||
" much, but it's a fun way to learn and discover new things.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Es ist kein Zufall, dass ich Cybersecurity studiere. Zwar mag ich auch die meisten anderen Felder der Informatik, aber die Sicherheit bleibt mein Favorit. Mein Eindruck ist, dass sie die stärksten Effekte auf die echte Welt hat (Vermutlich kann jeder einen Vorfall nennen, bei dem eine große Firma gehackt wurde und viele Nutzerdaten veröffentlicht wurden.). Sicherheit ist ein großen, komplexes Rätsel.\n"
|
||||
"<br><br>\n"
|
||||
"<abbr title=\"Capture the Flag\">CTF</abbr>s können Sicherheit zu einem Spiel machen, bei dem dennoch echte Probleme entdeckt und verstanden werden können."
|
||||
|
||||
#: start/templates/start/index.html:66
|
||||
msgid "further information"
|
||||
msgstr "zusätzliche Informationen"
|
||||
|
||||
#: start/templates/start/index.html:79
|
||||
msgid "Private"
|
||||
msgstr "Privat"
|
||||
|
||||
#: start/templates/start/index.html:83
|
||||
msgid ""
|
||||
"\n"
|
||||
" There is nothing to say that does not require a headline.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Es gibt nicht zu sagen, das keine eigene Überschrift benötigen würde."
|
||||
|
||||
#: start/templates/start/index.html:88
|
||||
msgid "Music"
|
||||
msgstr "Musik"
|
||||
|
||||
#: start/templates/start/index.html:89
|
||||
msgid ""
|
||||
"\n"
|
||||
" Music is amazing. I don't find a lot of time lately, but sometimes I feel like making music. Usually,\n"
|
||||
" I prefer calm music. Some types of music I like are:\n"
|
||||
" <ul class=\"list-group pt-3 list-group-flush w-75 mx-auto\">\n"
|
||||
" <li class=\"list-group-item\">LoFi</li>\n"
|
||||
" <li class=\"list-group-item\">JPop, specifically <a href=\"https://www.youtube.com/@Ado1024\">Ado</a></li>\n"
|
||||
" <li class=\"list-group-item\">Eurobeat, specifically <a href=\"https://www.youtube.com/@TurboA\">Turbo</a></li>\n"
|
||||
" <li class=\"list-group-item\">Classical Music, for example <a href=\"https://www.youtube.com/watch?v=ZPdk5GaIDjo\">\n"
|
||||
" Vivaldis Vier Jahreszeiten</a></li>\n"
|
||||
" </ul>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Musik ist großartig. Ich habe in letzter Zeit nicht viel Zeit dazu gefunden, aber gelegentlich produziere ich auch meine eigene Musik.\n"
|
||||
"Normalerweise mag ich ruhige Musik, hier eine kurze Liste von Musik die ich mag:\n"
|
||||
"<ul class=\"list-group pt-3 list-group-flush w-75 mx-auto\">\n"
|
||||
"<li class=\"list-group-item\">LoFi</li>\n"
|
||||
"<li class=\"list-group-item\">JPop, specifically <a href=\"https://www.youtube.com/@Ado1024\">Ado</a></li>\n"
|
||||
"<li class=\"list-group-item\">Eurobeat, specifically <a href=\"https://www.youtube.com/@TurboA\">Turbo</a></li>\n"
|
||||
"<li class=\"list-group-item\">Classical Music, for example <a href=\"https://www.youtube.com/watch?v=ZPdk5GaIDjo\">\n"
|
||||
"Vivaldis Vier Jahreszeiten</a></li>\n"
|
||||
"</ul>"
|
||||
|
||||
#: start/templates/start/index.html:102
|
||||
msgid "Lanugages"
|
||||
msgstr "Sprachen"
|
||||
|
||||
#: start/templates/start/index.html:103
|
||||
msgid ""
|
||||
"\n"
|
||||
" Somehow, I find myself fascinated by the japanese lanugage.\n"
|
||||
" <br><nobr>日本語が好き。</nobr>However,\n"
|
||||
" I'm still at the beginning of the journey.\n"
|
||||
" <br><br>\n"
|
||||
" I also speak and understand english, but it's more of a \"utility language\" for me. Not to\n"
|
||||
" offend anyone, but I don't think english is a \"beautiful\" language, as german and japanese are\n"
|
||||
" (in my personal opinion). The english translation of this website was also made by myself.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Die japanische Sprache fasziniert mich.\n"
|
||||
"<br><nobr>日本語が好き。</nobr>Leider bin ich noch am Anfang dieser Reise.\n"
|
||||
"<br><br>\n"
|
||||
"Abgesehen davon spreche und verstehe ich Englisch, betrachte das aber eher als „Nutzsprache“. Englisch ist (meiner Meinung nach) keine schöne Sprache, im Gegensatz zu Deutsch und Japanisch. Die englische Übersetzung dieser Website habe ich selbst erstellt. "
|
||||
|
||||
#: start/templates/start/legalinfo.html:16
|
||||
msgid "Information according to §5 TMG"
|
||||
msgstr "Angaben gemäß §5 TMG"
|
||||
|
||||
#: start/templates/start/legalinfo.html:23
|
||||
msgid "Germany"
|
||||
msgstr "Deutschland"
|
||||
|
||||
#: start/templates/start/legalinfo.html:25
|
||||
msgid "Email "
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: start/templates/start/links.html:8
|
||||
msgid "Personal"
|
||||
msgstr "Persönlich"
|
||||
|
||||
#: start/templates/start/links.html:42 start/templates/start/links.html:86
|
||||
msgid "visit"
|
||||
msgstr "öffnen"
|
||||
|
||||
#: start/templates/start/links.html:52
|
||||
msgid "Others"
|
||||
msgstr "Andere"
|
||||
|
||||
#: start/templates/start/search.html:9
|
||||
msgid "Search for"
|
||||
msgstr "Suche nach"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " Somehow, I find myself fascinated by the japanese lanugage. <nobr>日本語が好きだ。</nobr>\n"
|
||||
#~ " "
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "Aus irgendeinem Grund bin ich von der japanischen Sprache fasziniert. <nobr>日本語が好きだ。</nobr>"
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " Ich habe diese Website selbst programmiert und gehostet.\n"
|
||||
#~ " Falls Sie einen Fehler belibiger Art finden, würde ich mich freuen,\n"
|
||||
#~ " wenn Sie mich darüber benachrichtigen.\n"
|
||||
#~ " <br><br>\n"
|
||||
#~ " Die Suche nach Schwachstellen und Fehlern auf dieser Website ist für\n"
|
||||
#~ " diesen Zweck ausdrücklich erlaubt.\n"
|
||||
#~ " "
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "Ich habe diese Website selbst programmiert und gehostet.\n"
|
||||
#~ "Falls Sie einen Fehler belibiger Art finden\n"
|
||||
#~ "würde ich mich freuen, wenn Sie mich darüber benachrichtigen.\n"
|
||||
#~ "<br><br>\n"
|
||||
#~ "Die Suche nach Schwachstellen und Fehlern auf\n"
|
||||
#~ "dieser Website ist für diesen Zweck ausdrücklich erlaubt.\n"
|
||||
#~ " "
|
Binary file not shown.
|
@ -0,0 +1,487 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-08 20:26+0200\n"
|
||||
"PO-Revision-Date: 2023-10-08 15:18+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.3.2\n"
|
||||
|
||||
#: blog/admin.py:15 start/admin.py:8
|
||||
msgid "Regenerate traits"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:44
|
||||
msgid "Category"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:45
|
||||
msgid "Categories"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:282
|
||||
msgid "blog post"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:283
|
||||
msgid "blog posts"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/blogpost.html:9 blog/templates/blog/browse.html:9
|
||||
#: blog/templates/blog/index.html:8 start/templates/400.html:5
|
||||
#: start/templates/403.html:5 start/templates/404.html:5
|
||||
#: start/templates/500.html:5 start/templates/nav.html:9
|
||||
#: start/templates/start/index.html:8 start/templates/start/index.html:14
|
||||
#: start/templates/start/legalinfo.html:8 start/templates/start/links.html:5
|
||||
#: start/templates/start/search.html:5
|
||||
msgid "cscherr.de"
|
||||
msgstr "cscherr.de"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:9 blog/templates/blog/browse.html:9
|
||||
#: blog/templates/blog/index.html:8 blog/templates/blog/index.html:15
|
||||
#: start/templates/nav.html:51 start/templates/start/index.html:115
|
||||
msgid "Blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/templates/blog/blogpost.html:86 blog/templates/blog/browse.html:170
|
||||
#: blog/templates/blog/featured.html:52
|
||||
msgid "published"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/blogpost.html:90
|
||||
msgid "updated"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:47 start/forms.py:15
|
||||
#: start/templates/main_search_form.html:3
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:57
|
||||
msgid "select category"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:68
|
||||
msgid "Keywords"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:87
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:119
|
||||
msgid "No posts found for your filters."
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/browse.html:154 blog/templates/blog/featured.html:36
|
||||
msgid "category"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/featured.html:5
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:14
|
||||
msgid "What can be found here?"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:19
|
||||
msgid "Writeups"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:21
|
||||
msgid ""
|
||||
"\n"
|
||||
" Whenever I discover some interesting security thing, I will post "
|
||||
"a writeup here.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:27
|
||||
msgid "Open Source"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:28
|
||||
msgid ""
|
||||
"\n"
|
||||
" If something comes up, I may post Linux guides or my thoughts on "
|
||||
"current processes here.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:33 start/templates/start/index.html:44
|
||||
msgid "Selfhosting"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:34
|
||||
msgid ""
|
||||
"\n"
|
||||
" Selfhosting is something that I'm really fond of. There will be "
|
||||
"guides and thoughts about that too\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:39
|
||||
msgid "Anything Really"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:40
|
||||
msgid ""
|
||||
"\n"
|
||||
" This is my personal Blog after all, I will put here whatever I "
|
||||
"want and you can't stop me.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:46
|
||||
msgid "Looking for anything specific?"
|
||||
msgstr ""
|
||||
|
||||
#: blog/templates/blog/index.html:51
|
||||
msgid "Browse posts"
|
||||
msgstr ""
|
||||
|
||||
#: gawa/settings.py:128
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: gawa/settings.py:129
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:27
|
||||
msgid "Keyword"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:28
|
||||
msgid "keywords"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:72
|
||||
msgid "Searchable"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:73
|
||||
msgid "Searchables"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:95
|
||||
msgid "static site"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:96
|
||||
msgid "static sites"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:165
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
#: start/models.py:166 start/templates/nav.html:36
|
||||
#: start/templates/start/legalinfo.html:8 start/templates/start/links.html:5
|
||||
msgid "Links"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/400.html:5 start/templates/403.html:5
|
||||
#: start/templates/404.html:5 start/templates/500.html:5
|
||||
#: start/templates/start/index.html:8 start/templates/start/search.html:5
|
||||
msgid "startpage"
|
||||
msgstr "startpage"
|
||||
|
||||
#: start/templates/400.html:8
|
||||
msgid "Bad request"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/400.html:9
|
||||
msgid "You sent bad data to the server."
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/403.html:8
|
||||
msgid "Permission denied"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/403.html:9
|
||||
msgid "You are not allowed to access this page."
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/404.html:8
|
||||
msgid "Not found"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/404.html:9
|
||||
msgid "The resource you are looking for does not exist."
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/500.html:8
|
||||
msgid "Internal Server Error"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/500.html:9
|
||||
msgid "Something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/base.html:73
|
||||
msgid ""
|
||||
"\n"
|
||||
" This website is developed by myself and is\n"
|
||||
" <a href=\"https://git.cscherr.de/PlexSheep/"
|
||||
"gawa\">OpenSource</a>.\n"
|
||||
" <br><br>\n"
|
||||
" I encourage you to search for and report "
|
||||
"usuability and security issues\n"
|
||||
" on this site, aswell as server. For more "
|
||||
"information, see <a href=\"\">Reporting</a>.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/base.html:87 start/templates/nav.html:42
|
||||
#: start/templates/start/legalinfo.html:12
|
||||
msgid "Legal Info"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/base.html:96
|
||||
msgid "Quellcode dieser Website"
|
||||
msgstr "Source code of this website"
|
||||
|
||||
#: start/templates/base.html:100
|
||||
msgid "Contact"
|
||||
msgstr "Contact"
|
||||
|
||||
#: start/templates/base.html:108
|
||||
msgid "Deutschland"
|
||||
msgstr "Germany"
|
||||
|
||||
#: start/templates/main_search_form.html:4
|
||||
msgid "Go"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/nav.html:27 start/templates/nav.html:30
|
||||
#: start/templates/nav.html:54
|
||||
msgid "Start"
|
||||
msgstr "Start"
|
||||
|
||||
#: start/templates/nav.html:33 start/templates/start/index.html:25
|
||||
msgid "Professional"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/nav.html:57
|
||||
msgid "Browse"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/nav.html:99
|
||||
msgid "Anonym"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/nav.html:113
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/nav.html:117
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:15
|
||||
msgid "Personal Website"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:24 start/templates/start/index.html:78
|
||||
msgid "Who am I?"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:29
|
||||
msgid ""
|
||||
"\n"
|
||||
" I am <em>Christoph J. Scherr</em>, studying "
|
||||
"Cybersecurity at <a href=\"https://mannheim.dhbw.de\">\n"
|
||||
" <abbr title=\"Duale Hochschule Baden-"
|
||||
"Württemberg\">DHBW</abbr>Mannheim, Germany</a> and passionate about "
|
||||
"computers.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:35
|
||||
msgid ""
|
||||
"\n"
|
||||
" <h4>Work</h4>\n"
|
||||
" My form of study means that I often change "
|
||||
"between studying and working with my partner\n"
|
||||
" company <a href=\"https://newtec."
|
||||
"de\">NewTec</a>, which is a medium sized company with headquaters\n"
|
||||
" in Pfaffenhofen, Bavaria, Germany. I work "
|
||||
"mainly on programming on a <abbr title=\"System On a Chip\">\n"
|
||||
" SOC</abbr> in <a href=\"https://rust-lang."
|
||||
"org\">Rust</a> for industrial contexts.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:45
|
||||
msgid ""
|
||||
"\n"
|
||||
" I have a fascination for Computers. As a "
|
||||
"hobby, I run my own computer networks (like the one this website\n"
|
||||
" is served from) running various services, "
|
||||
"most of which would probably be considered overkill by most.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:51
|
||||
msgid "Hacking"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:52
|
||||
msgid ""
|
||||
"\n"
|
||||
" It's not a coincidence that I study "
|
||||
"Cybersecurity. While I like most fields of computer science,\n"
|
||||
" computer security is the field that is most "
|
||||
"interesting to me. It seems to have the highest impact\n"
|
||||
" on the real world (everyone probably knows "
|
||||
"of some company that got hacked and leaked a ton of user data\n"
|
||||
" because their security was bad) and is a "
|
||||
"giant, complex puzzle.\n"
|
||||
" <br><br>\n"
|
||||
" <abbr title=\"Capture the Flag\">CTF</abbr>s "
|
||||
"make the very serious field of Security into a playing field,\n"
|
||||
" but one that can actually help discover real "
|
||||
"vulnerabilities and educate the players. I haven't done that\n"
|
||||
" much, but it's a fun way to learn and "
|
||||
"discover new things.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:66
|
||||
msgid "further information"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:79
|
||||
msgid "Private"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:83
|
||||
msgid ""
|
||||
"\n"
|
||||
" There is nothing to say that does not "
|
||||
"require a headline.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:88
|
||||
msgid "Music"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:89
|
||||
msgid ""
|
||||
"\n"
|
||||
" Music is amazing. I don't find a lot of time "
|
||||
"lately, but sometimes I feel like making music. Usually,\n"
|
||||
" I prefer calm music. Some types of music I "
|
||||
"like are:\n"
|
||||
" <ul class=\"list-group pt-3 list-group-flush "
|
||||
"w-75 mx-auto\">\n"
|
||||
" <li class=\"list-group-item\">LoFi</li>\n"
|
||||
" <li class=\"list-group-item\">JPop, "
|
||||
"specifically <a href=\"https://www.youtube.com/@Ado1024\">Ado</a></li>\n"
|
||||
" <li class=\"list-group-item\">Eurobeat, "
|
||||
"specifically <a href=\"https://www.youtube.com/@TurboA\">Turbo</a></li>\n"
|
||||
" <li class=\"list-group-item\">Classical "
|
||||
"Music, for example <a href=\"https://www.youtube.com/watch?v=ZPdk5GaIDjo\">\n"
|
||||
" Vivaldis Vier Jahreszeiten</a></"
|
||||
"li>\n"
|
||||
" </ul>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:102
|
||||
msgid "Lanugages"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/index.html:103
|
||||
msgid ""
|
||||
"\n"
|
||||
" Somehow, I find myself fascinated by the "
|
||||
"japanese lanugage.\n"
|
||||
" <br><nobr>日本語が好き。</nobr>However,\n"
|
||||
" I'm still at the beginning of the journey.\n"
|
||||
" <br><br>\n"
|
||||
" I also speak and understand english, but "
|
||||
"it's more of a \"utility language\" for me. Not to\n"
|
||||
" offend anyone, but I don't think english is "
|
||||
"a \"beautiful\" language, as german and japanese are\n"
|
||||
" (in my personal opinion). The english "
|
||||
"translation of this website was also made by myself.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/legalinfo.html:16
|
||||
msgid "Information according to §5 TMG"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/legalinfo.html:23
|
||||
msgid "Germany"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/legalinfo.html:25
|
||||
msgid "Email "
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/links.html:8
|
||||
msgid "Personal"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/links.html:42 start/templates/start/links.html:86
|
||||
msgid "visit"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/links.html:52
|
||||
msgid "Others"
|
||||
msgstr ""
|
||||
|
||||
#: start/templates/start/search.html:9
|
||||
msgid "Search for"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid ""
|
||||
#~| "\n"
|
||||
#~| " Ich habe diese Website selbst programmiert "
|
||||
#~| "und gehostet. \n"
|
||||
#~| " Falls Sie einen Fehler belibiger Art finden, "
|
||||
#~| "würde ich mich freuen, \n"
|
||||
#~| " wenn Sie mich darüber benachrichtigen.\n"
|
||||
#~| " <br><br>\n"
|
||||
#~| " Die Suche nach Schwachstellen und Fehlern "
|
||||
#~| "auf dieser Website ist für \n"
|
||||
#~| " diesen Zweck ausdrücklich erlaubt.\n"
|
||||
#~| " "
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " Ich habe diese Website selbst programmiert "
|
||||
#~ "und gehostet.\n"
|
||||
#~ " Falls Sie einen Fehler belibiger Art finden, "
|
||||
#~ "würde ich mich freuen,\n"
|
||||
#~ " wenn Sie mich darüber benachrichtigen.\n"
|
||||
#~ " <br><br>\n"
|
||||
#~ " Die Suche nach Schwachstellen und Fehlern auf "
|
||||
#~ "dieser Website ist für\n"
|
||||
#~ " diesen Zweck ausdrücklich erlaubt.\n"
|
||||
#~ " "
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "I have programmed and hosted this Website by myself. If you find any "
|
||||
#~ "issues or bugs,\n"
|
||||
#~ "please contact me about it.\n"
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "I specifically allow the search for any issues this website might have. "
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="557.15094"
|
||||
height="210.8916"
|
||||
viewBox="0 0 557.15094 210.8916"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="404.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<rect
|
||||
x="292.2478"
|
||||
y="154.0531"
|
||||
width="462.1593"
|
||||
height="240.14159"
|
||||
id="rect1" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-239.50485,-280.19422)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
id="text1"
|
||||
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.5524px;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect1);fill:#3b3b3b;fill-opacity:1"
|
||||
transform="matrix(13.034066,0,0,13.034066,-3571.9156,-1800.7033)"><tspan
|
||||
x="292.24805"
|
||||
y="175.53608"
|
||||
id="tspan2">404</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue