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": {
|
"require": {
|
||||||
"twig/twig": "^3.4",
|
"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",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "aeb8850dadf0ed622694b70b23f24518",
|
"content-hash": "9ce76971c8555afa913cadbcffbebd26",
|
||||||
"packages": [
|
"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",
|
"name": "laminas/laminas-diactoros",
|
||||||
"version": "2.17.0",
|
"version": "2.17.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
// https://github.com/miladrahimi/phprouter
|
// https://github.com/miladrahimi/phprouter
|
||||||
|
|
||||||
|
use app\controllers\NewsController;
|
||||||
use app\controllers\PGPController;
|
use app\controllers\PGPController;
|
||||||
use app\controllers\APIController;
|
use app\controllers\APIController;
|
||||||
use framework\Router;
|
use framework\Router;
|
||||||
|
|
@ -11,6 +12,8 @@ $router = Router::create();
|
||||||
$router->view('/', 'index', 'home');
|
$router->view('/', 'index', 'home');
|
||||||
$router->view('/impressum', 'impressum');
|
$router->view('/impressum', 'impressum');
|
||||||
|
|
||||||
|
$router->get('/news', [NewsController::class, 'index']);
|
||||||
|
|
||||||
$router->get('/pgp/?', [PGPController::class, 'index']);
|
$router->get('/pgp/?', [PGPController::class, 'index']);
|
||||||
$router->get('/pgp/{email}', [PGPController::class, 'mail']);
|
$router->get('/pgp/{email}', [PGPController::class, 'mail']);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,12 @@
|
||||||
@apply font-medium text-xl mb-2 dark:text-slate-200
|
@apply font-medium text-xl mb-2 dark:text-slate-200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@apply font-medium text-lg mb-1 dark:text-slate-200
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply mt-2 text-lg
|
@apply mt-2 text-lg
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
|
@ -58,12 +59,15 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span>Startseite</span>
|
<span>Startseite</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://schule.moritzhuber.de" class="menu-item noline">
|
<a href="/news" class="menu-item noline {% if tab == "news" %}active{% endif %}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
<g 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"/>
|
<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>
|
</svg>
|
||||||
<span>Infotafel</span>
|
<span>Aktuelles</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/pgp" class="menu-item noline {% if tab == "pgp" %}active{% endif %}">
|
<a href="/pgp" class="menu-item noline {% if tab == "pgp" %}active{% endif %}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||||
|
|
@ -72,6 +76,13 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span>PGP-Schlüssel</span>
|
<span>PGP-Schlüssel</span>
|
||||||
</a>
|
</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">
|
<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">
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||||
<path d="M0 0h24v24H0V0z" fill="none"/>
|
<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");
|
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
|
function view(string $view, array $data = [], int $status = 200): string
|
||||||
{
|
{
|
||||||
$loader = new \Twig\Loader\FilesystemLoader(ROOT . '/src/views');
|
$loader = new \Twig\Loader\FilesystemLoader(ROOT . '/src/views');
|
||||||
|
|
|
||||||
Reference in a new issue