after 6 years update. hell yeah

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2025-05-15 19:04:24 +07:00
parent 7e1993730a
commit bb3a611b7d
57 changed files with 1221 additions and 1125 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
codeception.yml Normal file → Executable file
View File

80
composer.json Normal file → Executable file
View File

@ -1,42 +1,42 @@
{
"name": "mirzaev/beejee",
"description": "Test BeeJee",
"type": "project",
"license": "AGPL-3.0-or-later",
"homepage": "https://git.hood.su/mirzaev/beejee",
"authors": [
{
"name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "red@hood.su",
"role": "Developer"
}
],
"require": {
"php": ">=7.4.0",
"ext-PDO": "^7.4",
"twbs/bootstrap": "^4.5",
"twig/twig": "^3.1"
},
"require-dev": {
"codeception/codeception": "^4.1",
"codeception/module-webdriver": "^1.0.0"
},
"autoload": {
"psr-4": {
"mirzaev\\beejee\\": "mirzaev/beejee/system"
}
},
"autoload-dev": {
"psr-4": {
"mirzaev\\beejee\\tests\\": "mirzaev/beejee/tests"
}
},
"scripts": {
"post-update-cmd": [
"mkdir -p .\\mirzaev\\beejee\\system\\web\\css\\bootstrap > /dev/null 2>&1 || true",
"mkdir -p .\\mirzaev\\beejee\\system\\web\\js\\bootstrap > /dev/null 2>&1 || true",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\css\\* .\\mirzaev\\beejee\\system\\web\\css\\bootstrap",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\js\\* .\\\\mirzaev\\beejee\\system\\web\\js\\bootstrap"
]
}
"name": "mirzaev/beejee",
"description": "Task manager. MVC + CRUD. Developed in 2 days (2019)",
"type": "project",
"license": "WTFPL",
"homepage": "https://git.hood.su/mirzaev/beejee",
"authors": [
{
"name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "arsen@mirzaev.sexy",
"role": "Developer"
}
],
"require": {
"php": ">=8.4.0",
"ext-PDO": "^8.4",
"twbs/bootstrap": "^4.5",
"twig/twig": "^3.1"
},
"require-dev": {
"codeception/codeception": "^4.1",
"codeception/module-webdriver": "^1.0.0"
},
"autoload": {
"psr-4": {
"mirzaev\\beejee\\": "mirzaev/beejee/system"
}
},
"autoload-dev": {
"psr-4": {
"mirzaev\\beejee\\tests\\": "mirzaev/beejee/tests"
}
},
"scripts": {
"post-update-cmd": [
"mkdir -p .\\mirzaev\\beejee\\system\\web\\css\\bootstrap > /dev/null 2>&1 || true",
"mkdir -p .\\mirzaev\\beejee\\system\\web\\js\\bootstrap > /dev/null 2>&1 || true",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\css\\* .\\mirzaev\\beejee\\system\\web\\css\\bootstrap",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\js\\* .\\\\mirzaev\\beejee\\system\\web\\js\\bootstrap"
]
}
}

1751
composer.lock generated Normal file → Executable file

File diff suppressed because it is too large Load Diff

2
mirzaev/beejee/system/controllers/authController.php Normal file → Executable file
View File

