Compare commits

...

12 Commits

Author SHA1 Message Date
27968f6ae5 feat: add small update on notification function
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-13 00:05:31 +02:00
2388e78c5f feat: Add thoughts on html mails post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-07-12 12:48:20 +02:00
3fb5a5d4fc fix: typo
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2025-06-28 15:03:51 +02:00
dc6e60970a fix: metadata
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2025-06-28 15:02:52 +02:00
a6df0a82ae fix: metadata 2025-06-28 15:02:07 +02:00
90e8d15af7 feat: Add post improve-osm-by-using-it
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2025-06-28 14:56:40 +02:00
2641955a36 feat: Rework the bio a bit 2025-06-28 14:52:52 +02:00
4e4d825283 refactor: delete old stuff 2025-06-28 07:36:29 +02:00
fb31dedf4d feat: Add translation note 2025-03-04 18:01:00 +01:00
cc9b0733dc fix: Restrict height, fix syntax 2025-03-04 17:59:52 +01:00
b90ecbadc4 feat: update bio
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-03-04 17:28:52 +01:00
698d648263 feat: add fiat lux 2025-03-04 17:26:04 +01:00
16 changed files with 354 additions and 390 deletions

View File

@@ -33,7 +33,7 @@ paginate = 5 #frontpage pagination
author = "Julian-Samuel Gebühr"
authorLink = "https://hyteck.de/"
bio = [
"Student of Medical Informatics, Developer, He/Him"
"Business Analyst for work, Developer for fun. Activist because it's necessary. He/Him"
]
copyright = [
'&copy; 2025 CC-BY Julian-Samuel Gebühr</a> '

View File

@@ -0,0 +1,131 @@
---
title: "Thoughts on HTML mails"
date: 2025-07-12T12:05:10+02:00
lastmod: 2025-07-13T:00:04+02:00
draft: false
image: "uploads/html-e-mails.png"
categories: ['English']
tags: ['email', 'html', 'plaintext', 'django', 'notfellchen']
---
Lately I worked on notification e-mails for [notfellchen.org](https://notfellchen.org). Initially I just sent text
notifications without links to the site. Terrible idea! An E-Mail notification I send always has Call-to-Action or at
minimum a link to more information.
I left the system like this for half a year because it kinda worked for me (didn't suck enough for me to care), and I was the main receiver of these notifications.
However, as the platform is developed further and more users join I need to think about more user-centric notifications.
So what do I imagine is important to a user?
*
* **Information benefit**: An e-mail has the purpose to inform a user. This information should be immediately visible & understandable.
* **Actionables**: Users should be able to act on the information received. This is the bright red button "DO SOMETHING NOW!" you see so often.
* **Unsubscribing**: Informing e-mails stop is not only a legal requirement and morally the right thing to do but it also gives users agency and - I hope - increases the User Experience
With these I naturally came to the next question: Plaintext or HTML?
Some people would say [Plaintext is inherently better](https://useplaintext.email/) than HTML e-mails. Many of these reasons resonate with me including:
* Privacy invasion and tracking
* HTML emails are less accessible
* Some clients can't display HTML emails at all
* Mail client vulnerabilities
These are all valid points and are a reason I generally enjoy plaintext e-mails when I receive them.
But this is not about me but users. And there are some real benefits of HTML e-mails:
* Visually appealing: This is subjective but generally most users seem to agree on that
* User guidance: Rich text provides a real benefit when searching for the relevant information
Be honest: Do you read automated e-mails you receive completely? Or do you just skim for important information?
And here HTML-mails shine: **Information can easily be highlighted** and big button can lead the user to do the right action.
Some might argue that you can also a highlight a link in plaintext but that nearly always will worsen accessibility for screen-reader user.
# The result
In the end, I decided that providing plaintext-only e-mails was not enough. I set up html mails, mostly using
[djangos send_mail](https://docs.djangoproject.com/en/5.2/topics/email/#send-mail) function where I can pass the html message and attattching it correctly is done for me.
![A screenshot of an e-mail in thunderbird. The e-mail is structured in header, body and footer. The header says "Notfellchen.org", the body shows a message that a new user was registered and a bright green button to show the user. The footer offers a link to unsubscribe](mail_screenshot.png)
For anyone that is interested, here is how most my notifications are sent
```python
def send_notification_email(notification_pk):
notification = Notification.objects.get(pk=notification_pk)
subject = f"{notification.title}"
context = {"notification": notification, }
if notification.notification_type == NotificationTypeChoices.NEW_REPORT_COMMENT or notification.notification_type == NotificationTypeChoices.NEW_REPORT_AN:
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
plain_message = render_to_string('fellchensammlung/mail/notifications/report.txt', context)
[...]
elif notification.notification_type == NotificationTypeChoices.NEW_COMMENT:
html_message = render_to_string('fellchensammlung/mail/notifications/new-comment.html', context)
plain_message = render_to_string('fellchensammlung/mail/notifications/new-comment.txt', context)
else:
raise NotImplementedError("Unknown notification type")
if "plain_message" not in locals():
plain_message = strip_tags(html_message)
mail.send_mail(subject, plain_message, settings.DEFAULT_FROM_EMAIL,
[notification.user_to_notify.email],
html_message=html_message)
```
Yes this could be made more efficient - for now it works. I made the notification framework too complicated initially, so I'm still tyring out what works and what doesn't.
Here is the html template
```html
{% extends "fellchensammlung/mail/base.html" %}
{% load i18n %}
{% block title %}
{% translate 'Neuer User' %}
{% endblock %}
{% block content %}
<p>Moin,</p>
<p>
es wurde ein neuer Useraccount erstellt.
</p>
<p>
Details findest du hier
</p>
<p>
<a href="{{ notification.user_related.get_full_url }}" class="cta-button">{% translate 'User anzeigen' %}</a>
</p>
{% endblock %}
```
and here the plaintext
```
{% extends "fellchensammlung/mail/base.txt" %}
{% load i18n %}
{% block content %}{% blocktranslate %}Moin,
es wurde ein neuer Useraccount erstellt.
User anzeigen: {{ new_user_url }}
{% endblocktranslate %}{% endblock %}
```
Works pretty well for now. People that prefer plaintext will get these and most users will have skimmable html e-mail where the
styling will help them recognize where it's from and what to do. Accessibility-wise this seems like the best option.
And while adding a new notification will force me to create
* a new notification type,
* two new e-mail templates and
* a proper rendering on the website
this seems okay. Notifications are useful, but I don't want to shove them everywhere. I'm not running facebook or linkedin after all.
So for now I'm pretty happy with the new shiny e-mails and will roll out the changes soon (if I don't find any more wired bugs).
PS: I wrote this post after reading [blog & website in the age of containerized socials](https://blog.avas.space/blog-website-eval/) by ava.
Maybe this "Thoughts on" format will stay and I will post these in addition to more structured deep dives.
# Update
I did a rework of the notification function and it's now much cleaner now. However, it's less readable so this blogpost will stay as-is.
If you want to check out the new code have a look [on Codeberg](https://codeberg.org/moanos/notfellchen/src/commit/a4b8486bd489dacf8867b49d04f70f091556dc9d/src/fellchensammlung/mail.py).

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

View File

@@ -0,0 +1,100 @@
---
title: "Improve OpenStreetMap data by using it"
date: 2025-06-28T14:05:10+02:00
draft: false
image: "post/improve-osm-by-using-it/improve-osm-by-using-it.png"
categories: ['English']
tags: ['django', 'OpenStreetMap', 'notfellchen', 'osm', 'open data', 'geojson']
---
## Introduction
In the last month I improved the mapping of about 100 german animal shelters - not only out of the goodness of my heart, but because it helped me.
Let me explain why: I develop [notfellchen.org](https://notfellchen.org/), where users can search animals in animal shelters, specifically rats, they might want to adopt.
The idea is to have a central website that allows you to search for rats in your area.
This is necessary because only a small percentage of animal shelters has rats. As a user, just checking your next
shelter doesn't work. Some users will stop after checking the second or third one and just buy from a pet shop (which is a very, very bad idea).
Now a central platform for is nice for users but has one problem: How do I, as operator of notfellchen, know where rats are?
I need to **manually check every animal shelter in the country** and if they have rats, ask them for permission to use
images of the rats on my site.
So wait I need to have is a list of animal shelters in germany and have their website, e-mail and phone number.
The source for all of this: You guessed it - OpenStreetMap 🥳
# Getting the data
Downloading all german animal shelters is surprisingly easy: You use [Overpass Turbo](https://overpass-turbo.eu/) and get a `.geojson` to download.
here is the query I used:
```
[out:json][timeout:25];
// fetch area “Germany” to search in
{{geocodeArea:Germany}}->.searchArea;
// Check search area for all objects with animal shelter tag
nwr["amenity"="animal_shelter"](area.searchArea);
// print results
out geom;
```
Now upload it to notfellchen.org and I'll be fine right?
# Data Issues
Yeah well, this only *mostly* works. There were two main problems:
**Missing contact data** is annoying because I quickly want to check the website of animal shelters.
More annoying were what I'd call **mapping errors**.
Most commonly an animal shelter had multiple nodes/ways tagged as `amenity:animal_shelter`.
The highlight was the "Tierheim München" where about 10 buildings were tagged as `amenity:animal_shelter` and the contact
data was sitting on the building with name "Katzenhaus" ("cat house").
Now the "Tierheim München" appeared in my list 10 times but 9 of them had no contact data at all.
# Correcting it
I could have corrected this only in the notfellchen database. It would have been faster and I could even automate parts of it.
But I didn't.
For each issue I found, I opened OpenStreetMap and added websites, phone numbers or even re-mapped the area.
For "Tierheim München" I even [opened a thread in the forum](https://community.openstreetmap.org/t/mapping-of-multiple-related-buildings-animal-shelters/131801)
to discuss a proper tagging.
That makes sense for me because I get one important thing:
# What I get out of it: Updates
What if a new shelter was added later or a shelter changed? I already profit a lot from the time people spend adding information, so why stop?
My database stores the OSM ID, so I can regularly query the data again to get updates.
But that only works if I take an "upstream" approach: Fix the data in OSM, then load it into notfellchen.
Otherwise, any change in my database will be overwritten by "old" OSM data.
# Result
In the last month, I made 86 changes to OSM adding the following information
| Type of information | Number of times added |
|---------------------|-----------------------|
| Website | 66 |
| Phone Numbers | 65 |
| Operator | 63 |
| E-Mail | 49 |
| Fax | 9 |
Yes I sometimes even added fax numbers. It was easy enough to add and maybe there is someone might use it.
# Looking forward
I'm of course not done. Only half of the rescues known to OSM in germany are currently checked, so I'll continue that work.
After that I'll start adding the shelters that are just in my database.
Currently, 33 animal shelters are known to notfellchen that are not known to OSM. This number will likely grow, maybe double.
A lot to do. And luckily, this work both benefits me and everyone using OSM. Happy mapping!

View File

@@ -26,7 +26,7 @@ Wenn ich nach Sci-Fi und Fantasy suche, will ich weg von profit-optimierten List
und hin zu echten Empfehlungen. Viele solche Empfehlungen bekomme ich im lokalen Buchladen. Gerade junge Buchhändler\*innen können oft super Empfehlungen geben.
Leider sind diese wenigen Mitarbeiter\*innen selten und in Buchläden sind SciFi und Fantasy oft nur wenig vertreten und die Regale viel zu oft voll mit Büchern weißer Männer.
Deshalb jetzt zu den Empfehlungen die Versuchen, das anders zu machen!
Deshalb jetzt zu den Empfehlungen die versuchen, das anders zu machen!
*Die Liste beschreibt die Bücher nur kurz und sollte ohne Spoiler auskommen.
In allen Büchern kommen queere Charaktere vor. Alle Links zum führen zu einem lokaln Buchladen oder Websites der Autor\*innen oder des Verlags.*
@@ -48,11 +48,13 @@ Ace in Space ist eine tolle Erzählung von einer Raumjäger-Pilotin, einer Grupp
Angesiedelt ist das Buch eher im Cyber-Punk. Es geht gegen Großkonzerne, es geht um schnelle Flieger und Bars in engen Quartieren.
Außerdem hat es eine der besten Sexszenen, die ich je lesen durfte.
Judith schreibt oft dystopischere Geschichten als ich normalerweise lese. Gleichzeitig sind Laylayland und Wasteland zwei fantastische Bücher die ich nicht missen will.
Immer feministisch, queer und tollerweise auch mit Haupt(Charaktere) mit Behinderungen. Progressive Phanastik der höchsten Klassen!
Judith schreibt oft dystopischere Geschichten als ich normalerweise lese. Aber Laylayland und Wasteland zwei fantastische Bücher die ich nicht missen will.
Die Bücher sind feministisch, queer und tollerweise auch mit Haupt(Charaktere) mit Behinderungen. Progressive Phanastik der höchsten Klassen!
[Verlagsshop](https://amrun-verlag.de/produkt/aceinspace1/)
Wer eine ganze Kurzgeschichte "der Vögte" (also Judith und Christian Vogt) als Leseprobe haben will, findet diese am Ende des Artikels als PDF.
### T.J. Klune: Mr. Parnassus' Heim für magisch Begabte
Wunderschöne Geschichte über einen Beamten der aus der Stadt rauskommt und ein Haus an der See.
@@ -72,7 +74,7 @@ Ich habe die Novelle am Stück verschlungen und mit der Hauptperson gelacht und
In einer Fantasy Welt brennt eine Leibwächterin der Königin mit einer Magierin durch und sie eröffnen einen Teeladen.
Es gibt auch den Nachfolger "A Pirate's Life for Tea", den hab ich aber noch nicht gelesen
[Link zum Buch beim Frauenbuchladen Thalestris](https://frauenbuchladen.buchkatalog.de/cant-spell-treason-without-tea-9783492706896)
[Link zum Buch beim Frauenbuchladen Thalestris (Deutsche Übersetzung)](https://frauenbuchladen.buchkatalog.de/cant-spell-treason-without-tea-9783492706896)
### Travis Baldree: "Legends&Latte"
@@ -96,4 +98,10 @@ Besonders empfehlen kann ich die Geschichte "Uferlos" von Lena Richter die eine
Ich hoffe die Empfehlungen machen, trotz ihrer Kürze, Lust aufs Lesen! Kauft bei eurem lokalen Buchladen und unterstützt kleine Verlage!
*Falls das nicht sowieso schon klar war: Ich bekomme für diese Empfehlungen kein Geld, die Links haben kein Tracking, keine Referral Codes oder sonst etwas.*
*Falls das nicht sowieso schon klar war: Ich bekomme für diese Empfehlungen kein Geld, die Links haben kein Tracking, keine Referral Codes oder sonst etwas.*
### Kurzgeschichte FiatLux
Die Kurzgeschichte ist von Judith und Christian Vogt und steht unter der Lizenz [CC-BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/), darf also mit Namensnennung, nichtkommerziell und unter gleichen Bedingungen geteilt werden (wie schön ist das denn bitte?!).
{{< pdf FiatLuxVogt>}}

View File

@@ -0,0 +1,107 @@
---
title: "Musikempfehlungen- Beat gegen KI"
date: 2025-03-03T18:05:10+02:00
draft: true
image: "uploads/scifi-fantasy.png"
categrories: ['Deutsch']
tags: ['ki', 'science-fiction', 'schriftgelehrte', 'progressive Fantastik', 'scifi', 'fantasy', 'solarpunk']
---
## Einführung
KI ist überall. Große Teile des Internets werden gerade durch KI-generierten Inhalten überschwemmt.
Teilweise zeigen KI-generierte Artikel auch gefälschte Daten (vor 2022) an um so den Anschein zu erwecken nicht KI-generiert zu sein.
Verlässliche und authentische Informationen zu finden ist daher um so schwerer.
Deshalb beginnt **das Zeitalter der Schriftgelehrten**, denn Informationen zu sammeln und aufzubereiten, das ist, was sie tun. Schriftgelehrte\*r verwende ich hier als Übersetzung des englischen Wortes "Librarian".
Und so maße auch ich mir an mich (nur) dafür Schriftgelehrte\*r zu nennen und versuche auf diesem
Blog immer wieder Informationen zu sammeln und zu teilen (ironischerweise in dem Wissen, dass dieser Blog von
KI-Unternehmen gescrapt wird).
Starten möchte ich mit Folgendem:
## Sci-Fi und Fantasy
Wenn ich nach Sci-Fi und Fantasy suche, will ich weg von profit-optimierten Listen von Online-Buchhändler\*innen,
und hin zu echten Empfehlungen. Viele solche Empfehlungen bekomme ich im lokalen Buchladen. Gerade junge Buchhändler\*innen können oft super Empfehlungen geben.
Leider sind diese wenigen Mitarbeiter\*innen selten und in Buchläden sind SciFi und Fantasy oft nur wenig vertreten und die Regale viel zu oft voll mit Büchern weißer Männer.
Deshalb jetzt zu den Empfehlungen die versuchen, das anders zu machen!
*Die Liste beschreibt die Bücher nur kurz und sollte ohne Spoiler auskommen.
In allen Büchern kommen queere Charaktere vor. Alle Links zum führen zu einem lokaln Buchladen oder Websites der Autor\*innen oder des Verlags.*
### Becky Chambers: "Der lange Weg zu einem kleinen zorningen Planeten"
Dieses Buch ist eine wirklich wunderschön geschriebene Space-Opera mit Charakteren, die man ins Herz schließt.
Auf dem kleinen Schiff ist viel Alltag und auf dem langen Weg lässt einen jede Zwischenstation in eine weitere Welt eintauchen, egal ob in einen trubeligen Markt oder einen abgeschiedenen Eisplanet.
Der zweite und dritte Teil handeln im gleichen Universum, sind jedoch nur über wenige Personen/geteilte Themen mit der ersten Geschichte verbunden.
Alle anderen Bücher von Becky Chambers sind auch sehr empfehlenswert "A Psalm for the Wild-Built" ist eine kurze Solarpunk Utopie.
[Link zum Buch beim Frauenbuchladen Thalestris](https://frauenbuchladen.buchkatalog.de/der-lange-weg-zu-einem-kleinen-zornigen-planeten-9783596035687)
### Judith & Christian Vogt: Ace in Space
Ace in Space ist eine tolle Erzählung von einer Raumjäger-Pilotin, einer Gruppe Space-Punks die ihr Leben auf Social Media teilen.
Angesiedelt ist das Buch eher im Cyber-Punk. Es geht gegen Großkonzerne, es geht um schnelle Flieger und Bars in engen Quartieren.
Außerdem hat es eine der besten Sexszenen, die ich je lesen durfte.
Judith schreibt oft dystopischere Geschichten als ich normalerweise lese. Aber Laylayland und Wasteland zwei fantastische Bücher die ich nicht missen will.
Die Bücher sind feministisch, queer und tollerweise auch mit Haupt(Charaktere) mit Behinderungen. Progressive Phanastik der höchsten Klassen!
[Verlagsshop](https://amrun-verlag.de/produkt/aceinspace1/)
Wer eine ganze Kurzgeschichte "der Vögte" (also Judith und Christian Vogt) als Leseprobe haben will, findet diese am Ende des Artikels als PDF.
### T.J. Klune: Mr. Parnassus' Heim für magisch Begabte
Wunderschöne Geschichte über einen Beamten der aus der Stadt rauskommt und ein Haus an der See.
Mehr verrate ich nicht, ist aber eins meiner Lieblingsbücher.
[Link zum Buch beim Frauenbuchladen Thalestris](https://frauenbuchladen.buchkatalog.de/mr-parnassus-heim-fuer-magisch-begabte-9783453321366)
### Lena Richter: Dies ist mein letztes Lied
Eine mitreisende Geschichte in der die Hauptperson durch ihre Musik von Welt zu Welt gerissen wird. Was sie in den einzelnen Episoden erlebt ist schön und herzzerreißend.
Ich habe die Novelle am Stück verschlungen und mit der Hauptperson gelacht und geweint.
[Verlagsshop](https://www.ohneohren.com/shop/Lena-Richter-Dies-ist-mein-letztes-Lied-p520843015)
### Rebecca Thorne: "Can't spell treason without tea"
In einer Fantasy Welt brennt eine Leibwächterin der Königin mit einer Magierin durch und sie eröffnen einen Teeladen.
Es gibt auch den Nachfolger "A Pirate's Life for Tea", den hab ich aber noch nicht gelesen
[Link zum Buch beim Frauenbuchladen Thalestris (Deutsche Übersetzung)](https://frauenbuchladen.buchkatalog.de/cant-spell-treason-without-tea-9783492706896)
### Travis Baldree: "Legends&Latte"
Eine Ork, die lange Jahre mit einer Gruppe Abenteuer unterwegs war versucht nun einen Buch & Teeladen aufzumachen.
Sehr unterhaltsam, unerwartet friedlich und macht unglaublich Lust mehr in Cafes zu gehen!
"Bookshops & Bonedust" ist die Vorgeschichte, die aber gut nach Legends & Latte gelesen werden kann und auch später geschrieben wurde.
[Link zum Buch beim Frauenbuchladen Thalestris](https://frauenbuchladen.buchkatalog.de/magie-und-milchschaum-9783423263566)
### Sammlung: Sonnenseiten - Street Art trifft Solarpunk
22 Autor\*innen haben Geschichten zusammengetragen die die beiden Kunstformen Street Art und Solarpunk verbinden.
Besonders empfehlen kann ich die Geschichte "Uferlos" von Lena Richter die eine schwimmende Stadt zum Denken anregt und "Cloudart" von Dominik Windgätter in der ein "Maskenmädchen" Kunstwerke in den Himmel zeichnet.
[Link zum Buch beim Frauenbuchladen Thalestris](https://frauenbuchladen.buchkatalog.de/sonnenseiten-9783756803972)
## Schluss
Ich hoffe die Empfehlungen machen, trotz ihrer Kürze, Lust aufs Lesen! Kauft bei eurem lokalen Buchladen und unterstützt kleine Verlage!
*Falls das nicht sowieso schon klar war: Ich bekomme für diese Empfehlungen kein Geld, die Links haben kein Tracking, keine Referral Codes oder sonst etwas.*
### Kurzgeschichte FiatLux
Die Kurzgeschichte ist von Judith und Christian Vogt und steht unter der Lizenz [CC-BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/), darf also mit Namensnennung, nichtkommerziell und unter gleichen Bedingungen geteilt werden (wie schön ist das denn bitte?!).
{{< pdf FiatLuxVogt>}}

View File

@@ -1,5 +1,4 @@
<div>
<object data="/uploads/{{ index .Params 0}}.pdf" type="application/pdf" width="100%" height="500px">
<p><a href="/uploads/{{ index .Params 0}}.pdf">Download the PDF!</a></p>
<object class="fitvidsignore" data="/uploads/{{ index .Params 0}}.pdf" type="application/pdf" width="100%" height="500px">
<p><a href="/uploads/{{ index .Params 0}}.pdf">Download the PDF!</a></p>
</object>

View File

@@ -1,14 +0,0 @@
<?php
//database settings
define ("DB_USER", "moanos");
define ("DB_HOST", "localhost");
define ("DB_PW", "dwDs5k4PMQ1a7tK51OjK");
define ("DB_DATABASE", "moanos_gartensia");
//database tables:
define ("TABLE_USER", "user");
define("MODULE_PATH", $_SERVER['DOCUMENT_ROOT']);
?>

View File

@@ -1,45 +0,0 @@
<?php
require_once(__dir__."/config.inc.php");
$aData[TABLE_USER] = array(
'user_ID' => array(
'type' => 'INT',
'size' => 11,
'unique' => 'TRUE',
'standard' => 'NOT NULL',
'extra' => 'AUTO_INCREMENT PRIMARY KEY'
),
'name' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
),
'email' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
),
'signalmessenger' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
),
'sms' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
),
'telegram' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
),
'threema' => array(
'type' => 'VARCHAR',
'size' => 255,
'standard' => 'NOT NULL'
)
);

View File

@@ -1,277 +0,0 @@
<?php
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL);
class Data{
function __construct(){
$this->link_database();
$this->em_check_database();
$this->read_variables();
date_default_timezone_set('Europe/Berlin');
}
function read_variables() {
//reads all GET and POST variables into the object, addslashing both
if (count($_POST)) {
foreach ($_POST as $key => $val){
$key=addslashes("r_".$key);
if (is_array($val)) {
for ($z=0;$z<count($val);$z++) {
$val[$z]=addslashes($val[$z]);
}
}
else {
$val=addslashes($val);
}
$this->$key=$val;
}
}
if (count($_GET)) {
foreach ($_GET as $key => $val){
$key=addslashes("r_".$key);
if (is_array($val)) {
for ($z=0;$z<count($val);$z++) {
$val[$z]=addslashes($val[$z]);
}
}
else {
$val=addslashes($val);
}
$this->$key=$val;
}
}
}//end of function read variables
function link_database() {
$this->databaselink = new mysqli(DB_HOST,DB_USER,DB_PW,DB_DATABASE);
$this->databaselink->set_charset('utf8');
if ($this->databaselink->connect_errno) {
return "Datenbank nicht erreichbar: (" . $this->databaselink->connect_errno . ") " . $this->databaselink->connect_error;
}
else{
$this->databasename=DB_DATABASE;
$this->databaselink->query("SET SQL_MODE = '';");
return True;
}
}
function em_check_database() {
/*
params:
None
returns:
None
This function compares the database structure to a predefined structure which is saved in db_array_config.php
and adds missing structures. Makes installation+updates easy
*/
$aTable=array();
//Alle Tabellen in Array lesen, inklusive aller Eigenschaften
$result=$this->databaselink->query("show tables from ".DB_DATABASE);
while($row = $result->fetch_array(MYSQLI_BOTH)){
$aTable[]=$row[0];
}
$aData=array();
$database_structure_path = __DIR__."/config/db_array.inc.php";
include($database_structure_path);
foreach($aData as $table=>$fields){
if(!in_array($table,$aTable)) {
//Add table to database
$mCounter=0;
$sCommand="CREATE TABLE IF NOT EXISTS `".$table."` (";
foreach($fields as $fieldname=>$properties){
$extra = "";
if($mCounter==0) {
$key="KEY `".$fieldname."` (`".$fieldname."`)";
}
if($properties["size"]!="") {
$size="(".$properties["size"].")";
}
else {
$size="";
}
if((isset($properties["unique"])) and ($properties['unique']==true)) {
$unique="UNIQUE KEY `".$fieldname."_2` (`".$fieldname."`),";}
else {
$unique="";
}
if((isset($properties["extra"])) and ($properties != "")){
$extra = $properties['extra'];
}
$sCommand .= "`".$fieldname."` ".$properties["type"].$size." ".$properties["standard"]." ".$extra.",";
$mCounter++;
}
$sCommand.=$unique.$key.") ENGINE=InnoDB ;";
$this->last_query[]=$sCommand;
$updateresult=$this->databaselink->query($sCommand);
}
else {
//Felder checken und Tabelle updaten
$resultField=$this->databaselink->query("show fields from ".DB_DATABASE.".".$table);
while($aRowF = $resultField->fetch_array(MYSQLI_BOTH)){
$aTableFields[]=$aRowF[0];
}
foreach($fields as $fieldname=>$properties) {
if(!in_array($fieldname,$aTableFields)) {
if((isset($properties["size"]) and ($properties['size']!=""))) {
$size="(".$properties["size"].")";
}
else {
$size="";
}
$sCommand="ALTER TABLE `".$table."` ADD `".$fieldname."` ".$properties["type"].$size." ".$properties["standard"];
$this->last_query[]=$sCommand;
$updateresult=$this->databaselink->query($sCommand);
}
}
}
unset($aTableFields);
unset($aFields);
unset($properties);
}
unset($aData);
}
function store_data($sTable,$aFields,$sKey_ID,$mID) {
//updates or inserts data
//returns ID or -1 if fails
$i=0; $returnID = 0;
if(($mID>0) or ($mID!="") or ($mID != null)) {
//search for it
$aCheckFields=array($sKey_ID=>$mID);
$aRow=$this->select_row($sTable,$aCheckFields);
$returnID=$aRow[$sKey_ID];
}
if(($returnID>0) or ($returnID!="")) {
$sQuery="update ".$sTable." set ";
foreach($aFields as $key=>$value) {
$sQuery.=$key."='".$value."'";
$i++;
if($i<count($aFields)) {
$sQuery.=",";
}
}
$sQuery.=" where ".$sKey_ID."='".$mID."'";
$mDataset_ID=$returnID;
}
else {
$sKeys = ""; $sValues = "";
$sQuery="insert into ".$sTable." (";
foreach($aFields as $sKey=>$value) {
$sKeys.=$sKey;
$sValues.="'".$value."'";
$i++;
if($i<count($aFields)) {
$sKeys.=",";
$sValues.=",";
}
}
$sQuery.=$sKeys.") values (".$sValues.")";
}
$this->last_query[]=$sQuery;
if ($pResult = $this->databaselink->query($sQuery)) {
if(($returnID>0) or ($returnID!="")) {
return $returnID;
}
else {
return $this->databaselink->insert_id;
}
}
else {
return -1;
}
}
function save_user($aUser){
/*
args:
Array $aUser
Array of user information which will be saved.
e.g. array(
'forename' => String $forname,
'surname' => String $surname,
'email' => String $email,
'UID' => String $UID,
'language' => String $language,
'admin' => Bool $admin,
'password' => String md5(str_rev($password)), #deprecated, do not use!
'password_hash' => password_hash(String $password, PASSWORD_DEFAULT)
);
returns:
None
Function will save user Information given in $aUser. If user exists it will
overwrite existing data but not delete not-specified data
*/
$aFields = $aUser;
if ((isset($this->r_user_ID))and ($this->r_user_ID != "")){
$this->ID=$this->store_data(TABLE_USER, $aFields, 'user_ID' , $this->r_user_ID);
}
else{
$this->ID=$this->store_data(TABLE_USER, $aFields, NULL , NULL);
}
}
function get_view($Datei) {
ob_start(); //startet Buffer
include($Datei);
$output=ob_get_contents(); //Buffer wird geschrieben
ob_end_clean(); //Buffer wird gelöscht
return $output;
}
}
//end of class
session_start();
include ("config/config.inc.php");
$oObject = new Data;
$oObject->output = "";
switch ($oObject->r_ac){
case 'user_save':
$aUser = array();
if(isset($oObject->r_user_ID)){
$aUser['user_ID'] = $oObject->r_user_ID;
}
if(isset($oObject->r_name)){
$aUser['name'] = $oObject->r_name;
}
if(isset($oObject->r_email)){
$aUser['email'] = $oObject->r_email;
}
if(isset($oObject->r_email)){
$aUser['signalmessenger'] = $oObject->r_signalmessenger;
}
if(isset($oObject->r_email)){
$aUser['sms'] = $oObject->r_sms;
}
if(isset($oObject->r_email)){
$aUser['telegram'] = $oObject->r_telegram;
}
if(isset($oObject->r_email)){
$aUser['threema'] = $oObject->r_threema;
}
$oObject->save_user($aUser);
$oObject->output .= "Erfolgreich gespeichert";
break;
default:
$oObject->output = $oObject->get_view("views/user_form.php");
break;
}
function output($oObject){
echo $oObject->get_view("views/head.php");
echo $oObject->get_view("views/body.php");
}
output($oObject);
?>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View File

@@ -1,13 +0,0 @@
<body>
<?php
if ((isset($this->error)) and ($this->error != "")){
echo "<div id=error>";
echo $this->error;
echo "</div>";
}
echo "<div id=content>";
echo $this->output;
echo "</div>";
?>

View File

@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="author" content="Sam">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<link rel="SHORTCUT ICON" type="image/x-icon" href="images/favicon.ico">
<?php
echo ' <link REL="stylesheet" TYPE="text/css" HREF="css/styles.css">
<title>Address collection</title>
';
?>
</head>

View File

@@ -1,17 +0,0 @@
<?php
$form = '<form action="'.htmlspecialchars($_SERVER["PHP_SELF"]).'" method="post">';
$form .='
<input type = hidden name="ac" value = "user_save">
<input type = hidden name="user_ID" value = "">';
$form .= 'Name: <input type="text" name="name" value=""><br>';
$form .= 'E-Mail: <input type="text" name="email" value=""><br>';
$form .= 'Signal: <input type="text" name="signalmessenger" value=""><br>';
$form .= 'SMS: <input type="text" name="sms" value=""><br>';
$form .= 'Telegram: <input type="text" name="telegram" value=""><br>';
$form .= 'Threema: <input type="text" name="threema" value=""><br>';
$form .= '
<input type="submit" value="Send">
<input type="reset" value="Reset";
</form>';
echo $form;
?>