This commit is contained in:
Torsten Schulz
2023-06-16 11:57:49 +02:00
commit 44da93c0e9
328 changed files with 134580 additions and 0 deletions

81
include/accounts.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
include 'renderer.php';
class Accounts extends Renderer {
public function __construct(?string $templateName) {
parent::__construct($templateName);
$this->renderInactiveUsers();
$this->renderActiveUsers();
}
private function renderInactiveUsers(): void {
$result = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE active=0');
$content = '<tbody>';
while ($row = mysqli_fetch_assoc($result)) {
$content .= '<tr><td>' . $row['username'] . '</td><td>' . $this->decode($row['realname'], $row['salt'])
. '</td><td>' . $this->decode($row['email'], $row['salt']) . '</td>'
. '<td><button type="submit" name="action" value="activate:' . $row['username'] . '">Aktivieren</button>'
. '<button type="submit" name="action" value="delete:' . $row['username'] . '">Löschen</button></td></tr>';
}
$content .= '</tbody>';
$this->content['inactive_accounts'] = $content;
}
private function renderActiveUsers(): void {
$result = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE active=1');
$content = '<tbody>';
while ($row = mysqli_fetch_assoc($result)) {
$content .= '<tr><td>' . $row['username'] . '</td><td>' . $this->decode($row['realname'], $row['salt'])
. '</td><td>' . $this->decode($row['email'], $row['salt']) . '</td>'
. '<td><button type="submit" name="action" value="delete:' . $row['username'] . '">Löschen</button></td></tr>';
}
$content .= '</tbody>';
$this->content['active_accounts'] = $content;
}
protected function formAction(): void {
$actionParams = explode(':', trim(filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING)));
switch ($actionParams[0]) {
case 'activate':
$this->activateAccount($actionParams[1]);
break;
case 'delete':
$this->deleteAccount($actionParams[1]);
break;
default:
break;
}
}
private function activateAccount(string $accountName): void {
$emailTo = 'Vorstand Förderverein AJS <foerderverein-ajs@gmx.de>';
mysqli_begin_transaction($this->dbConnection);
mysqli_query($this->dbConnection, sprintf('UPDATE `user` SET `active`=1 WHERE `username`="%s"', $accountName));
mysqli_commit($this->dbConnection);
$result = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE `username` = lower("' . trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES)) . '")');
$user = mysqli_fetch_assoc($result);
$message = 'Dein Account "' . $accountName . '" wurde aktiviert.';
$headers = 'From: ' . $emailTo . "\r\n" .
'Reply-To: ' . $emailTo . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($user['email'], 'Zugang zu internem Bereich beantragt', $message, $headers);
header('Location: accounts', true, 301);
die();
}
private function deleteAccount(string $accountName): void {
$emailTo = 'Vorstand Förderverein AJS <foerderverein-ajs@gmx.de>';
$result = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE `username` = lower("' . trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES)) . '")');
$user = mysqli_fetch_assoc($result);
mysqli_begin_transaction($this->dbConnection);
mysqli_query($this->dbConnection, sprintf('UPDATE `user` SET `active`=-1 WHERE `username`="%s"', $accountName));
mysqli_commit($this->dbConnection);
$message = 'Der Account "' . $accountName . '" wurde als gelöscht markiert.';
$headers = 'From: ' . $emailTo . "\r\n" .
'Reply-To: ' . $emailTo . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($user['email'], 'Zugang zu internem Bereich beantragt', $message, $headers);
header('Location: accounts', true, 301);
die();
}
}

57
include/bulkmail.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
include 'renderer.php';
class Bulkmail extends Renderer {
protected array $formFields = [
['label' => 'Betreff', 'type' => 'text', 'size' => 50, 'name' => 'subject', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'optional' => false],
['label' => 'Text', 'type' => 'textarea', 'name' => 'body', 'combine_with_next_line' => false, 'filter' => FILTER_UNSAFE_RAW, 'optional' => false, 'rows' => 20, 'cols' => '60'],
['label' => 'Signatur', 'type' => 'combobox', 'size' => 5, 'name' => 'signature', 'combine_with_next_line' => false,
'values' => ['Persönliche Signatur', 'Vorstandssignatur'],
],
];
protected string $formSendButtonLabel = 'Email an alle aktiven Mitglieder absenden';
protected function formAction(): void {
$this->sendEmail($this->generateSubject(), $this->generateBody(), $this->generateSignature(filter_input(INPUT_POST, 'signature')));
}
protected function generateSubject(): string {
return filter_input(INPUT_POST, 'subject', FILTER_SANITIZE_STRING);
}
protected function generateBody(): string {
return preg_replace('/(<script(.*)\/script>|<iframe(.*)\/iframe>)/', '', filter_input(INPUT_POST, 'body'));
}
protected function sendEmail(string $subject, string $body, string $signature): void {
$mail = $this->initSmtpMailer();;
$bccRecipients = $this->loadReceivers();
try {
$mail->setFrom('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addReplyTo('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addAddress('vorstand@fvajs.de', 'Vorstand des FVAJS');
foreach ($bccRecipients as $recipient) {
$mail->addBCC($recipient);
}
$this->sendMail($mail, $subject, $body, $signature);
} catch (Exception $e) {
$this->templateName = 'error_smtp';
}
$this->templateName = 'bulkmail_success';
$this->content['recipients'] = implode('<br />', $bccRecipients);
}
protected function loadReceivers(): array {
$query = 'SELECT c.first_name, c.last_name, c.email, c.salt
FROM clubmember c
JOIN clubmember_status cs
ON cs.id = c.membership_status
WHERE cs.status_text = "Mitgliedschaft bestätigt"';
$dbResult = mysqli_query($this->dbConnection, $query);
$recipientsList = [];
while ($row = mysqli_fetch_assoc($dbResult)) {
$recipientsList[] = $this->decode($row['email'], $row['salt']);
}
return $recipientsList;
}
}

29
include/contact.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
include 'renderer.php';
class Contact extends Renderer {
protected array $formFields = [
['label' => 'Ihre Nachricht an uns:', 'type' => 'textarea', 'size' => 50, 'name' => 'message', 'combine_with_next_line' => false,
'rows' => 14, 'cols' => 100],
['label' => 'Ihr Name', 'type' => 'text', 'size' => 50, 'name' => 'sendername', 'combine_with_next_line' => false],
['label' => 'Ihre Email-Adresse', 'type' => 'text', 'size' => 50, 'name' => 'senderemail', 'combine_with_next_line' => false],
];
protected string $formSendButtonLabel = 'Nachricht absenden';
protected function formAction(): void {
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);
$senderName = filter_input(INPUT_POST, 'sendername', FILTER_SANITIZE_STRING);
$senderEmail = filter_input(INPUT_POST, 'senderemail', FILTER_SANITIZE_EMAIL);
$noForm = trim($message) !== '';
if (!$noForm) {
$this->errors[] = 'Leider haben Sie keine Nachricht an uns hinterlassen. Bitte versuchen Sie es nochmal.';
return;
}
$mail = $this->initSmtpMailer();
$mail->setFrom(($senderEmail !== '') ? $senderEmail : 'foerderverein-ajs@gmx.de', ($senderName !== '') ? $senderName: 'Anonymer Sender');
$mail->addReplyTo(($senderEmail !== '') ? $senderEmail : 'foerderverein-ajs@gmx.de', ($senderName !== '') ? $senderName: 'Anonymer Sender');
$mail->addAddress('foerderverein-ajs@gmx.de', 'Vorstand Förderverein AJS');
$this->sendMail($mail, 'Kontaktformular', $message, '');
$this->templateName = 'message_success';
}
}

View File

@@ -0,0 +1,17 @@
<?php
include 'renderer.php';
class Deletedocument extends Renderer {
public function render(): void {
if ($_SESSION['userid'] === 0 && in_array($this->getScriptName() , $this->internalMenuItems, false)) {
$this->templateName = 'login_error';
parent::render();
return;
}
$query = 'DELETE FROM `document` WHERE `title` = "' . filter_input(INPUT_POST, 'file', FILTER_SANITIZE_ADD_SLASHES) . '"';
mysqli_query($this->dbConnection, $query);
@unlink('/var/shared/fvajs/' . filter_input(INPUT_GET, 'ts', FILTER_SANITIZE_STRING));
echo '';
}
}

72
include/documents.php Normal file
View File

@@ -0,0 +1,72 @@
<?php
include 'renderer.php';
class Documents extends Renderer {
protected array $formFields = [
['label' => 'Dokumenttitel', 'type' => 'text', 'size' => 50, 'name' => 'documenttitle', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'optional' => false],
['label' => 'Dokumentdatei', 'type' => 'file', 'size' => 50000, 'name' => 'document', 'combine_with_next_line' => false, 'optional' => true],
['label' => 'Oder Datei erstellen aus', 'type' => 'textarea', 'cols' => 80, 'rows' => '15', 'name' => 'newtext', 'combine_with_next_line' => false, 'optional' => true],
];
protected string $formSendButtonLabel = 'Dokument hinzufügen';
protected string $encType = 'multipart/form-data';
public function __construct() {
parent::__construct();
}
protected function formAction(): void {
$this->formCheckFields();
if (count($this->errors) === 0) {
$newFileName = $this->generateRandomString(64);
$salt = $this->generateRandomString();
$this->saveFile($newFileName, $salt);
$query = sprintf('INSERT INTO ffajs.document
(title, original_filename, local_filename, salt)
VALUES("%s", "%s", "%s", "%s")', trim(filter_input(INPUT_POST, 'documenttitle', $this->formFields[0]['filter'])),
$this->getOriginalFileName(),
$newFileName, $salt);
mysqli_query($this->dbConnection, $query);
}
}
protected function formCheckFields(): bool {
parent::formCheckFields();
$result = mysqli_query($this->dbConnection, 'SELECT `id` FROM `document` WHERE `title` = "' . trim(filter_input(INPUT_POST, 'documenttitle', $this->formFields[0]['filter'])) . '"');
if (mysqli_num_rows($result) > 0) {
$this->errors['documenttitle'] = 'Der Titel existiert bereits';
}
return count($this->errors) === 0;
}
protected function saveFile(string $newFileName, string $salt): void {
if (trim(filter_input(INPUT_POST, 'newtext', FILTER_SANITIZE_STRING)) !== '') {
$content = filter_input(INPUT_POST, 'newtext', FILTER_SANITIZE_STRING);
} elseif ($_FILES['document']['tmp_name']) {
$content = file_get_contents($_FILES['document']['tmp_name']);
} else {
$this->errors['newtext'] = 'Es muss eine Datei hochgeladen oder hier ein Text eingegeben werden.';
return;
}
$this->saveFileLocal($newFileName, $content, $salt);
}
protected function generateContent(): void {
$result = mysqli_query($this->dbConnection, 'SELECT * FROM `document` ORDER BY `title`');
$tableBody = '';
while ($row = mysqli_fetch_assoc($result)) {
$tableBody .= '<tr>';
$tableBody .= '<td>' . $row['title'] . '</td>';
$tableBody .= '<td>' . $row['original_filename'] . '</td>';
$tableBody .= '<td><a href="download?file=' . $row['local_filename'] . '">Download</a></td>';
$tableBody .= '<td><button type="button" name="delete" value="' . $row['local_filename'] . '">Löschen</button></td>';
$tableBody .= '</tr>';
}
$this->content['documents'] = $tableBody;
}
protected function getOriginalFileName(): string {
return trim(filter_input(INPUT_POST, 'newtext', FILTER_SANITIZE_STRING)) !== ''
? trim(filter_input(INPUT_POST, 'documenttitle', $this->formFields[0]['filter'])) . '.txt'
: $_FILES['document']['name'];
}
}