@ -22,7 +22,7 @@ final class authController extends controller
* @return array|null
*/
public function auth(array $params = null): ?array
{
{
$login = $params['login'] ?? $_COOKIE['login'];
$password = $params['password'] ?? $_COOKIE['password'];

234
mirzaev/beejee/system/controllers/controller.php Normal file → Executable file
View File

@ -5,10 +5,10 @@ declare(strict_types=1);
namespace mirzaev\beejee\controllers;
use mirzaev\beejee\core,
mirzaev\beejee\models\model;
mirzaev\beejee\models\model;
use Twig\Loader\FilesystemLoader,
Twig\Environment as view;
Twig\Environment as view;
use Exception;
@ -20,129 +20,131 @@ use Exception;
*/
class controller
{
/**
* @var model $model Модель
*/
protected model $model;
/**
* @var model $model Модель
*/
protected model $model;
/**
* @var view $view Шаблонизатор представления
*/
protected view $view;
/**
* @var view $view Шаблонизатор представления
*/
protected view $view;
/**
* Конструктор
*
* @return void
*/
public function __construct()
{
// Установка значения по умолчанию для модели (если будет найдена)
$this->__get('model');
// Установка значения по умолчанию для шаблонизатора представлений
$this->__get('view');
}
/**
* Конструктор
*
* @return void
*/
public function __construct()
{
// Установка значения по умолчанию для модели (если будет найдена)
$this->__get('model');
/**
* Отрисовка шаблона
*
* @param string $route Маршрут
*/
public function view(string $route)
{
// Чтение представления по шаблону пути: "/views/[controller]/index
// Никаких слоёв и шаблонизаторов
// Не стал в ядре записывать путь до шаблонов
if (file_exists($view = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $route . DIRECTORY_SEPARATOR . 'index.html')) {
include $view;
}
}
// Установка значения по умолчанию для шаблонизатора представлений
$this->__get('view');
}
/**
* Записать свойство
*
* @param mixed $name Название
* @param mixed $value Значение
*
* @return void
*/
public function __set($name, $value): void
{
if ($name === 'model') {
if (!isset($this->model)) {
$this->model = $value;
return;
} else {
throw new Exception('Запрещено переопределять модель');
}
} else if ($name === 'view') {
if (!isset($this->view)) {
$this->view = $value;
return;
} else {
throw new Exception('Запрещено переопределять шаблонизатор представления');
}
}
/**
* Отрисовка шаблона
*
* @param string $route Маршрут
*/
public function view(string $route)
{
// Чтение представления по шаблону пути: "/views/[controller]/index
// Никаких слоёв и шаблонизаторов
// Не стал в ядре записывать путь до шаблонов
if (file_exists($view = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $route . DIRECTORY_SEPARATOR . 'index.html')) {
include $view;
}
}
throw new Exception('Свойство не найдено: ' . $name);
}
/**
* Записать свойство
*
* @param mixed $name Название
* @param mixed $value Значение
*
* @return void
*/
public function __set($name, $value): void
{
if ($name === 'model') {
if (!isset($this->model)) {
$this->model = $value;
return;
} else {
throw new Exception('Запрещено переопределять модель');
}
} else if ($name === 'view') {
if (!isset($this->view)) {
$this->view = $value;
return;
} else {
throw new Exception('Запрещено переопределять шаблонизатор представления');
}
}
/**
* Прочитать свойство
*
* @param mixed $name Название
*
* @return mixed
*/
public function __get($name)
{
if ($name === 'model') {
if (isset($this->model)) {
// Если модель найдена
return $this->model;
} else {
// Инициализация класса модели
$model = preg_replace('/' . core::controllerPostfix() . '$/i', '', basename(get_class($this))) . core::modelPostfix();
// Иначе
if (class_exists($model_class = core::namespace() . '\\models\\' . $model)) {
// Если найдена одноимённая с контроллером модель (без постфикса)
return $this->model = new $model_class;
}
return;
}
} else if ($name === 'view') {
if (isset($this->view)) {
// Если модель найдена
return $this->view;
} else {
$path = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views';
$loader = new FilesystemLoader($path);
throw new Exception('Свойство не найдено: ' . $name);
}
return $this->view = (new view($loader, [
// 'cache' => $path . DIRECTORY_SEPARATOR . 'cache',
]));
}
}
/**
* Прочитать свойство
*
* @param mixed $name Название
*
* @return mixed
*/
public function __get($name)
{
if ($name === 'model') {
if (isset($this->model)) {
// Если модель найдена
return $this->model;
} else {
// Инициализация класса модели
$model = preg_replace('/' . core::controllerPostfix() . '$/i', '', substr($this::class, strrpos($this::class, '\\') + 1)) . core::modelPostfix();
throw new Exception('Свойство не найдено: ' . $name);
}
// Иначе
if (class_exists($model_class = core::namespace() . '\\models\\' . $model)) {
// Если найдена одноимённая с контроллером модель (без постфикса)
return $this->model = new $model_class;
}
return;
}
} else if ($name === 'view') {
if (isset($this->view)) {
// Если модель найдена
return $this->view;
} else {
$path = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views';
$loader = new FilesystemLoader($path);
return $this->view = (new view($loader, [
// 'cache' => $path . DIRECTORY_SEPARATOR . 'cache',
]));
}
}
throw new Exception('Свойство не найдено: ' . $name);
}
/**
* Проверить свойство на инициализированность
*
* @param string $name Название
*
* @return mixed
*/
public function __isset(string $name)
{
if ($name === 'model') {
return isset($this->model);
} else if ($name === 'view') {
return isset($this->view);
}
/**
* Проверить свойство на инициализированность
*
* @param string $name Название
*
* @return mixed
*/
public function __isset(string $name)
{
if ($name === 'model') {
return isset($this->model);
} else if ($name === 'view') {
return isset($this->view);
}
throw new Exception('Свойство не найдено: ' . $name);
}
throw new Exception('Свойство не найдено: ' . $name);
}
}

0
mirzaev/beejee/system/controllers/errorsController.php Normal file → Executable file
View File

36
mirzaev/beejee/system/controllers/mainController.php Normal file → Executable file
View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace mirzaev\beejee\controllers;
use mirzaev\beejee\controllers\controller,
mirzaev\beejee\controllers\tasksController;
mirzaev\beejee\controllers\tasksController;
use PDO;
@ -17,24 +17,26 @@ use PDO;
*/
final class mainController extends controller
{
public function index(array $params)
{
$tasks = new tasksController;
public function index(array $params)
{
$tasks = new tasksController;
// Нормализация
$page['current'] = filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT);
$page['sort'] = filter_var($params['page'], FILTER_SANITIZE_STRING);
$params['page'] ??= '/';
// Инициализация страниц заданий
$page['count'] = $tasks->count();
$page['current'] = filter_var($params['page'], FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['previous'] = $page['current'] > 1 ? $page['current'] - 1 : 1;
$page['next'] = (int) $page['current'] < (int) $page['count'] ? $page['current'] + 1 : $page['current'];
// Нормализация
$page['current'] = filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT);
$page['sort'] = filter_var($params['page'], FILTER_SANITIZE_STRING);
// Инициализация сортировки
$sort = empty($params['sort']) ? 'id' : $params['sort'];
// Инициализация страниц заданий
$page['count'] = $tasks->count();
$page['current'] = filter_var($params['page'], FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['previous'] = $page['current'] > 1 ? $page['current'] - 1 : 1;
$page['next'] = (int) $page['current'] < (int) $page['count'] ? $page['current'] + 1 : $page['current'];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', ['page' => $page, 'sort' => $sort]);
}
// Инициализация сортировки
$sort = empty($params['sort']) ? 'id' : $params['sort'];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', ['page' => $page, 'sort' => $sort]);
}
}

222
mirzaev/beejee/system/controllers/tasksController.php Normal file → Executable file
View File

@ -14,134 +14,134 @@ use mirzaev\beejee\controllers\controller;
*/
final class tasksController extends controller
{
/**
* Записать в базу данных
*
* @param array $params
*
* @return string|null
*/
public function create(array $params): ?string
{
// Инициализация параметров
$name = $params['name'];
$email = $params['email'];
$task = $params['task'];
/**
* Записать в базу данных
*
* @param array $params
*
* @return string|null
*/
public function create(array $params): ?string
{
// Инициализация параметров
$name = $params['name'];
$email = $params['email'];
$task = $params['task'];
session_start();
session_start();
if (!isset($_SESSION['task_create_last']) || (isset($_SESSION['task_create_last']) && $_SESSION['task_create_last'] < time() - 5)) {
// Если это первый вызов или последний вызов был более 5 секунд назад
if (!isset($_SESSION['task_create_last']) || (isset($_SESSION['task_create_last']) && $_SESSION['task_create_last'] < time() - 5)) {
// Если это первый вызов или последний вызов был более 5 секунд назад
// Запись задания
$this->model->create($name, $email, $task);
// Запись задания
$this->model->create($name, $email, $task);
$_SESSION['task_create_last'] = time();
$_SESSION['task_create_last'] = time();
return null;
}
return null;
}
return 'Следующее задание можно создать через ' . (5 - (time() - $_SESSION['task_create_last'])) . ' секунд';
}
return 'Следующее задание можно создать через ' . (5 - (time() - $_SESSION['task_create_last'])) . ' секунд';
}
/**
* Прочитать из базы данных
*
* @param array $params
*
* @return array
*/
public function read(array $params): array
{
// Нормализация
$page['current'] = filter_var(filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['sort'] = filter_var($params['sort'], FILTER_SANITIZE_STRING);
/**
* Прочитать из базы данных
*
* @param array $params
*
* @return array
*/
public function read(array $params): array
{
// Нормализация
$page['current'] = filter_var(filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['sort'] = filter_var($params['sort'] ?? '', FILTER_SANITIZE_STRING);
// Инициализация параметров
$limit = 3;
$sort = empty($params['sort']) ? 'id' : $params['sort'];
$page = ($params['page'] - 1) * $limit;
// Инициализация параметров
$limit = 3;
$sort = empty($params['sort']) ? 'id' : $params['sort'];
$page = ($params['page'] - 1) * $limit;
// Чтение заданий
return $this->model->read($page, $limit, $sort);
}
// Чтение заданий
return $this->model->read($page, $limit, $sort);
}
/**
* Обновить в базе данных
*
* @param array $params
*
* @return void
*/
public function update(array $params): void
{
session_start();
/**
* Обновить в базе данных
*
* @param array $params
*
* @return void
*/
public function update(array $params): void
{
session_start();
// Нормализация
$page['id'] = filter_var(filter_var($params['id'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['name'] = filter_var($params['name'], FILTER_SANITIZE_STRING);
$page['email'] = filter_var($params['email'], FILTER_SANITIZE_EMAIL);
$page['task'] = filter_var($params['task'], FILTER_SANITIZE_STRING);
// Нормализация
$page['id'] = filter_var(filter_var($params['id'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['name'] = filter_var($params['name'], FILTER_SANITIZE_STRING);
$page['email'] = filter_var($params['email'], FILTER_SANITIZE_EMAIL);
$page['task'] = filter_var($params['task'], FILTER_SANITIZE_STRING);
// Инициализация параметров
$id = (int) $params['id'];
$name = $params['name'];
$email = $params['email'];
$task = $params['task'];
$completed = $_SESSION['admin'] == 1 ? $params['completed'] : null;
// Инициализация параметров
$id = (int) $params['id'];
$name = $params['name'];
$email = $params['email'];
$task = $params['task'];
$completed = $_SESSION['admin'] == 1 ? $params['completed'] : null;
// Запись задания
$this->model->update($id, $name, $email, $task, $completed);
}
// Запись задания
$this->model->update($id, $name, $email, $task, $completed);
}
/**
* Удалить из базы данных
*
* @param array $params
*
* @return void
*/
public function delete(array $params): void
{
// Инициализация параметров
$id = (int) $params['id'];
/**
* Удалить из базы данных
*
* @param array $params
*
* @return void
*/
public function delete(array $params): void
{
// Инициализация параметров
$id = (int) $params['id'];
// ВНИМАНИЕ: Сессию можно подобрать брутфорсом (либо ещё как-нибудь узнать)
// Я бы не стал так делать на нормальном проекте - писал привязку к IP, например
session_start();
if ($_SESSION['admin'] == 1) {
// Если пользователь является админстратором
$this->model->delete($id);
}
}
// ВНИМАНИЕ: Сессию можно подобрать брутфорсом (либо ещё как-нибудь узнать)
// Я бы не стал так делать на нормальном проекте - писал привязку к IP, например
session_start();
if ($_SESSION['admin'] == 1) {
// Если пользователь является админстратором
$this->model->delete($id);
}
}
/**
* Сгенерировать представление HTML-листа заданий
*
* @param array $params
*
* @return string
*/
public function genList(array $params): string
{
// Инициализация заданий
$tasks = $this->read($params);
/**
* Сгенерировать представление HTML-листа заданий
*
* @param array $params
*
* @return string
*/
public function genList(array $params): string
{
// Инициализация заданий
$tasks = $this->read($params);
// Инициализация админ-прав
session_start();
$admin = $_SESSION['admin'];
// Инициализация админ-прав
session_start();
$admin = $_SESSION['admin'];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'tasks' . DIRECTORY_SEPARATOR . 'list.html', ['tasks' => $tasks, 'admin' => $admin]);
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'tasks' . DIRECTORY_SEPARATOR . 'list.html', ['tasks' => $tasks, 'admin' => $admin]);
}
/**
* Подсчитать количество заданий
*
* @return int
*/
public function count(): int
{
return $this->model->count();
}
/**
* Подсчитать количество заданий
*
* @return int
*/
public function count(): int
{
return $this->__get('model')->count();
}
}

0
mirzaev/beejee/system/core.php Normal file → Executable file
View File

0
mirzaev/beejee/system/models/authModel.php Normal file → Executable file
View File

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace mirzaev\beejee\models;
use mirzaev\beejee\models\model;
use PDO;
/**
* Бебра
*
* @package mirzaev\beejee\models
* @author Arsen Mirzaev Tatyano-Muradovich <red@hood.su>
*/
final class mainModel extends model {}

0
mirzaev/beejee/system/models/model.php Normal file → Executable file
View File

0
mirzaev/beejee/system/models/tasksModel.php Normal file → Executable file
View File

0
mirzaev/beejee/system/public/Nginx_1.17_vhost.conf Normal file → Executable file
View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-grid.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-grid.min.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-reboot.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-reboot.min.css vendored Normal file → Executable file
View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap.min.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/main.css Normal file → Executable file
View File

0
mirzaev/beejee/system/public/img/avatar.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

2
mirzaev/beejee/system/public/index.php Normal file → Executable file
View File

@ -25,4 +25,4 @@ router::create('deauth', 'auth', 'deauth', 'POST');
router::create('auth', 'auth', 'genSidebarPanel', 'GET');
router::create('reg', 'auth', 'reg', 'POST');
new Core('mysql:dbname=beejee;host=127.0.0.1', 'root', 'root');
new Core('mysql:dbname=task_manager;unix_socket=/run/mysqld/mysqld.sock', login: 'magomed', password: 'varyeniki_besplatno_1988');

0
mirzaev/beejee/system/public/js/auth.js Normal file → Executable file
View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.bundle.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.bundle.min.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.min.js vendored Normal file → Executable file
View File

View File

View File

0
mirzaev/beejee/system/public/js/tasks.js Normal file → Executable file
View File

0
mirzaev/beejee/system/router.php Normal file → Executable file
View File

0
mirzaev/beejee/system/views/auth/auth_sidebar.html Normal file → Executable file
View File

0
mirzaev/beejee/system/views/main/index.html Normal file → Executable file
View File

0
mirzaev/beejee/system/views/tasks/list.html Normal file → Executable file
View File

0
mirzaev/beejee/tests/.gitignore vendored Normal file → Executable file
View File

0
mirzaev/beejee/tests/MainCest.php Normal file → Executable file
View File

0
mirzaev/beejee/tests/_data/.gitkeep Normal file → Executable file
View File

0
mirzaev/beejee/tests/_output/.gitkeep Normal file → Executable file
View File

View File

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

0
mirzaev/beejee/tests/_output/failed Normal file → Executable file
View File

0
mirzaev/beejee/tests/_support/AcceptanceTester.php Normal file → Executable file
View File

0
mirzaev/beejee/tests/_support/Helper/Acceptance.php Normal file → Executable file
View File

View File