News #3
14 changed files with 428 additions and 8 deletions
37
app/controllers/NewsController.php
Normal file
37
app/controllers/NewsController.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use app\models\NewsModel;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use framework\Controller;
|
||||
use Parsedown;
|
||||
|
||||
class NewsController extends Controller
|
||||
{
|
||||
static string $tab = 'news';
|
||||
|
||||
private array $weekdays = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
private array $months = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"];
|
||||
|
||||
public function index(): string
|
||||
{
|
||||
$news = NewsModel::lastFive();
|
||||
|
||||
$parser = new Parsedown();
|
||||
$parser->setSafeMode(true);
|
||||
|
||||
foreach ($news as $item) {
|
||||
$item->content = $parser->parse($item->content);
|
||||
try {
|
||||
$dt = new DateTime($item->date);
|
||||
// Format Mo 7. Nov
|
||||
$item->date = $this->weekdays[$dt->format("N") - 1] . " " . $dt->format("j. ") . $this->months[$dt->format("n") - 1];
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $this->view('news', ["news" => $news]);
|
||||
}
|
||||
}
|
||||
19
app/models/NewsModel.php
Normal file
19
app/models/NewsModel.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use framework\Collection;
|
||||
use framework\database\Database;
|
||||
use framework\database\Model;
|
||||
use framework\database\Query;
|
||||
|
||||
class NewsModel extends Model
|
||||
{
|
||||
public static string $tableName = "News";
|
||||
|
||||
static function lastFive(): Collection
|
||||
{
|
||||
$table = self::tableName();
|
||||
return self::arrayToModel(Database::query(new Query("SELECT * FROM `$table` ORDER BY `date` DESC LIMIT 5")));
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
},
|
||||
"require": {
|
||||
"twig/twig": "^3.4",
|
||||
"miladrahimi/phprouter": "^5.1"
|
||||
"miladrahimi/phprouter": "^5.1",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"ext-pdo": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
52
composer.lock
generated
52
composer.lock
generated
|
|
@ -4,8 +4,58 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "aeb8850dadf0ed622694b70b23f24518",
|
||||
"content-hash": "9ce76971c8555afa913cadbcffbebd26",
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.7.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erusev/parsedown.git",
|
||||
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
|
||||
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Parsedown": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuil Rusev",
|
||||
"email": "hello@erusev.com",
|
||||
"homepage": "http://erusev.com"
|
||||
}
|
||||
],
|
||||
"description": "Parser for Markdown.",
|
||||
"homepage": "http://parsedown.org",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/erusev/parsedown/issues",
|
||||
"source": "https://github.com/erusev/parsedown/tree/1.7.x"
|
||||
},
|
||||
"time": "2019-12-30T22:54:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-diactoros",
|
||||
"version": "2.17.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
// https://github.com/miladrahimi/phprouter
|
||||
|
||||
use app\controllers\NewsController;
|
||||
use app\controllers\PGPController;
|
||||
use app\controllers\APIController;
|
||||
use framework\Router;
|
||||
|
|
@ -11,6 +12,8 @@ $router = Router::create();
|
|||
$router->view('/', 'index', 'home');
|
||||
$router->view('/impressum', 'impressum');
|
||||
|
||||
$router->get('/news', [NewsController::class, 'index']);
|
||||
|
||||
$router->get('/pgp/?', [PGPController::class, 'index']);
|
||||
$router->get('/pgp/{email}', [PGPController::class, 'mail']);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,12 @@
|
|||
@apply font-medium text-xl mb-2 dark:text-slate-200
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply font-medium text-lg mb-1 dark:text-slate-200
|
||||
}
|
||||
|
||||
p {
|
||||
@apply mt-2 text-lg
|
||||
|
||||
}
|
||||
|
||||
code {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
|
@ -58,12 +59,15 @@
|
|||
</svg>
|
||||
<span>Startseite</span>
|
||||
</a>
|
||||
<a href="https://schule.moritzhuber.de" class="menu-item noline">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 15c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1-8h-2V7h2v2z"/>
|
||||
<a href="/news" class="menu-item noline {% if tab == "news" %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<g fill="none">
|
||||
<path d="M0 0h24v24H0z"/>
|
||||
<path d="M0 0h24v24H0z"/>
|
||||
</g>
|
||||
<path d="m21.15 3.85-.82.82-.95-.96c-.39-.39-1.02-.39-1.42 0l-.96.96-.96-.96c-.39-.39-1.03-.39-1.42 0l-.95.96-.96-.96a.996.996 0 0 0-1.41 0l-.96.96-.96-.96c-.39-.39-1.02-.39-1.42 0L7 4.67l-.96-.96c-.39-.39-1.03-.39-1.42 0l-.95.96-.82-.82a.5.5 0 0 0-.85.36V19c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4.21a.5.5 0 0 0-.85-.36zM11 19H4v-6h7v6zm9 0h-7v-2h7v2zm0-4h-7v-2h7v2zm0-4H4V8h16v3z"/>
|
||||
</svg>
|
||||
<span>Infotafel</span>
|
||||
<span>Aktuelles</span>
|
||||
</a>
|
||||
<a href="/pgp" class="menu-item noline {% if tab == "pgp" %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||
|
|
@ -72,6 +76,13 @@
|
|||
</svg>
|
||||
<span>PGP-Schlüssel</span>
|
||||
</a>
|
||||
<a href="https://schule.moritzhuber.de" class="menu-item noline">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 15c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1-8h-2V7h2v2z"/>
|
||||
</svg>
|
||||
<span>Infotafel</span>
|
||||
</a>
|
||||
<a href="https://cloud.moritzhuber.de/s/upload-moritz" class="menu-item noline">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
||||
|
|
|
|||
18
src/views/news.twig
Normal file
18
src/views/news.twig
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "frame.twig" %}
|
||||
|
||||
{% block title %}Aktuelles{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Aktuelles</h2>
|
||||
<div class="mt-4">
|
||||
{% for item in news %}
|
||||
<article
|
||||
class="p-2 text-lg mt-2 rounded-xl bg-emerald-50 dark:bg-slate-800 border-2 border-emerald-100 dark:border dark:border-slate-700">
|
||||
<div class="text-base text-emerald-600">{{ item.date }}</div>
|
||||
{{ item.content|raw }}
|
||||
</article>
|
||||
{% else %}
|
||||
Keine Einträge, schau wann anders nochmal vorbei
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
95
storage/framework/Collection.php
Normal file
95
storage/framework/Collection.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace framework;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Implements better arrays
|
||||
**/
|
||||
class Collection implements IteratorAggregate
|
||||
{
|
||||
protected array $items = [];
|
||||
|
||||
/**
|
||||
* Create a new Collection
|
||||
* @param array $items
|
||||
*/
|
||||
public function __construct(array $items = [])
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collection with the given range.
|
||||
*
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return static<int, int>
|
||||
*/
|
||||
public static function range(int $from, int $to): static
|
||||
{
|
||||
return new static(range($from, $to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all items of the collection
|
||||
* @return array
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip items in the collection
|
||||
* @return $this
|
||||
*/
|
||||
public function flip(): static
|
||||
{
|
||||
return new static(array_flip($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns first element of collection
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function first(): mixed
|
||||
{
|
||||
return $this->items[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns last element of collection
|
||||
* @return false|mixed|null
|
||||
*/
|
||||
public function last(): mixed
|
||||
{
|
||||
return end($this->items) ?? null;
|
||||
}
|
||||
|
||||
public function add($item): self
|
||||
{
|
||||
$this->items[] = $item;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* return all items as json
|
||||
* @return bool|string
|
||||
*/
|
||||
public function json(): bool|string
|
||||
{
|
||||
return json_encode($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
}
|
||||
16
storage/framework/Controller.php
Normal file
16
storage/framework/Controller.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace framework;
|
||||
|
||||
class Controller
|
||||
{
|
||||
public static function tab(): ?string
|
||||
{
|
||||
return get_called_class()::$tab ?? null;
|
||||
}
|
||||
|
||||
function view(string $view, array $data = []): string
|
||||
{
|
||||
return view($view, array_merge($data, ['tab' => self::tab()]));
|
||||
}
|
||||
}
|
||||
48
storage/framework/database/Database.php
Normal file
48
storage/framework/database/Database.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace framework\database;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
class Database
|
||||
{
|
||||
static PDO $pdo;
|
||||
static bool $init = false;
|
||||
|
||||
static function query(Query $query): array
|
||||
{
|
||||
self::init();
|
||||
try {
|
||||
if (!empty($query->parameters)) {
|
||||
$stmt = self::$pdo->prepare($query->query);
|
||||
$values = array_values($query->parameters);
|
||||
$stmt->execute([...$values]);
|
||||
} else {
|
||||
$stmt = self::$pdo->query($query->query, PDO::FETCH_ASSOC);
|
||||
}
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
private static function init(): void
|
||||
{
|
||||
if (self::$init)
|
||||
return;
|
||||
$host = env("DB_HOST");
|
||||
$dbname = env("DB_NAME");
|
||||
|
||||
try {
|
||||
static::$pdo = new PDO(
|
||||
"mysql:host=$host;dbname=$dbname;charset=utf8",
|
||||
env("DB_USER"),
|
||||
env("DB_PASS"),
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
|
||||
}
|
||||
self::$init = true;
|
||||
}
|
||||
}
|
||||
89
storage/framework/database/Model.php
Normal file
89
storage/framework/database/Model.php
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace framework\database;
|
||||
|
||||
use framework\Collection;
|
||||
|
||||
/**
|
||||
* @method static tableName()
|
||||
* @property $id
|
||||
*/
|
||||
abstract class Model
|
||||
{
|
||||
static string $tableName;
|
||||
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
return match ($name) {
|
||||
"tableName" => get_called_class()::$tableName ??
|
||||
str_replace("Model", "s", getClassName(get_called_class())),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
protected static function arrayToModel(array|Collection|null $data): Collection|self|null
|
||||
{
|
||||
if ($data === null) {
|
||||
return null;
|
||||
} elseif (empty($data)) {
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
if (isAssociativeArray($data)) {
|
||||
$className = get_called_class();
|
||||
$model = new $className();
|
||||
foreach ($data as $key => $value) {
|
||||
$model->$key = $value;
|
||||
}
|
||||
return $model;
|
||||
} else {
|
||||
$models = new Collection();
|
||||
foreach ($data as $item) {
|
||||
$models->add(self::arrayToModel($item));
|
||||
}
|
||||
return $models;
|
||||
}
|
||||
}
|
||||
|
||||
static function all(): Collection
|
||||
{
|
||||
$table = self::tableName();
|
||||
return self::arrayToModel(Database::query(new Query("SELECT * FROM `$table`")));
|
||||
}
|
||||
|
||||
static function findById($id): ?self
|
||||
{
|
||||
$table = self::tableName();
|
||||
$model = self::arrayToModel(
|
||||
Database::query(
|
||||
new Query("SELECT * FROM `$table` WHERE `id` = (?) LIMIT 1", ["id" => $id])
|
||||
)
|
||||
);
|
||||
return $model->first();
|
||||
}
|
||||
|
||||
static function where($condition, $value): Collection
|
||||
{
|
||||
$table = self::tableName();
|
||||
return self::arrayToModel(
|
||||
Database::query(
|
||||
new Query("SELECT * FROM `$table` WHERE `$condition` = (?)", [$condition => $value])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function delete(): void
|
||||
{
|
||||
$table = self::tableName();
|
||||
Database::query(
|
||||
new Query("DELETE FROM `$table` WHERE `id` = (?)", [
|
||||
"id" => $this->id,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
function json(): string
|
||||
{
|
||||
return json_encode($this);
|
||||
}
|
||||
}
|
||||
15
storage/framework/database/Query.php
Normal file
15
storage/framework/database/Query.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace framework\database;
|
||||
|
||||
class Query
|
||||
{
|
||||
public string $query;
|
||||
public array $parameters;
|
||||
|
||||
public function __construct(string $query, array $parameters = [])
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,20 @@ function run_script(string $name, ...$params): bool|string|null
|
|||
return shell_exec("sh $script_location $params");
|
||||
}
|
||||
|
||||
function getClassName(string $class): string
|
||||
{
|
||||
$exp = explode("\\", $class);
|
||||
return end($exp);
|
||||
}
|
||||
|
||||
function isAssociativeArray(array $array): bool
|
||||
{
|
||||
if ([] == $array) {
|
||||
return false;
|
||||
}
|
||||
return array_keys($array) !== range(0, count($array) - 1);
|
||||
}
|
||||
|
||||
function view(string $view, array $data = [], int $status = 200): string
|
||||
{
|
||||
$loader = new \Twig\Loader\FilesystemLoader(ROOT . '/src/views');
|
||||
|
|
|
|||
Reference in a new issue