31
include/download.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
include 'renderer.php';
class Download extends Renderer {
public function render(): void {
if ($_SESSION['userid'] === 0 && in_array($this->getScriptName() , $this->internalMenuItems, false)) {
$this->templateName = 'login_error';
parent::render();
return;
}
$params = $this->getUriParams();
$query = sprintf('SELECT `original_filename`, `salt` FROM `document` WHERE `local_filename` = "%s"', $params['file']);
$result = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($result) < 1) {
die ('Datei nicht gefunden');
}
$row = mysqli_fetch_assoc($result);
$encryptedFile = file_get_contents('/var/shared/fvajs/' . $params['file']);
$decryptedFile = $this->decode($encryptedFile, $row['salt']);
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Cache-Control: no-cache, must-revalidate");
header("Expires: 0");
header('Content-Disposition: attachment; filename="' . $row['original_filename'] . '"');
header('Content-Length: ' . strlen($decryptedFile));
header('Pragma: public');
flush();
echo $decryptedFile;
}
}

89
include/editmember.php Normal file
View File

@@ -0,0 +1,89 @@
<?php
include 'renderer.php';
class Editmember extends Renderer {
protected array $formFields = [
['label' => 'Nachname', 'type' => 'text', 'size' => 50, 'name' => 'lastname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'last_name', 'optional' => false],
['label' => 'Vorname', 'type' => 'text', 'size' => 50, 'name' => 'firstname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'first_name', 'optional' => false],
['label' => 'Straße', 'type' => 'text', 'size' => 50, 'name' => 'streetname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'street', 'optional' => false],
['label' => 'Plz', 'type' => 'text', 'size' => 5, 'name' => 'zip', 'combine_with_next_line' => true, 'regex' => '/^([0-9]{5})$/', 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'zip', 'optional' => false],
['label' => 'Ort', 'type' => 'text', 'size' => 40, 'name' => 'town', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'town', 'optional' => false],
['label' => 'Birthday', 'type' => 'date', 'size' => 50, 'name' => 'birthdate', 'combine_with_next_line' => false, 'regex' => '/^((19|20)([0-9]){2}-[0-9]{2})-([0-9]{2})$/', 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'birthdate', 'optional' => false],
['label' => 'Phone', 'type' => 'text', 'size' => 50, 'name' => 'phone', 'combine_with_next_line' => false, '/^0([0-9]{2,6})([ ]{0,1})([-\/]{0,1})([ ]{0,1})([0-9 ]{4,25})$/', 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'phone', 'optional' => false],
['label' => 'Email', 'type' => 'email', 'size' => 50, 'name' => 'email', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_EMAIL, 'dbfield' => 'email', 'optional' => true],
['label' => 'Name des Kindes', 'type' => 'text', 'size' => 50, 'name' => 'childname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'child_name', 'optional' => true],
['label' => 'Straße des Kindes', 'type' => 'text', 'size' => 50, 'name' => 'childstreet', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'child_street', 'optional' => true],
['label' => 'Gewählter Beitrag', 'type' => 'number', 'size' => 50, 'name' => 'subscription', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'subscription', 'optional' => false],
['label' => 'Geldinstitut', 'type' => 'text', 'size' => 50, 'name' => 'bankname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'bank_name', 'optional' => true],
['label' => 'IBAN', 'type' => 'text', 'size' => 50, 'name' => 'iban', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'iban', 'optional' => true],
['label' => 'BIC', 'type' => 'text', 'size' => 50, 'name' => 'bic', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'bic', 'optional' => true],
['label' => 'Kontoinhaber', 'type' => 'text', 'size' => 50, 'name' => 'accountmember', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'account_member_name', 'optional' => true],
['label' => 'Mitgliedsstatus', 'type' => 'dbselect', 'size' => 0, 'name' => 'status', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'membership_status',
'sourcedb' => 'clubmember_status', 'optionfield' => 'status_text', 'encryption' => false],
['label' => 'Position im Verein', 'type' => 'dbselect', 'size' => 0, 'name' => 'position', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'position_id',
'sourcedb' => 'clubmember_position', 'optionfield' => 'description', 'encryption' => false, 'with_null_field' => true],
['label' => 'Online-Account', 'type' => 'dbselect', 'size' => 0, 'name' => 'user_id', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'user_id',
'sourcedb' => 'user', 'optionfield' => 'username', 'encryption' => false, 'with_null_field' => true],
];
protected string $dbTable = 'clubmember';
protected bool $dbGenerateSaltField = true;
protected bool $dbUpdate = true;
protected string $formSendButtonLabel = 'Änderungen speichern';
protected bool $saveAllFields = true;
public function __construct(?string $templateName = null) {
parent::__construct($templateName);
$id = $this->getUriParams()['id'];
$this->loadUserData($id);
}
protected function loadUserData(string $id): void {
$query = sprintf("SELECT c.first_name as firstname, c.last_name as lastname, c.street as streetname, c.zip, c.town, c.birthdate, c.phone, c.email, "
. " c.child_name as childname, c.child_street as childstreet, c.subscription, c.bank_name as bankname, c.iban , c.bic , "
. " c.account_member_name as accountmember, c.membership_status as status, c.position_id as position, c.user_id as user_id, c.salt "
. "FROM clubmember c "
. "WHERE c.id = %d", $id);
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) === 0) {
$this->templateName = 'notfound_error';
return;
}
$line = mysqli_fetch_assoc($dbResult);
$salt = $line['salt'];
array_walk($line, function(&$item, $key) use ($salt) {
if (!isset($item)) {
return;
} elseif ($this->fieldHasToBeEncrypted($key)) {
$item = $this->decode($item, $salt);
}
});
$this->predefines = $line;
}
protected function formAction(): void {
if (!$this->formCheckFields()) {
return;
}
$saltQuery = "SELECT id, membership_status, salt FROM clubmember c WHERE id=" . $this->getUriParams()['id'];
$dbResult = mysqli_query($this->dbConnection, $saltQuery);
if (!$dbResult) {
return;
}
$oldDataRow = mysqli_fetch_assoc($dbResult);
$this->salt = $oldDataRow['salt'];
$this->saveToDb();
if (filter_input(INPUT_POST, 'status', FILTER_SANITIZE_NUMBER_INT) != $oldDataRow['membership_status']) {
$this->saveMembershipStatusChangeHistory($oldDataRow['id'], $oldDataRow['membership_status'],
filter_input(INPUT_POST, 'status', FILTER_SANITIZE_NUMBER_INT));
}
$this->cleanFields = false;
$this->messages[] = 'Mitglied erfolgreich bearbeitet.';
}
protected function saveMembershipStatusChangeHistory(string $clubmemberId, string $oldStatus, string $newStatus): void {
$query = sprintf("INSERT INTO ffajs.clubmember_status_history (clubmember_id, changer_id, old_status_id, new_status_id, change_timestamp) "
. "VALUES(%d, %d, %d, %d, now()); ",
$clubmemberId, $_SESSION['userid'], $oldStatus, $newStatus);
mysqli_query($this->dbConnection, $query);
}
}

72
include/emailinbox.php Normal file
View File

@@ -0,0 +1,72 @@
<?php
require 'renderer.php';
require 'vendor/autoload.php';
class Emailinbox extends Renderer {
public function __construct(?string $templateName = null) {
parent::__construct();
if (!$this->connectToImap()) {
$this->templateName = 'imaperror';
}
}
protected function readEmailHeaders(): array {
$cleanedHeaders = [];
try {
$mailsIds = $this->mbox->searchMailbox('ALL');
} catch(PhpImap\Exceptions\ConnectionException $ex) {
echo "IMAP connection failed: " . implode(",", $ex->getErrors('all'));
die();
}
if(!$mailsIds) {
die('Mailbox is empty');
}
$headers = $this->mbox->getMailsInfo($mailsIds);
foreach ($headers as $header) {
try {
$cleanedHeaders[trim($header->msgno)] = [
'title' => $header->subject,
'date' => \DateTime::createFromFormat('D, d M Y H:i:s O', str_replace(' (CET)', '', $header->date))->setTimezone(new DateTimeZone('Europe/Berlin')),
'from' => $header->from,
'unread' => !$header->seen,
];
} catch (\exception $err) {
}
}
return $cleanedHeaders;
}
protected function getDateObjectFromString(string $dateString) {
$date = DateTime::createFromFormat(DateTimeInterface::RFC1123, $dateString);
if (!$date) {
$date = DateTime::createFromFormat(DateTimeInterface::RFC1123, substr($dateString, 0, -6));
}
if (!$date) {
$date = DateTime::createFromFormat('d M Y H:i:s O', $dateString);
}
if (!$date) {
echo $dateString;
}
return $date;
}
protected function generateContent(): void {
$headers = $this->readEmailHeaders();
uasort($headers, ['Emailinbox', 'compareDateTimes']);
array_walk($headers, function(&$item, $key) {
$newItem = '<tr>';
$newItem .= '<td style="white-space:nowrap">' . ($item['date'] === false ? '' : $item['date']->format('d.m.Y H:i:s')) . '</td>';
$newItem .= '<td><a href="mail?uid=' . $key . '">' . ($item['unread'] === 'U' ? '<b>' : '') . $item['title'] . ($item['unread'] === 'u' ? '</b>' : '') . '</td>';
$newItem .= '<td>' . $item['from'] . '</td>';
$newItem .= '</tr>';
$item = $newItem;
});
$this->content['mails'] = implode('', $headers);
}
protected function compareDateTimes($item1, $item2): int {
return $item2['date'] <=> $item1['date'];
}
}

7
include/footer.php Normal file
View File

@@ -0,0 +1,7 @@
</main>
<footer>
<span class="footer-copyright">(c) 2022 Förderverein August-Jaspert-Schule e.V.</span>
<a href="imprint.php" class="footer-imprint">Impressum</span>
</footer>
</body>
</html>

13
include/header.php Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Förderverein August-Jaspert-Schule e.V.</title>
<link rel="stylesheet" type="text/css" href="style/main.css" />
</head>
<body>
<header>
<h1>Förderverein August-Jaspert-Schule e.V.</h1>
</header>
<?php include 'include/menu.php'; ?>
<main>

31
include/login.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
include 'renderer.php';
class Login extends Renderer {
protected array $formFields = [
['label' => 'Benutzername', 'type' => 'text', 'size' => 50, 'name' => 'username', 'combine_with_next_line' => false],
['label' => 'Paßwort', 'type' => 'password', 'size' => 50, 'name' => 'password', 'combine_with_next_line' => false],
];
protected string $formSendButtonLabel = 'Einloggen';
protected function formAction(): void {
$this->userId = 0;
$result = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE `username` = lower("' . trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES)) . '")');
if ($result->num_rows !== 1) {
$this->errors[] = 'Benutzer und/oder Paßwort falsch';
return;
}
$user = $result->fetch_assoc();
if (!password_verify(filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING), $user['password'])) {
$this->errors[] = 'Benutzer und/oder Paßwort falsch';
return;
}
if ($user['active'] !== '1') {
$this->errors[] = 'Dein Zugang ist noch nicht freigeschaltet.';
return;
}
$_SESSION['userid'] = $user['id'];
header('Location: accounts', true, 301);
die();
}
}

11
include/logout.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
include 'renderer.php';
class Logout extends Renderer {
public function __construct(?string $templateName = null) {
session_start();
$_SESSION['userid'] = 0;
session_write_close();
header('Location: index', true, 301);
}
}

34
include/mail.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
require 'vendor/autoload.php';
require 'mailhandling.php';
class Mail extends Mailhandling {
// protected $mbox = null;
protected $uid = 0;
public function __construct(?string $templateName = null) {
parent::__construct();
if (!$this->connectToImap()) {
$this->templateName = 'imaperror';
return;
}
$this->uid = $this->getUriParams()['uid'];
$this->content['uid'] = $this->uid;
}
protected function generateContent(): void {
$messageStructure = imap_fetchstructure($this->mbox, $this->uid);
$this->fetchEmailHeader($this->content);
$this->fetchEmailBody($messageStructure, $this->content, 'INBOX', FT_PEEK);
$this->renderAttachments($messageStructure);
}
protected function renderAttachments($messageStructure): void {
$attachments = $this->getAttachments($messageStructure);
$contentArray = [];
foreach ($attachments as $attachment) {
$contentArray[] = $attachment['filename'];
}
$this->content['attachments'] = implode('<br>', $contentArray);
}
}

82
include/mailhandling.php Normal file
View File

@@ -0,0 +1,82 @@
<?php
require 'renderer.php';
class Mailhandling extends Renderer {
public function __construct() {
parent::__construct();
if (!$this->connectToImap()) {
$this->templateName = 'imaperror';
return;
}
}
protected function fetchEmailHeader(array &$content): void {
$header = imap_headerinfo($this->mbox, $this->uid);
$content['sender'] = imap_utf8($header->fromaddress);
$content['receiver'] = imap_utf8($header->toaddress);
$content['subject'] = imap_utf8($header->subject);
$content['senddate'] = date('d.m.Y H:i:s', strtotime($header->date));
}
protected function fetchEmailBody($messageStructure, array &$content): void {
$message = imap_fetchbody($this->mbox, $this->uid, 1.1, FT_PEEK);
if($message == '') {
$message = imap_fetchbody($this->mbox, $this->uid, 1, FT_PEEK);
}
$decodedMessage = quoted_printable_decode($message);
if (isset($messageStructure->parts) && isset($messageStructure->parts[1]) && isset($messageStructure->parts[1]->parameters)
&& isset($messageStructure->parts[1]->parameters) && is_array($messageStructure->parts[1]->parameters) && isset($messageStructure->parts[1]->parameters[0]->value) && strtolower($messageStructure->parts[1]->parameters[0]->value) != 'utf-8') {
$decodedMessage = utf8_encode($decodedMessage);
}
$content['emailbody'] = nl2br($decodedMessage);
}
protected function getAttachments($structure): array {
$attachments = [];
if(isset($structure->parts) && count($structure->parts)) {
for($i = 0; $i < count($structure->parts); $i++) {
$attachments[$i] = [
'is_attachment' => false,
'filename' => '',
'name' => '',
'attachment' => ''];
if($structure->parts[$i]->ifdparameters) {
foreach($structure->parts[$i]->dparameters as $object) {
if(strtolower($object->attribute) == 'filename') {
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['filename'] = $object->value;
}
}
}
if($structure->parts[$i]->ifparameters) {
foreach($structure->parts[$i]->parameters as $object) {
if(strtolower($object->attribute) == 'name') {
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['name'] = $object->value;
}
}
}
if($attachments[$i]['is_attachment']) {
$attachments[$i]['attachment'] = imap_fetchbody($this->mbox, $this->uid, $i+1);
if($structure->parts[$i]->encoding == 3) { // 3 = BASE64
$attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
}
elseif($structure->parts[$i]->encoding == 4) { // 4 = QUOTED-PRINTABLE
$attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
}
}
if ($attachments[$i]['is_attachment']) {
}
}
}
$realAttachments = array_filter($attachments, function($attachment) {
return $attachment['is_attachment'];
});
return $realAttachments;
}
}

30
include/members.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
include 'renderer.php';
class Members extends Renderer {
public function __construct(?string $templateName = null) {
parent::__construct($templateName);
$result = mysqli_query($this->dbConnection,
'SELECT c.*, cs.`status_text`, (SELECT ph.`payment_date` FROM `paying_history` ph WHERE ph.`clubmember_id` = c.`id` ORDER BY `payment_date` DESC LIMIT 1) as last_payment '
. 'FROM `clubmember` c '
. 'JOIN `clubmember_status` cs '
. ' ON cs.`id` = c.`membership_status` '
. 'WHERE cs.`status_text` NOT IN ("Mitgliedschaft abgelehnt", "Mitgliedschaft gekündigt") '
. 'ORDER BY c.`last_name`, c.`first_name`');
$tableBody = '<tbody>';
while ($row = mysqli_fetch_assoc($result)) {
$tableBody .= '<tr>'
. '<td>' . $row['id'] . '</td>'
. '<td>' . $this->decode($row['last_name'], $row['salt']) . ', ' . $this->decode($row['first_name'], $row['salt']) . '</td>'
. '<td>' . $this->decode($row['email'], $row['salt']) . '</td>'
. '<td>' . $row['last_payment'] . '</td>'
. '<td>' . $row['membership_start'] . '</td>'
. '<td>' . $row['status_text'] . '</td>'
. '<td><a href="editmember?id=' . $row['id'] . '">Bearbeiten</a>&nbsp;'
. '<a href="payings?id=' . $row['id'] . '">Zahlungen</a></td>'
. '</tr>';
}
$tableBody .= '</tbody>';
$this->content['memberlist'] = $tableBody;
}
}

125
include/membership.php Normal file
View File

@@ -0,0 +1,125 @@
<?php
include 'renderer.php';
class Membership extends Renderer {
protected array $formFields = [
['label' => 'Vorname', 'type' => 'text', 'size' => 50, 'name' => 'firstname', 'combine_with_next_line' => false],
['label' => 'Nachname', 'type' => 'text', 'size' => 50, 'name' => 'lastname', 'combine_with_next_line' => false],
['label' => 'Straße (mit Hausnummer)', 'type' => 'text', 'size' => 50, 'name' => 'street', 'combine_with_next_line' => false],
['label' => 'Plz', 'type' => 'text', 'size' => 5, 'name' => 'zip', 'combine_with_next_line' => true],
['label' => 'Ort', 'type' => 'text', 'size' => 40, 'name' => 'town', 'combine_with_next_line' => false],
['label' => 'Geburtsdatum', 'type' => 'date', 'size' => 50, 'name' => 'birthdate', 'combine_with_next_line' => false],
['label' => 'Telefon-Nr.', 'type' => 'text', 'size' => 50, 'name' => 'phone', 'combine_with_next_line' => false],
['label' => 'Email-Adresse', 'type' => 'email', 'size' => 50, 'name' => 'email', 'combine_with_next_line' => false],
['label' => 'Name des Kindes (Optional)', 'type' => 'text', 'size' => 50, 'name' => 'childname', 'combine_with_next_line' => false],
['label' => 'Straße des Kindes (Optional)', 'type' => 'text', 'size' => 50, 'name' => 'childstreet', 'combine_with_next_line' => false],
['label' => 'Gewählter Jahresbeitrag', 'type' => 'combobox', 'size' => 50, 'name' => 'payheight', 'combine_with_next_line' => false,
'values' => ['12 €', '25 €', '50 €', '60 €', '100 €', 'Selbst wählen'], 'default' => '25 €',],
['label' => 'Höhe des freien Beitrags (Optional)', 'type' => 'number', 'size' => 50, 'name' => 'freepayheight', 'value' => 0, 'combine_with_next_line' => false],
['label' => 'Ich stimme der elektronischen Verarbeitung und Speicherung meiner Daten zu', 'type' => 'checkbox', 'size' => 1, 'name' => 'accept_electronical_usage', 'value' => 1],
['type' => 'spacer', 'value' => ''],
['type' => 'infotext', 'label' => '<h3>(Optional) Bankeinzugsinformationen</h3>'],
['label' => 'Geldinstitut', 'type' => 'text', 'size' => 50, 'name' => 'bankname', 'combine_with_next_line' => false],
['label' => 'IBAN', 'type' => 'text', 'size' => 50, 'name' => 'iban', 'combine_with_next_line' => false],
['label' => 'BIC', 'type' => 'text', 'size' => 50, 'name' => 'bic', 'combine_with_next_line' => false],
['label' => 'Kontoinhaber', 'type' => 'text', 'size' => 50, 'name' => 'accountmembername', 'combine_with_next_line' => false],
];
protected string $formSendButtonLabel = 'Mitgliedschaftsantrag vorausgefüllt beantragen';
protected function formAction(): void {
$formData['firstname'] = trim(filter_input(INPUT_POST, 'firstname', FILTER_SANITIZE_STRING));
$formData['lastname'] = trim(filter_input(INPUT_POST, 'lastname', FILTER_SANITIZE_STRING));
$formData['street'] = trim(filter_input(INPUT_POST, 'street', FILTER_SANITIZE_STRING));
$formData['zip'] = trim(filter_input(INPUT_POST, 'zip', FILTER_SANITIZE_STRING));
$formData['town'] = trim(filter_input(INPUT_POST, 'town', FILTER_SANITIZE_STRING));
$formData['birthDate'] = trim(filter_input(INPUT_POST, 'birthdate', FILTER_SANITIZE_STRING));
$formData['phoneNumber'] = trim(filter_input(INPUT_POST, 'phone', FILTER_SANITIZE_STRING));
$formData['email'] = trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_STRING));
$formData['childName'] = trim(filter_input(INPUT_POST, 'childname', FILTER_SANITIZE_STRING));
$formData['childStreet'] = trim(filter_input(INPUT_POST, 'childstreet', FILTER_SANITIZE_STRING));
$formData['payHeight'] = trim(filter_input(INPUT_POST, 'payheight', FILTER_SANITIZE_STRING));
$formData['bankname'] = trim(filter_input(INPUT_POST, 'bankname', FILTER_SANITIZE_STRING));
$formData['iban'] = trim(filter_input(INPUT_POST, 'iban', FILTER_SANITIZE_STRING));
$formData['bic'] = trim(filter_input(INPUT_POST, 'bic', FILTER_SANITIZE_STRING));
$formData['agreedElectronicalHandling'] = intval(filter_input(INPUT_POST, 'accept_electronical_usage', FILTER_SANITIZE_NUMBER_INT));
$formData['accountmembername'] = trim(filter_input(INPUT_POST, 'accountmembername', FILTER_SANITIZE_STRING));
$this->checkFormData($formData);
$this->saveNewMember($formData);
$this->sendEmail($formData);
}
protected function checkFormData(array $formData): void {
if ($formData['payHeight'] === 'Selbst wählen') {
$formData['payHeight'] = filter_input(INPUT_POST, 'freepayheight', FILTER_SANITIZE_NUMBER_INT);
}
if (!preg_match('/^([0-9]{5})$/', $formData['zip'])) {
$this->errors['zip'] = 'Die Postleitzahl ist nicht korrekt';
}
if (!preg_match('/^((19|20)([0-9]){2}-[0-9]{2})-([0-9]{2})$/', $formData['birthDate']) || (DateTimeImmutable::createFromFormat('Y-m-d', $formData['birthDate']))->getTimestamp() > time()) {
$this->errors['birthdate'] = 'Das eingegebene Geburtsdatum ist nicht korrekt';
}
if (!preg_match('/^0([0-9]{2,6})([ ]{0,1})([-\/]{0,1})([ ]{0,1})([0-9 ]{4,25})$/', $formData['phoneNumber'])) {
$this->errors['phone'] = 'Die Telefonnummer ist nicht korrekt';
}
if (!filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
$this->errors['email'] = 'Die Email-Adresse ist inkorrekt';
}
if ($formData['agreedElectronicalHandling'] !== 1) {
$this->errors['accept_electronical_usage'] = 'Für die Online-Registrierung müssen Sie der elektronischen Verarbeitung zustimmen';
}
}
protected function saveNewMember(array $formData): void {
$salt = $this->generateRandomString();
$query = sprintf("INSERT INTO ffajs.clubmember( "
. " first_name, last_name, street, zip, town, "
. " birthdate, phone, email, child_name, child_street, "
. " subscription, bank_name, iban, bic, account_member_name, "
. " membership_status, salt) "
. "VALUES('%s', '%s', '%s', '%s', '%s', "
. " '%s', '%s', '%s', '%s', '%s', "
. " '%s', '%s', '%s', '%s', '%s', "
. " %d, '%s') ",
$this->getDbEncryptedValueIfNeeded($formData, 'firstname', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'lastname', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'street', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'zip', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'town', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'birthDate', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'phoneNumber', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'email', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'childName', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'childStreet', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'payHeight', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'bankname', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'iban', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'bic', $salt),
$this->getDbEncryptedValueIfNeeded($formData, 'accountmembername', $salt),
1,
$salt);
mysqli_query($this->dbConnection, $query);
}
protected function sendEmail(array $formData): void {
$noForm = count($this->errors) === 0;
if ($noForm) {
$data = ['Name' => $formData['firstname'], $formData['lastname'], 'Strasse' => $formData['street'], 'Ort' => $formData['zip'] . ' ' . $formData['town'],
'Geburtstag' => $formData['birthDate'], 'Telefon' => $formData['phoneNumber'], 'Email' => $formData['email'],
'Name des Kindes' => $formData['childName'], 'Straße des Kindes' => $formData['childStreet'],
'Gewünschter Mitgliedsbeitrag' => $formData['payHeight'], 'Geldinstitut' => $formData['bankname'],
'IBAN' => $formData['iban'], 'BIC' => $formData['bic'], 'Kontoinhaber' => $formData['accountmembername'],
'Elektronischer Verarbeitung zugestimmg' => $formData['agreedElectronicalHandling']
];
$mail = $this->initSmtpMailer();
$mail->setFrom($formData['email'], $formData['firstname'] . ' ' . $formData['lastname']);
$mail->addReplyTo($formData['email'], $formData['firstname'] . ' ' . $formData['lastname']);
$mail->addAddress('foerderverein-ajs@gmx.de', 'Vorstand Förderverein AJS');
$message = '';
foreach ($data as $field => $value) {
$message .= $field . ': ' . $value . "\n";
}
$this->sendMail($mail, 'Mitgliedsantrag', $message, '');
$this->templateName = 'membership_success';
}
}
}

38
include/newmember.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
include 'renderer.php';
class Newmember extends Renderer {
protected array $formFields = [
['label' => 'Nachname', 'type' => 'text', 'size' => 50, 'name' => 'lastname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'last_name', 'optional' => false],
['label' => 'Vorname', 'type' => 'text', 'size' => 50, 'name' => 'firstname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'first_name', 'optional' => false],
['label' => 'Straße', 'type' => 'text', 'size' => 50, 'name' => 'streetname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'street', 'optional' => false],
['label' => 'Plz', 'type' => 'text', 'size' => 5, 'name' => 'zip', 'combine_with_next_line' => true, 'regex' => '/^([0-9]{5})$/', 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'zip', 'optional' => false],
['label' => 'Ort', 'type' => 'text', 'size' => 40, 'name' => 'town', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'town', 'optional' => false],
['label' => 'Birthday', 'type' => 'date', 'size' => 50, 'name' => 'birthdate', 'combine_with_next_line' => false, 'regex' => '/^((19|20)([0-9]){2}-[0-9]{2})-([0-9]{2})$/', 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'birthdate', 'optional' => false],
['label' => 'Phone', 'type' => 'text', 'size' => 50, 'name' => 'phone', 'combine_with_next_line' => false, '/^0([0-9]{2,6})([ ]{0,1})([-\/]{0,1})([ ]{0,1})([0-9 ]{4,25})$/', 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'phone', 'optional' => false],
['label' => 'Email', 'type' => 'email', 'size' => 50, 'name' => 'email', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_EMAIL, 'dbfield' => 'email', 'optional' => true],
['label' => 'Name des Kindes', 'type' => 'text', 'size' => 50, 'name' => 'childname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'child_name', 'optional' => true],
['label' => 'Straße des Kindes', 'type' => 'text', 'size' => 50, 'name' => 'childstreet', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'child_street', 'optional' => true],
['label' => 'Gewählter Beitrag', 'type' => 'number', 'size' => 50, 'name' => 'subscription', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'subscription', 'optional' => false],
['label' => 'Geldinstitut', 'type' => 'text', 'size' => 50, 'name' => 'bankname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'bank_name', 'optional' => true],
['label' => 'IBAN', 'type' => 'text', 'size' => 50, 'name' => 'iban', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'iban', 'optional' => true],
['label' => 'BIC', 'type' => 'text', 'size' => 50, 'name' => 'bic', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'bic', 'optional' => true],
['label' => 'Kontoinhaber', 'type' => 'text', 'size' => 50, 'name' => 'accountmember', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'account_member_name', 'optional' => true],
['label' => 'Mitgliedsstatus', 'type' => 'dbselect', 'size' => 0, 'name' => 'status', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'membership_status',
'sourcedb' => 'clubmember_status', 'optionfield' => 'status_text', 'encryption' => false],
['label' => 'Position im Verein', 'type' => 'dbselect', 'size' => 0, 'name' => 'position', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'position_id',
'sourcedb' => 'clubmember_position', 'optionfield' => 'description', 'encryption' => false, 'with_null_field' => true],
];
protected string $formSendButtonLabel = 'Eintragen';
protected string $dbTable = 'clubmember';
protected bool $dbGenerateSaltField = true;
protected function formAction(): void {
if (!$this->formCheckFields()) {
return;
}
$this->saveToDb();
$this->cleanFields = true;
$this->messages[] = 'Mitglied erfolgreich eingetragen.';
}
}

40
include/newpassword.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
include 'renderer.php';
class Newpassword extends Renderer {
protected array $formFields = [
['label' => 'Email-Adresse', 'type' => 'email', 'size' => 50, 'name' => 'email', 'combine_with_next_line' => false],
];
protected string $formSendButtonLabel = 'Login-Name zusenden und Paßwort-Reset anfordern';
protected function formAction(): void {
$email = trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL));
$query = 'SELECT id, realname, email, username, salt FROM user';
$dbResult = mysqli_query($this->dbConnection, $query);
while ($row = mysqli_fetch_assoc($dbResult)) {
if ($email === $this->decode($row['email'], $row['salt'])) {
$salt = $row['salt'];
$this->sendResetEmail($row['id'], $row['username'], $this->decode($row['email'], $salt), $this->decode($row['realname'], $salt));
break;
}
}
$this->templateName = 'newpassword_done';
}
protected function sendResetEmail(int $id, string $username, string $emailAddress, string $realName) {
$resetId = $this->generateRandomString();
$query = 'update user set recreate_db_hash="' . $resetId . '" where id=' . $id;
mysqli_query($this->dbConnection, $query);
$mail = $this->initSmtpMailer();
$mail->setFrom('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addReplyTo('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addAddress($emailAddress, $realName);
$this->sendMail($mail, 'Passwort zurücksetzen für ' . filter_input(INPUT_SERVER, 'SERVER_NAME'), $this->getEmailBody($username, $realName, $resetId), $this->generateSignature(''));
}
protected function getEmailBody(string $username, string $realname, string $code) {
$rawBody = file_get_contents('templates/resetaccountmailbody.html');
return str_replace(['{{name}}', '{{username}}', '{{server}}', '{{code}}', '{{protocol}}'],
[$realname, $username, filter_input(INPUT_SERVER, 'SERVER_NAME'), $code, (filter_input(INPUT_SERVER, 'HTTPS') ? 's' : '') ], $rawBody);
}
}

76
include/payings.php Normal file
View File

@@ -0,0 +1,76 @@
<?php
include 'renderer.php';
class Payings extends Renderer {
// ['label' => 'Nachname', 'type' => 'text', 'size' => 50, 'name' => 'lastname', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'last_name', 'optional' => false],
protected array $formFields = [
['label' => 'Datum', 'type' => 'date', 'size' => 10, 'name' => 'date', 'combine_with_next_line' => false,
'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'payment_date', 'optional' => false, 'encryption' => false],
['label' => 'Betrag', 'type' => 'number', 'size' => 10, 'name' => 'value', 'combine_with_next_line' => false,
'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'payment_height', 'optional' => false, 'encryption' => false],
['label' => '', 'type' => 'hidden', 'size'=> 1, 'name' => 'clubmember-id', 'value' => 0, 'combine_with_next_line' => false,
'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'clubmember_id', 'optional' => false, 'encryption' => false],
];
protected string $formSendButtonLabel = 'Zahlung eintragen';
protected string $dbTable = 'paying_history';
public function __construct(?string $templateName = null) {
parent::__construct($templateName);
$id = $this->getUriParams()['id'];
$this->formFields[2]['value'] = $id;
$this->loadUserData($id);
}
protected function loadUserData(string $id): void {
$query = sprintf("SELECT c.first_name, c.last_name, c.salt "
. "FROM clubmember c "
. "WHERE c.id = %d", $id);
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) === 0) {
$this->templateName = 'notfound_error';
return;
}
$line = mysqli_fetch_assoc($dbResult);
$salt = $line['salt'];
$this->content['member-name'] = $this->decode($line['last_name'], $salt) . ', ' . $this->decode($line['first_name'], $salt);
$this->predefines = $line;
}
protected function generateContent(): void {
$this->loadPayHistory($this->getUriParams()['id']);
}
protected function loadPayHistory(string $id): void {
$query = sprintf("SELECT ph.payment_date, ph.payment_height, u.realname, u.salt "
. "FROM paying_history ph "
. "JOIN `user` u "
. " ON u.id = ph.registered_by_id "
. "WHERE ph.clubmember_id = %d "
. "ORDER BY payment_date DESC", $id);
$dbResult = mysqli_query($this->dbConnection, $query);
$tableBody = '';
while ($row = mysqli_fetch_assoc($dbResult)) {
$tableBody .= '<tr>';
$tableBody .= '<td>' . $row['payment_date'] . '</td>';
$tableBody .= '<td>' . $row['payment_height'] . '</td>';
$tableBody .= '<td>' . $this->decode($row['realname'], $row['salt']) . '</td>';
$tableBody .= '</tr>';
}
$this->content['payings'] = $tableBody;
}
protected function formAction(): void {
if (!$this->formCheckFields()) {
return;
}
$this->saveToDb();
$this->cleanFields = true;
}
protected function createDbKeyValues(): array {
$data = parent::createDbKeyValues();
$data['keys'][] = '`registered_by_id`';
$data['values'][] = $_SESSION['userid'];
return $data;
}
}

137
include/planboard.php Normal file
View File

@@ -0,0 +1,137 @@
<?php
include 'renderer.php';
class Planboard extends Renderer {
public function __construct() {
parent::__construct();
}
protected function generateContent(): void {
$this->setOwnColor();
$this->setColorLegend();
$this->setContentTopics();
}
protected function setOwnColor(): void {
$query = sprintf('SELECT c.color
FROM `user` u
JOIN color c
ON c.id = u.color_id
WHERE u.id = %d', $_SESSION['userid']);
$dbResult = mysqli_query($this->dbConnection, $query);
$row = mysqli_fetch_assoc($dbResult);
$this->content['owncolor'] = (!$row) ? '000000' : $row['color'];
}
protected function setColorLegend(): void {
$legendData = [];
$query = 'SELECT c.color, c2.last_name, c2.first_name, u.realname, u.salt usalt, c2.salt csalt
FROM `user` u
JOIN color c
ON c.id = u.color_id
LEFT JOIN clubmember c2
ON c2.user_id = u.id
WHERE u.active = 1';
$dbResult = mysqli_query($this->dbConnection, $query);
while ($row = mysqli_fetch_assoc($dbResult)) {
$legendData[] = '<div style="color:#' . $row['color'] . '">' . (($row['last_name'])
? $this->decode($row['last_name'], $row['csalt']) . ', ' . $this->decode($row['first_name'], $row['csalt'])
: $this->decode($row['realname'], $row['usalt'])) . '</div>';
}
$this->content['colors'] = implode('', $legendData);
}
protected function setContentTopics(): void {
$topics = $this->getAllTopics();
$prerenderedTopics = [];
foreach ($topics as $topic) {
$prerenderedTopics[] = '<option value="' . $topic['id'] . '">' . $topic['title'] . '</option>';
}
$this->content['topics'] = implode('', $prerenderedTopics);
}
public function render(): void {
$action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING);
if ($action === 'generate') {
$this->generateDiscussion();
return;
}
if ($action === 'fetchtopic') {
$this->fetchTopic();
return;
}
if ($action === 'setshortdescription') {
$this->setShortDescription();
return;
}
if ($action == 'setdiscussion') {
$this->setDiscussion();
}
parent::render();
}
protected function generateDiscussion(): void {
$topicName = TRIM(filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING));
if ($topicName === '') {
echo '{"error": "Der Topic darf nicht leer sein."}';
return;
}
$query = sprintf('SELECT id
FROM discussion d
where title = "%s"', $topicName);
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) > 0) {
echo '{"error": "Der Topic existiert schon."}';
return;
}
$query = sprintf('INSERT INTO discussion (title, short_description, discussion) VALUES ("%s", "", "[]")', $topicName);
mysqli_query($this->dbConnection, $query);
$id = mysqli_insert_id($this->dbConnection);
$output = [
'topics' => $this->getAllTopics(),
'id' => $id,
];
echo json_encode($output);
}
protected function getAllTopics(): array {
$query = 'SELECT d.id, d.title
FROM discussion d
ORDER BY d.title ';
$dbResult = mysqli_query($this->dbConnection, $query);
$result = [];
while ($row = mysqli_fetch_assoc($dbResult)) {
$result[] = ['id' => $row['id'], 'title' => $row['title'] ];
}
return $result;
}
protected function fetchTopic(): void {
$query = sprintf('SELECT short_description, discussion
FROM discussion d
WHERE id = %d', TRIM(filter_input(INPUT_POST, 'id', FILTER_SANITIZE_STRING)));
$dbResult = mysqli_query($this->dbConnection, $query);
$row = mysqli_fetch_assoc($dbResult);
$result = [
'shortdescription' => ($row ? $row['short_description'] : ''),
'discussion' => ($row ? $row['discussion'] : '[]'),
];
echo json_encode($result);
}
protected function setShortDescription(): void {
$query = sprintf('UPDATE discussion SET short_description = "%s" WHERE id = %d',
filter_input(INPUT_POST, 'text', FILTER_SANITIZE_ADD_SLASHES),
filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT));
mysqli_query($this->dbConnection, $query);
echo '{"result":"success"}';
}
protected function setDiscussion(): void {
$query = sprintf('UPDATE discussion SET discussion = "%s" WHERE id = %d',
filter_input(INPUT_POST, 'text', FILTER_SANITIZE_ADD_SLASHES),
filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT));
mysqli_query($this->dbConnection, $query);
echo '{"result":"success"}';
}
}

31
include/projects.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
include 'renderer.php';
class Projects extends Renderer {
protected function generateContent(): void {
$query = 'SELECT p.short_title, p.description, pt.caption
FROM project p
JOIN project_type pt
ON pt.id = p.project_type_id
ORDER BY pt.order_id, p.short_title';
$dbResult = mysqli_query($this->dbConnection, $query);
$previousSection = '';
$output = '';
while ($row = mysqli_fetch_assoc($dbResult)) {
if ($row['caption'] != $previousSection) {
if ($output !== '') {
$output .= '</ul>';
}
$output .= '<h3>' . $row['caption'] . '</h3><ul>';
$previousSection = $row['caption'];
}
$output .= '<li>' . $row['short_title'];
if (trim($row['description']) != '') {
$output .= ' - ' . $row['description'];
}
$output .= '</li>';
}
$output .= '</ul>';
$this->content['projects'] = $output;
}
}

View File

@@ -0,0 +1,114 @@
<?php
include 'renderer.php';
class Projectsmanagement extends Renderer {
protected array $formFields = [
['label' => 'Projekt', 'type' => 'dbselect', 'size' => 0, 'name' => 'shorttitle', 'combine_with_next_line' => true, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'shorttitle',
'sourcedb' => 'project', 'optionfield' => 'short_title', 'encryption' => false, 'with_null_field' => true],
['label' => '', 'type' => 'text', 'size' => 20, 'name' => 'new_title', 'combine_with_next_line' => true, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'shorttitle',
'optional' => true],
['label' => '', 'type' => 'button', 'size' => 0, 'name' => 'addproject', 'combine_with_next_line' => false, 'filter' => false, 'text' => 'Projekt hinzufügen'],
['label' => 'Kurze Beschreibung', 'type' => 'text', 'size' => 50, 'name' => 'description', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_STRING, 'dbfield' => 'description',
'optionial' => true],
['label' => 'Projektart', 'type' => 'dbselect', 'size' => 0, 'name' => 'projecttype', 'combine_with_next_line' => false, 'filter' => FILTER_SANITIZE_NUMBER_INT, 'dbfield' => 'project_type_id',
'sourcedb' => 'project_type', 'optionfield' => 'caption', 'encryption' => false, 'with_null_field' => false],
];
protected bool $isAjaxForm = true;
public function render(): void {
$action = trim(filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING));
if ($action != '') {
switch ($action) {
case 'create':
$this->createProject();
break;
case 'getdetails':
$this->getDetails();
break;
case 'setdescription':
$this->setDescription();
break;
case 'setprojecttype':
$this->setProjectType();
break;
}
return;
}
parent::render();
}
protected function createProject(): void {
$newName = trim(filter_input(INPUT_POST, 'newname', FILTER_SANITIZE_STRING));
if ($newName == '') {
echo '{"error": "Jedes Projekt benötigt einen Namen."}';
return;
}
$query = sprintf('select id from project p where short_title = "%s"', $newName);
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) > 0) {
echo '{"error": "Ein Projekt mit dem Namen existiert bereits."}';
return;
}
$query = sprintf('INSERT INTO project (short_title, description, project_type_id)
SELECT "%s", "", id
FROM project_type p
WHERE caption = "Fortlaufende Projekte"', $newName);
mysqli_query($this->dbConnection, $query);
$id = mysqli_insert_id($this->dbConnection);
$query = 'SELECT id, short_title FROM project p ORDER BY short_title';
$dbResult = mysqli_query($this->dbConnection, $query);
$list = [];
while ($row = mysqli_fetch_assoc($dbResult)) {
$list[] = ['id' => $row['id'], 'title' => $row['short_title'] ];
}
echo json_encode(['list' => $list, 'id' => $id]);
}
protected function setDescription(): void {
$newDescription = trim(filter_input(INPUT_POST, 'description', FILTER_SANITIZE_ADD_SLASHES));
$query = sprintf('UPDATE project SET description = "%s" WHERE id = "%d"', $newDescription, filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT));
mysqli_query($this->dbConnection, $query);
echo '{}';
}
protected function getDetails(): void {
$query = sprintf('SELECT description, project_type_id FROM project WHERE id = %d', filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT));
$dbResult = mysqli_query($this->dbConnection, $query);
if ($row = mysqli_fetch_assoc($dbResult)) {
echo json_encode(['description' => $row['description'], 'projecttype' => $row['project_type_id']]);
} else {
echo json_encode(['description' => '', 'projecttype' => '1']);
}
}
protected function setProjectType(): void {
$newProjectType = trim(filter_input(INPUT_POST, 'newtype', FILTER_SANITIZE_NUMBER_INT));
$query = sprintf('UPDATE project SET project_type_id = "%d" WHERE id = "%d"', $newProjectType, filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT));
mysqli_query($this->dbConnection, $query);
echo '{}';
}
protected function generateContent(): void {
$typesQuery = 'SELECT * FROM project_type ORDER BY id';
$typesResult = mysqli_query($this->dbConnection, $typesQuery);
$types = [];
while ($row = mysqli_fetch_assoc($typesResult)) {
$types[$row['id'] ] = $row['caption'];
}
$query = 'SELECT * FROM project ORDER BY short_title';
$result = mysqli_query($this->dbConnection, $query);
$overviewHtml = '<table><thead><tr><th>Projekt</th><th>Projekttyp</th><tr><thead><tbody>';
while ($row = mysqli_fetch_assoc($result)) {
$overviewHtml .= '<tr><td>' . $row['short_title'] . '</td>';
$overviewHtml .= '<td><select name="project_type" data="' . $row['id'] . '">';
foreach ($types as $id => $type) {
$overviewHtml .= '<option value="' . $id . '"' . ($id == $row['project_type_id'] ? ' selected' : '') . '>' . $type . '</option>';
}
$overviewHtml .= '</select></td>';
$overviewHtml .= '</tr>';
}
$overviewHtml .= '</tbody></table>';
$this->content['projects'] = $overviewHtml;
}
}

76
include/register.php Normal file
View File

@@ -0,0 +1,76 @@
<?php
include 'renderer.php';
class Register extends Renderer {
protected array $formFields = [
['label' => 'Gewünscher Benutzername', 'type' => 'text', 'size' => 50, 'name' => 'username', 'combine_with_next_line' => false],
['label' => 'Vollständiger Name', 'type' => 'text', 'size' => 50, 'name' => 'fullname', 'combine_with_next_line' => false],
['label' => 'Email-Adresse', 'type' => 'email', 'size' => 50, 'name' => 'email', 'combine_with_next_line' => false],
['label' => 'Gewünschtes Paßwort', 'type' => 'password', 'size' => 50, 'name' => 'password', 'combine_with_next_line' => false],
['label' => 'Paßwort wiederholen', 'type' => 'password', 'size' => 50, 'name' => 'password2', 'combine_with_next_line' => false],
['label' => 'Ich stimme der Speicherung meiner Daten zu.', 'type' => 'checkbox', 'size' => 50, 'name' => 'accept', 'combine_with_next_line' => false,
'value' => 1],
];
protected string $formSendButtonLabel = 'Zugang beantragen';
protected function formAction(): void {
$this->writeToDb();
$this->sendEmail();
$this->templateName = 'register_successful';
}
protected function formCheckFields(): bool {
$username = trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES));
$user = mysqli_query($this->dbConnection, 'SELECT * FROM user WHERE `username` = lower("' . $username . '")');
if ($user->num_rows !== 0) {
$this->errors[] = 'Der Benutzername existiert bereits.';
}
if (!preg_match('/^([a-z0-9]{3,16})$/', $username)) {
$this->errors['username'] = 'Der Benutzername darf nur aus Buchstaben (ohne Umlaute) und Zahlen bestehen und muss zwischen drei und sechzen Zeichen lang sein.';
}
if (!filter_var(strtolower(trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL)), FILTER_VALIDATE_EMAIL))) {
$this->errors['email'] = 'Die Email-Adresse ist inkorrekt';
}
if (strlen(filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING)) < 8) {
$this->errors['password'] = 'Das gewählte Paßwort ist zu kurz (Minimum: 8 Zeichen).';
}
if (filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING) !== filter_input(INPUT_POST, 'password2', FILTER_SANITIZE_STRING)) {
$this->errors['password2'] = 'Die Paßwörter stimmen nicht überein.';
}
if (filter_input(INPUT_POST, 'accept', FILTER_SANITIZE_NUMBER_INT) !== '1') {
$this->errors['accept'] = 'Sie müssen der Speicherung Ihrer Daten zustimmen.';
}
return (count($errors) === 0);
}
protected function writeToDb(): void {
$salt = $this->generateRandomString();
$encryptedName = $this->encode(trim(filter_input(INPUT_POST, 'fullname', FILTER_SANITIZE_STRING), $salt));
$email = strtolower(trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL)));
$encryptedEmail = $this->encode($email, $salt);
$query = sprintf("INSERT INTO ffajs.`user` (username, password, realname, email, active, save_data_accepted, salt, color_id) "
. "VALUES('%s', '%s', '%s', '%s', 0, %d, '%s', (SELECT c.id
FROM color c
left join `user` u
on u.color_id = c.id
where u.id is null
order by rand()
limit 1))",
strtolower(trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING))),
password_hash(filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING), PASSWORD_DEFAULT),
$encryptedName,
$encryptedEmail,
filter_input(INPUT_POST, 'accept', FILTER_SANITIZE_NUMBER_INT) ?: 0,
$salt);
mysqli_query($this->dbConnection, $query);
}
protected function sendEmail(): void {
$mail = $this->initSmtpMailer();
$mail->setFrom('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addReplyTo('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$mail->addAddress('foerderverein-ajs@gmx.de', 'Förderverein der August-Jaspert-Schule');
$message = 'Ein neuer Antrag auf Benutzerzugang wurde gestellt';
$this->sendMail($mail, 'Zugang zu internem Bereich beantragt', $message, '');
}
}

517
include/renderer.php Normal file
View File

@@ -0,0 +1,517 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
use PhpImap\Mailbox;
require 'vendor/autoload.php';
class Renderer {
protected string $templateName;
private array $menuItems = [];
protected array $internalMenuItems = [];
protected $hiddenCount = 0;
protected array $predefines = [];
protected array $errors = [];
protected array $formFields = [];
protected string $formSendButtonLabel = '';
protected bool $isAjaxForm = false;
protected string $encType = 'application/x-www-form-urlencoded';
protected $dbConnection;
protected string $encryptionKey = 'CXOoBDbt0qjJ6zlTILGVsFIZTE0aTZUC';
protected array $content = [];
protected string $dbTable = '';
protected bool $dbGenerateSaltField = false;
protected bool $dbUpdate = false;
protected bool $cleanFields = false;
protected array $messages = [];
protected string $salt = '';
private string $website = '';
protected bool $saveAllFields = false;
protected string $imapServer = 'imap.gmx.net';
protected int $imapPort = 993;
protected string $smtpServer = 'mail.gmx.net';
protected int $smtpPort = 465;
protected string $emailUser = 'foerderverein-ajs@gmx.de';
protected string $emailPassword = 'HarheimerWeg16';
protected $mbox;
public function __construct(?string $templateName = null) {
session_start();
$this->createPublicMenuItems();
$this->createInternalMenuItems();
if (!isset($_SESSION['userid'])) {
$_SESSION['userid'] = 0;
}
$scriptName = $this->getScriptName();
if (!in_array($scriptName, array_merge($this->menuItems, $this->internalMenuItems))) {
header('Location: /', true, 301);
return;
}
$this->templateName = $templateName ?: (!in_array($scriptName, ['ffajs', 'fvajs', '', '/']) ? $scriptName : 'index');
$this->connectDb();
}
private function createPublicMenuItems(): void {
$this->menuItems = $this->getMenuItemsFromFile('publicpages');
}
private function createInternalMenuItems(): void {
$this->internalMenuItems = $this->getMenuItemsFromFile('internalpages');
}
private function getMenuItemsFromFile(string $fileName): array {
$items = json_decode(file_get_contents('conf/' . $fileName . '.conf'));
$menu = [];
foreach ($items as $url => $caption) {
$menu[($caption !== '' ? $caption : '$hidden:0' . $this->hiddenCount++)] = $url;
}
return $menu;
}
public function getScriptName(): string {
return $this->getSplittedUri()[0];
}
public function getUrl(): string {
return filter_input(INPUT_SERVER, 'REQUEST_URI');
}
protected function getUriParams(): array {
$paramPairs = explode('&', $this->getSplittedUri()[1]);
$result = [];
foreach ($paramPairs as $paramPair) {
$pair = explode('=', $paramPair);
if (count($pair) === 2) {
$result[$pair[0]] = $pair[1];
}
}
return $result;
}
protected function getSplittedUri(): array {
$scriptNameParts = pathinfo(filter_input(INPUT_SERVER, 'REQUEST_URI'));
$uri = $scriptNameParts['filename'] ?: 'index';
return explode('?', $uri);
}
private function connectDb(): void {
$user = 'ffajs';
$password = 'BpAJSis1999xx';
$host = 'tsschulz.de';
$port = 3306;
$db = 'ffajs';
$this->dbConnection = mysqli_connect($host, $user, $password, $db, $port);
}
public function render(): void {
if (trim(filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING)) !== '') {
$this->formAction();
}
$this->website = file_get_contents('templates/page.html');
$this->header();
$this->menu();
if ($_SESSION['userid'] === 0 && in_array($this->getScriptName() , $this->internalMenuItems, false)) {
$this->templateName = 'login_error';
}
$this->body();
$this->footer();
echo $this->website;
}
private function header(): void {
$this->website = str_replace('{{header}}', file_get_contents('templates/header.html'), $this->website);
}
private function menu(): void {
$menu = '<menu>';
$scriptName = $this->getScriptName();
foreach ($this->menuItems as $page => $link) {
if (substr($page, 0, 8) === '$hidden:') {
continue;
}
$menu .= '<a href="' . $link . '"' . ($link === $scriptName ? 'class="active"' : '') . '>' . $page . '</a>';
}
if (isset($_SESSION) && $_SESSION['userid'] === 0) {
$menu .= '<a href="login" class="login"><img src="' . (filter_input(INPUT_SERVER, 'HTTP_HOST') == 'localhost' ? '/ffajs' : '') .
'/images/icons/key.png" class="menu-icon" alt="login" /></a>';
} else {
$menu .= '<div class="internal-menu-main"><span class="intern">Verwaltung</span><div class="internal-menu-sub">';
foreach ($this->internalMenuItems as $page => $link) {
if (substr($page, 0, 8) === '$hidden:') {
continue;
}
$menu .= '<a class="intern" href="' . $link . '"' . ($link === $scriptName ? 'class="active"' : '') . '>' . $page . '</a>';
}
$menu .= '</div></div>';
$menu .= '<a href="logout" class="login"><img src="' . (filter_input(INPUT_SERVER, 'HTTP_HOST') == 'localhost' ? '/ffajs' : '') .
'/images/icons/key.png" class="menu-icon" alt="logout" /></a>';
}
$menu .= '</menu>';
$this->website = str_replace('{{menu}}', $menu, $this->website);
}
protected function body(): void {
$this->generateContent();
$rawContent = file_get_contents('templates/' . $this->templateName . '.html');
$errorHtml = count($this->errors) > 0
? '<div class="error">' . implode('<br />', $this->errors) . '</div>'
: '';
$messagesHtml = count($this->messages) > 0
? '<div class="messages">' . implode('<br />', $this->messages) . '</div>'
: '';
$placeholders = ['{{errors}}', '{{form}}', '{{messages}}'];
$renderedContent = [$errorHtml, $this->renderForm(), $messagesHtml];
foreach ($this->content as $placeholder => $html) {
$placeholders[] = '{{' . $placeholder . '}}';
$renderedContent[] = $html;
}
$content = str_replace($placeholders, $renderedContent, $rawContent);
$this->website = str_replace('{{content}}', $content, $this->website);
}
private function footer(): void {
$this->website = str_replace('{{footer}}', file_get_contents('templates/footer.html'), $this->website);
}
protected function showInputField(array $errors, string $inputType, string $fieldName, int $fieldLength): void {
echo '<input type="' . $inputType . '" name="' . $fieldName . '" size="' . $fieldLength . '" value="' . filter_input(INPUT_POST, $fieldName, FILTER_SANITIZE_STRING) . '" />';
if (isset($errors[$fieldName])) {
echo '<span class="error">' . $errors[$fieldName] . '</span>';
}
}
protected function renderForm(): string {
$form = '<form method="post" action="' . $this->getUrl() . '" enctype="' . $this->encType . '"><table class="form">';
$label = '';
$input = '';
$error = '';
for ($formFieldIndex = 0; $formFieldIndex < count($this->formFields); ++$formFieldIndex) {
$newLabelExtension = $this->renderLabel($formFieldIndex);
$label .= (strlen($label) > 0 && strlen($newLabelExtension) > 0 ? ' / ' : '') . $newLabelExtension;
$input .= $this->renderInput($formFieldIndex);
if (isset($this->formFields[$formFieldIndex]['name']) && isset($this->errors[$this->formFields[$formFieldIndex]['name'] ])) {
$error .= '<span class="error">' . $this->errors[$this->formFields[$formFieldIndex]['name'] ] . '</span>';
}
if (!isset($this->formFields[$formFieldIndex]['combine_with_next_line'] ) || !$this->formFields[$formFieldIndex]['combine_with_next_line']) {
$form .= '<tr><th>' . $label . '</th><td>' . $input . '</td><td>' . $error . '</td></tr>';
$label = '';
$input = '';
$error = '';
}
}
$form .= '</table>';
if (!$this->isAjaxForm) {
$form .= '<button type="submit" name="action" value="submit">' . $this->formSendButtonLabel . '</button>';
}
$form .= '<button type="reset">Zurücksetzen</button>';
$form .= '</form>';
return $form;
}
protected function renderLabel(int $index): string {
if (!isset($this->formFields[$index]['name']) && !isset($this->formFields[$index]['label'])) {
return '';
} elseif (isset($this->formFields[$index]['label'])) {
return $this->formFields[$index]['label'];
}
return '<label for="' . $this->formFields[$index]['name'] . '">' . $this->formFields[$index]['label'] . '</label>';
}
protected function renderInput(int $index): string {
if ($this->formFields[$index]['type'] === 'combobox') {
return $this->renderCombobox($index);
}
if ($this->formFields[$index]['type'] === 'textarea') {
return $this->renderTextarea($index);
}
if ($this->formFields[$index]['type'] === 'infotext') {
return '';
}
if ($this->formFields[$index]['type'] === 'dbselect') {
return $this->renderDbSelect($index);
}
if ($this->formFields[$index]['type'] === 'spacer') {
return '&nbsp;';
}
if ($this->formFields[$index]['type'] === 'button') {
return $this->renderButton($index);
}
return $this->renderStandardInput($index);
}
protected function renderCombobox(int $index): string {
$inputField = '<select size="1" id="' . $this->formFields[$index]['name']
. '" name="' . $this->formFields[$index]['name'] . '" size="' . $this->formFields[$index]['size'] . '"'
. ' value="' . $this->inputValue($index) . '">';
$inputValue = $this->inputValue($index);
foreach ($this->formFields[$index]['values'] as $value) {
$inputField .= '<option value="' . $value . '"';
if ($value == $inputValue || (isset($this->formFields[$index]['default']) && $inputValue == '' && $value == $this->formFields[$index]['default'])) {
$inputField .= ' selected';
}
$inputField .= '>' . $value . '</option>';
}
$inputField .= '</select>';
return $inputField;
}
protected function renderStandardInput(int $index): string {
return '<input type="' . $this->formFields[$index]['type'] . '" id="' . $this->formFields[$index]['name'] .
'" name="' . $this->formFields[$index]['name'] . '" size="' . $this->formFields[$index]['size'] . '"' .
' value="' . $this->inputValue($index) . '" />';
}
protected function renderTextarea(int $index): string {
return '<textarea id="' . $this->formFields[$index]['name'] .'" '
. 'name="' . $this->formFields[$index]['name'] . '" cols="' . $this->formFields[$index]['cols'] . '" '
. 'rows="' . $this->formFields[$index]['rows'] . '">' . $this->inputValue($index) . '</textarea>';
}
protected function renderDbSelect(int $index): string {
$query = 'SELECT `id`, `' . $this->formFields[$index]['optionfield'] . '` as label FROM `' . $this->formFields[$index]['sourcedb'] . '` ORDER BY `id`';
$dbResult = mysqli_query($this->dbConnection, $query);
$inputValue = $this->inputValue($index);
$selectField = '<select id="' . $this->formFields[$index]['name'] . '" '
. 'name="' . $this->formFields[$index]['name'] . '">';
if (isset($this->formFields[$index]['with_null_field']) && $this->formFields[$index]['with_null_field'] === true) {
$selectField .= '<option value="NULL">---</option>';
}
while ($row = mysqli_fetch_assoc($dbResult)) {
$selectField .= '<option value="' . $row['id'] . '"';
if ($row['id'] == $inputValue) {
$selectField .= ' selected';
}
$selectField .= '>' . $row['label'] . '</option>';
}
$selectField .= '</select>';
return $selectField;
}
protected function renderButton(int $index): string {
return '<button id="' . $this->formFields[$index]['name'] . '" name="' . $this->formFields[$index]['name'] . '">' . $this->formFields[$index]['text'] . '</button>';
}
protected function inputValue($index): string {
if (isset($this->formFields[$index]['value'])) {
return $this->formFields[$index]['value'];
} elseif ($this->cleanFields) {
return '';
}
$value = filter_input(INPUT_POST, $this->formFields[$index]['name'], FILTER_SANITIZE_STRING) ?: '';
if (trim($value) === '' && isset($this->predefines[$this->formFields[$index]['name']])) {
$value = $this->predefines[$this->formFields[$index]['name']];
}
return $value;
}
protected function formAction(): void {
}
protected function formCheckFields(): bool {
foreach ($this->formFields as $field) {
$value = filter_input(INPUT_POST, $field['name'], isset($field['filter']) ? $field['filter'] : FILTER_SANITIZE_STRING);
if (isset($field['optional']) && ($field['optional'] == false) && trim($value) === '' && $field['type'] !== 'file') {
$this->errors[$field['name']] = 'Das Feld darf nicht leer sein';
continue;
}
if (isset($field['regex']) && !preg_match($field['regex'], $value)) {
$this->errors[$field['name']] = 'Inkorrekte eingabe';
}
}
return count($this->errors) === 0;
}
protected function createDbInsert(): string {
$preparedFieldData = $this->createDbKeyValues();
return 'INSERT INTO `' . $this->dbTable . '` (' . implode(', ', $preparedFieldData['keys'])
. ') VALUES (' . implode(', ', $preparedFieldData['values']) . ')';
}
protected function createKeyValuePairsForUpdate(array $data): string {
$resultArray = [];
$numEntries = count($data['keys']);
for ($i = 0; $i < $numEntries; ++$i) {
$resultArray[] = $data['keys'][$i] . ' = ' . $data['values'][$i];
}
return implode(', ', $resultArray);
}
protected function createDbUpdate(): string {
$preparedFieldData = $this->createDbKeyValues();
$query = 'UPDATE `' . $this->dbTable . '` SET ';
$query .= $this->createKeyValuePairsForUpdate($preparedFieldData);
$query .= ' WHERE `id` = ' . $this->getUriParams()['id'];
return $query;
}
protected function saveToDb(): void {
$query = $this->dbUpdate
? $this->createDbUpdate()
: $this->createDbInsert();
mysqli_query($this->dbConnection, $query);
}
protected function createDbKeyValues(): array {
$keysArray = [];
$valuesArray = [];
$salt = $this->salt != '' ? $this->salt : $this->generateRandomString();
foreach ($this->formFields as $field) {
if (!$this->saveAllFields && isset($field['optional']) && $field['optional'] === true && trim(filter_input(INPUT_POST, $field['name'], $field['filter'])) === '') {
continue;
}
$keysArray[] = '`' . $field['dbfield'] . '`';
if (isset($field['with_null_field']) && filter_input(INPUT_POST, $field['name']) === 'NULL') {
$valuesArray[] = 'NULL';
} else {
$rawValue = filter_input(INPUT_POST, $field['name'], $field['filter']);
$dbValue = $rawValue === '' || (isset($field['encryption']) && $field['encryption'] === false) ? $rawValue : $this->encode($rawValue, $salt);
$valuesArray[] = '"' . $dbValue . '"';
}
}
if ($this->dbGenerateSaltField) {
$keysArray[] = '`salt`';
$valuesArray[] = '"' . $salt . '"';
}
return ['keys' => $keysArray, 'values' => $valuesArray];
}
protected function getDbEncryptedValueIfNeeded(array $formData, string $fieldName, string $salt): mixed {
$formField = $this->getFormField($fieldName);
if (count($formField) !== 0) {
if (!$this->fieldHasToBeEncrypted($fieldName)) {
return $formData[$fieldName];
}
}
return $this->encode($formData[$fieldName], $salt);
}
protected function getFormField($fieldName): array {
foreach ($this->formFields as $formField) {
if (!isset($formField['name'])) {
continue;
}
if ($formField['name'] === $fieldName) {
return $formField;
}
}
return [];
}
protected function fieldHasToBeEncrypted(string $fieldName): bool {
$formField = $this->getFormField($fieldName);
return !isset($formField['encryption']) || $formField['encryption'] !== false;
}
protected function generateContent(): void {
}
protected function generateRandomString($length = 16) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
protected function encode(string $toEncode, string $salt): string {
return openssl_encrypt($toEncode, 'aes-256-cbc', $this->encryptionKey, 0, $salt);
}
protected function decode(string $toDecode, string $salt): string {
return openssl_decrypt($toDecode, 'aes-256-cbc', $this->encryptionKey, 0, $salt);
}
protected function initSmtpMailer(): PHPMailer {
$mail = new PHPMailer(true);
$mail->SMTPDebug = SMTP::DEBUG_OFF;
$mail->isSMTP();
$mail->Host = $this->smtpServer;
$mail->Port = $this->smtpPort;
$mail->SMTPAuth = true;
$mail->Username = $this->emailUser;
$mail->Password = $this->emailPassword;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->isHTML(true);
return $mail;
}
protected function sendMail(PHPMailer $mail, string $subject, string $body, string $signature): void {
$completeBody = $body . $signature . $this->emailLegalInformation();
$mail->Subject = $subject;
$mail->Body = $completeBody;
$mail->AltBody = 'Diese Email benötigt HTML-Ansicht';
$mail->send();
}
protected function connectToImap(): bool {
$this->mbox = new PhpImap\Mailbox(
'{' . $this->imapServer . ':' . $this->imapPort . '/imap/ssl}INBOX',
$this->emailUser,
$this->emailPassword,
__DIR__,
'UTF-8',
true,
false
);
$this->mbox->setConnectionArgs(
CL_EXPUNGE,
3,
[]
);
/* $this->mbox = imap_open ("{" . $this->imapServer . ":" . $this->imapPort . "/imap/ssl}INBOX", $this->emailUser, $this->emailPassword);
if ($this->mbox === false) {
$errors = imap_errors();
$this->errors = is_array($errors) ? $errors : [$errors];
}*/
return ($this->mbox !== false);
}
protected function saveFileLocal(string $newFileName, string $content, string $salt): void {
$encodedFile = $this->encode($content, $salt);
file_put_contents('/var/shared/fvajs/' . $newFileName, $encodedFile);
}
protected function emailLegalInformation(): string {
return '';
}
protected function generateSignature(string $signatureType): string {
$sender = ($signatureType === 'Persönliche Signatur') ? $this->generatePersonalSignature() : 'Der Vorstand';
return '<p>-------------<br/>'
. $sender .'<br/><br/>'
. 'Verein der Freunde und F&ouml;rderer der August-Jaspert-Schule e.V.<br/>'
. 'Harheimer Weg 16<br/>'
. '60437 Frankfurt<br/>'
. 'Email: foerderverein-ajs@gmx.de<br/>'
. 'Homepage: https://fvajs.de<br/>'
. 'Registergerich: Amtsgerich Frankfurt a.M. <Registernummer>'
. 'Vertretungsberechtigt: Lucas Fastabend, Alexandra <nachname>, Torsten Schulz'
. '</p>';
}
protected function generatePersonalSignature(): string {
$query = sprintf('SELECT c.first_name, c.last_name, c.salt, cp.description
FROM clubmember c
JOIN `user` u
ON u.id = c.user_id
LEFT JOIN clubmember_position cp
ON cp.id = c.position_id
WHERE u.id = %d', $_SESSION['userid']);
$dbResult = mysqli_query($this->dbConnection, $query);
$row = mysqli_fetch_assoc($dbResult);
$name = $this->decode($row['first_name'], $row['salt']) . ' ' . $this->decode($row['last_name'], $row['salt']);
if (trim($row['description']) === '') {
return name;
}
return htmlspecialchars($name . ' (' . $row['description'] . ')');
}
}

71
include/savemail.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
require 'mailhandling.php';
require 'vendor/autoload.php';
class Savemail extends Mailhandling {
// protected $mbox = null;
protected $uid = 0;
public function __construct(?string $templateName = null) {
parent::__construct();
if (!$this->connectToImap()) {
$this->templateName = 'imaperror';
return;
}
$this->uid = $this->getUriParams()['uid'];
$this->type = $this->getUriParams()['type'];
$this->content['uid'] = $this->uid;
if ($this->type === 'content') {
$this->saveEmailBody();
}
}
protected function saveEmailBody(): void {
$messageStructure = imap_fetchstructure($this->mbox, $this->uid);
$this->fetchEmailHeader($this->content);
$this->fetchEmailBody($messageStructure, $this->content);
$content = $this->generateBodyContent();
$newFileName = $this->generateRandomString(64);
$salt = $this->generateRandomString();
$breaks = array("<br />","<br>","<br/>");
$rerenderedContent = str_ireplace($breaks, "\r\n", $content);
$this->saveFileLocal($newFileName, $rerenderedContent, $salt);
$this->generateDocumentTitle();
$query = sprintf('INSERT INTO ffajs.document
(title, original_filename, local_filename, salt)
VALUES("%s", "%s", "%s", "%s")', $this->content['saved-title'],
$this->content['saved-title'],
$newFileName, $salt);
mysqli_query($this->dbConnection, $query);
}
protected function generateBodyContent(): string {
return 'Von: ' . $this->content['sender'] . "\n"
. 'An: ' . $this->content['receiver'] . "\n"
. 'Datum: ' . $this->content['senddate'] . "\n"
. 'Betreff: ' . $this->content['subject'] . "\n\n"
. $this->content['emailbody'];
}
protected function generateDocumentTitle(): void {
$originalSubject = $this->content['subject'];
$count = 0;
$found = false;
do {
$query = sprintf('SELECT id
FROM document d
WHERE lower(trim(d.title)) = lower(trim("%s"))', $originalSubject);
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) > 0) {
$found = true;
$counter = 0;
if (preg_match('/(.*):(\d+)$/', trim($originalSubject))) {
$counter = preg_split('/(.*):(\d+)$/', trim($originalSubject));
$originalSubject = preg_replace('/(.*):(\d+)$/', '{1}', $originalSubject);
}
$originalSubject .= ':' . (++$counter);
}
} while ($found === true && ($count++ < 10));
$this->content['saved-title'] = $originalSubject;
}
}

52
include/setpassword.php Normal file
View File

@@ -0,0 +1,52 @@
<?php
include 'renderer.php';
class Setpassword extends Renderer {
protected array $formFields = [
['label' => 'Benutzername', 'type' => 'text', 'size' => 50, 'name' => 'username', 'combine_with_next_line' => false],
['label' => '', 'type' => 'hidden', 'name' => 'code', 'combine_with_next_line' => false, 'size' => 50],
['label' => 'Neues Paßwort', 'type' => 'password', 'name' => 'newpassword1', 'combine_with_next_line' => false, 'size' => 50],
['label' => 'Paßwort wiederolen', 'type' => 'password', 'name' => 'newpassword2', 'combine_with_next_line' => false, 'size' => 50],
];
protected string $formSendButtonLabel = 'Neues Paßwort setzen';
protected array $errors = [];
protected function generateContent(): void {
$this->formFields[1]['value'] = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
}
protected function formAction(): void {
if (!$this->formCheckFields()) {
return;
}
$query = 'UPDATE user SET password="' . password_hash(filter_input(INPUT_POST, 'newpassword1', FILTER_SANITIZE_STRING), PASSWORD_DEFAULT) . '", recreate_db_hash = NULL ' .
'WHERE username="' . trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES)) . '"';
mysqli_query($this->dbConnection, $query);
$this->templateName = 'passwordresettet';
}
protected function formCheckFields(): bool {
$userName = trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_ADD_SLASHES));
if (!preg_match('/^([a-z0-9]{3,16})$/', $userName)) {
$this->errors['username'] = 'Der Benutzername darf nur aus Buchstaben (ohne Umlaute) und Zahlen bestehen und muss zwischen drei und sechzen Zeichen lang sein.';
} else {
$query = 'SELECT id, recreate_db_hash FROM user WHERE username="' . $userName . '"';
$dbResult = mysqli_query($this->dbConnection, $query);
if (mysqli_num_rows($dbResult) == 0) {
$this->errors['username'] = 'Der Benutzername ist nicht vergeben';
} else {
$row = mysqli_fetch_assoc($dbResult);
if ($row['recreate_db_hash'] !== filter_input(INPUT_POST, 'code', FILTER_SANITIZE_STRING)) {
$this->errors[] = 'Ungültige Anfrage. Bitte beginnen Sie den Vorgan von vorne.';
}
}
}
if (strlen(filter_input(INPUT_POST, 'newpassword1', FILTER_SANITIZE_STRING)) < 8) {
$this->errors['newpassword1'] = 'Das gewählte Paßwort ist zu kurz (Minimum: 8 Zeichen).';
}
if (filter_input(INPUT_POST, 'newpassword2', FILTER_SANITIZE_STRING) !== filter_input(INPUT_POST, 'newpassword1', FILTER_SANITIZE_STRING)) {
$this->errors['newpassword2'] = 'Die Paßwörter stimmen nicht überein.';
}
return (count($this->errors) === 0);
}
}