супер попа (обновление)

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2022-04-16 17:30:06 +10:00
parent c8025acc79
commit 30fd8f0ec3
14 changed files with 623 additions and 338 deletions

View File

@ -36,12 +36,12 @@ final class accounts_controller extends core
public function registration(array $vars = []): ?string public function registration(array $vars = []): ?string
{ {
// Инициализация журнала ошибок // Инициализация журнала ошибок
$vars['errors'] = ['account' => []]; $vars['errors'] = [];
if ($vars['account'] = accounts::registration(email: $vars['email'] ?? null, password: $vars['password'] ?? null, errors: $vars['errors']['account'])) { if ($vars['account'] = accounts::registration(mail: $vars['mail'] ?? null, password: $vars['password'] ?? null, errors: $vars['errors'])) {
// Удалось зарегистрироваться // Удалось зарегистрироваться
if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), $vars)) { if ($vars['account'] = accounts::authentication($vars['mail'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), $vars)) {
// Удалось аутентифицироваться // Удалось аутентифицироваться
} else { } else {
// Не удалось аутентифицироваться // Не удалось аутентифицироваться
@ -75,9 +75,9 @@ final class accounts_controller extends core
public function authentication(array $vars = []): ?string public function authentication(array $vars = []): ?string
{ {
// Инициализация журнала ошибок // Инициализация журнала ошибок
$vars['errors'] = ['account' => []]; $vars['errors'] = [];
if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), errors: $vars['errors']['account'])) { if ($vars['account'] = accounts::authentication($vars['mail'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), errors: $vars['errors'])) {
// Удалось аутентифицироваться // Удалось аутентифицироваться
} else { } else {
// Не удалось аутентифицироваться // Не удалось аутентифицироваться
@ -105,9 +105,9 @@ final class accounts_controller extends core
public function deauthentication(array $vars = []): ?string public function deauthentication(array $vars = []): ?string
{ {
// Инициализация журнала ошибок // Инициализация журнала ошибок
$vars['errors'] = ['account' => []]; $vars['errors'] = [];
if (accounts::deauthentication(errors: $vars['errors']['account'])) { if (accounts::deauthentication(errors: $vars['errors'])) {
// Удалось деаутентифицироваться // Удалось деаутентифицироваться
// Деинициализация аккаунта // Деинициализация аккаунта
@ -137,21 +137,21 @@ final class accounts_controller extends core
public function data(array $vars = []): ?string public function data(array $vars = []): ?string
{ {
// Инициализация журнала ошибок // Инициализация журнала ошибок
$vars['errors'] = ['account' => []]; $vars['errors'] = [];
if ($account = accounts::read(['id' => $vars['id']], $vars['errors'])) { if ($account = accounts::read(['id' => $vars['id']], $vars['errors'])) {
// Найдены данные запрашиваемого аккаунта // Найдены данные запрашиваемого аккаунта
// Инициализация аккаунта // Инициализация аккаунта
$vars['account'] = accounts::account($vars['errors']); $vars['account'] = accounts::init(errors: $vars['errors']);
if ($vars['account'] && $vars['account']['permissions']['accounts'] ?? 0 === 1) { if ($vars['account'] && $vars['account']->permissions['accounts'] ?? 0 === 1) {
// Удалось аутентифицироваться и пройдена проверка авторизации // Удалось аутентифицироваться и пройдена проверка авторизации
} else { } else {
// Не удалось аутентифицироваться // Не удалось аутентифицироваться
// Удаление запрещённых к публикации полей // Удаление запрещённых к публикации полей
$account['password'] = $account['hash'] = $account['time'] = null; $account->password = $account->hash = $account->time = null;
} }
// Генерация ответа // Генерация ответа

View File

@ -18,29 +18,30 @@ use mirzaev\surikovlib\models\books_model as books;
final class books_controller extends core final class books_controller extends core
{ {
/** /**
* Страница с библеотекой * Страница с книгами (или книгой)
* *
* @param array $vars * @param array $vars
*/ */
public function index(array $vars = []): ?string public function index(array $vars = []): ?string
{ {
// Проверка аутентифицированности // Инициализация журнала ошибок
$vars['account'] = accounts::account($vars); $vars['errors'] = [];
if (!empty($vars['id'])) { // Проверка аутентифицированности
$vars['account'] = accounts::init(errors: $vars['errors']);
if (isset($vars['id'])) {
// Определённая книга // Определённая книга
// Чтение книги // Чтение метаданных книги
$vars['book'] = books::read(['id' => $vars['id']])[0] ?? null; $vars['book'] = books::read(['id' => $vars['id']])[0] ?? null;
if (empty($vars['book'])) { // Инициализация страницы
// Не найдена книга if (empty($vars['page']) || $vars['page'] < 0) $vars['page'] = 0;
return null;
}
// Генерация представления // Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . 'book.html', $vars); return $this->view->render(DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . 'book.html', $vars);
} else { } else {
// Все книги // Все книги
@ -52,40 +53,6 @@ final class books_controller extends core
} }
} }
/**
* Обложка
*
* @param array $vars
*
* @return string|null Файл, если найден
*/
public function cover(array $vars = []): ?string
{
// Проверка входных параметров
if (empty($vars['id'])) return null;
// Инициализация пути до файла
$file = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $vars['id'] . DIRECTORY_SEPARATOR . '0.jpg';
if (file_exists($file)) {
// Найден файл
// Настройка заголовков
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename=' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
// Очистить буфер вывода
ob_end_clean();
return file_get_contents($file);
}
return null;
}
/** /**
* Запись * Запись
* *
@ -96,59 +63,54 @@ final class books_controller extends core
*/ */
public function write(array $vars = [], array $files = []): ?string public function write(array $vars = [], array $files = []): ?string
{ {
// ПЕРЕНЕСТИ В МОДЕЛЬ // Инициализация журнала ошибок
$vars['errors'] = [];
// Инициализация буфера сохранённых книг if (count($books = books::import($files['books'] ?? [], errors: $vars['errors'])) > 0) {
$books = []; // Загружены книги
} else {
for ($i = -1; count($files['books']['name']) > ++$i;) { // Не загружены книги
// Перебор загруженных книг
// Генерация хеша файла
$hash = hash_file('md5', $files['books']['tmp_name'][$i]) ?? 0;
if (move_uploaded_file($files['books']['tmp_name'][$i], \STORAGE . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $hash . '_' . $files['books']['name'][$i])) {
// Загружен и перемещён из временной папки файл с книгой
// Запись в буфер сохранённых книг
$books[] = $hash . '_' . $files['books']['name'][$i];
}
}
foreach ($books as $book) {
// Перебор сохранённых книг
// Запись в базу данных
// Инициализация пути до хранилища
$storage = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . 'id' . DIRECTORY_SEPARATOR;
// Извлечение изображений из PDF-документа
exec("cd $storage && pdfimages -j $book");
} }
// Перенаправление // Перенаправление
header('Location: /books', response_code: 303); header('Location: /books', response_code: 303);
return null; return 'wtf';
} }
/** /**
* Чтение * Чтение
* *
* @param array $vars Параметры запроса * @param array $vars
* *
* @return string|null HTML-документ * @return string|null Файл, если найден
*/ */
public function read(array $vars = []): ?string public function read(array $vars = []): ?string
{ {
if (accounts::deauthentication($vars)) { if (isset($vars['id'], $vars['file'])) {
// Удалось деаутентифицироваться // Найдены обязательные входные параметры
// Инициализация пути до файла
$file = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $vars['id'] . DIRECTORY_SEPARATOR . $vars['file'] . '.jpg';
if (file_exists($file)) {
// Найден файл
// Настройка заголовков
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename=' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
// Очистить буфер вывода
ob_end_clean();
return file_get_contents($file);
}
} }
// Генерация представления return null;
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
} }
/** /**
@ -160,11 +122,5 @@ final class books_controller extends core
*/ */
public function delete(array $vars = []): ?string public function delete(array $vars = []): ?string
{ {
if (accounts::deauthentication($vars)) {
// Удалось деаутентифицироваться
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
} }
} }

View File

@ -20,8 +20,11 @@ final class main_controller extends core
{ {
public function index(array $vars = []): ?string public function index(array $vars = []): ?string
{ {
// Инициализация журнала ошибок
$vars['errors'] = [];
// Проверка аутентифицированности // Проверка аутентифицированности
$vars['account'] = accounts::account($vars); $vars['account'] = accounts::init(errors: $vars['errors']);
// Генерация представления // Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars); return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);

View File

@ -15,33 +15,86 @@ use exception;
*/ */
final class accounts_model extends core final class accounts_model extends core
{ {
/**
* Идентификатор
*/
public int $id;
/**
* Почта
*/
public string $mail;
/**
* Пароль
*/
public string $password;
/**
* Хеш
*/
public string $hash;
/**
* Время активности хеша
*/
public int $time;
/**
* Время активности хеша
*/
public array $permissions;
/**
* Конструктор
*
* @param array $vars Параметры
*/
public function __construct(array $vars = []) {
foreach ($vars as $key => $value) {
// Перебор параметров
// Запись свойства
if (property_exists($this, $key)) $this->$key = $value;
}
}
/** /**
* Регистрация * Регистрация
* *
* @param string $name Входной псевдоним * @param string $mail Почта
* @param string $email Почта * @param string $password Пароль
* @param string $password Пароль (password) * @param bool $authenticate Автоматическая аутентификация в случае успешной регистрации
* @param bool $authentication Автоматическая аутентификация в случае успешной регистрации
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array|bool Аккаунт, если удалось аутентифицироваться * @return static|null Аккаунт
*/ */
public static function registration(string $name = null, string $email = null, string $password, array &$errors = []): array public static function registration(string $mail, string $password, bool $authenticate = true, array &$errors = []): ?static
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
if (static::account($errors)) { if (static::init(errors: $errors)) {
// Аутентифицирован пользователь // Аутентифицирован пользователь
// Запись ошибки // Запись ошибки
throw new exception('Уже аутентифицирован'); throw new exception('Уже аутентифицирован');
} }
if (empty($account = static::read(['name' => $name]) or $account = static::read(['email' => $email]))) { if (empty($account = static::read(['mail' => $mail]))) {
// Не удалось найти аккаунт // Не удалось найти аккаунт
if (static::write($name, $email, $password, $errors)) { if (static::write($mail, $password, $errors)) {
// Удалось зарегистрироваться // Удалось зарегистрироваться
if ($authenticate) {
// Запрошена аутентификация
// Аутентификация
$account = static::authentication($mail, $password, true, $errors);
}
return $account; return $account;
} }
} else { } else {
@ -51,7 +104,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -59,41 +112,44 @@ final class accounts_model extends core
]; ];
} }
return []; return null;
} }
/** /**
* Аутентификация * Аутентификация
* *
* @param string $login Входной псевдоним * @param string $mail Почта
* @param string $password Пароль (password) * @param string $password Пароль
* @param bool $remember Функция "Запомнить меня" - увеличенное время хранения cookies * @param bool $remember Функция "Запомнить меня" - увеличенное время хранения cookies
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array Аккаунт (если не найден, то пустой массив) * @return static|null Аккаунт
*/ */
public static function authentication(string $login, string $password, bool $remember = false, array &$errors = []): array public static function authentication(string $mail, string $password, bool $remember = false, array &$errors = []): ?static
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
if (static::account($errors)) { if (static::init(errors: $errors)) {
// Аутентифицирован пользователь // Аутентифицирован пользователь
// Запись ошибки // Запись ошибки
throw new exception('Уже аутентифицирован'); throw new exception('Уже аутентифицирован');
} }
if (empty($account = static::read(['mail' => $mail]))) {
if (empty($account = static::read(['name' => $login]) or $account = static::read(['email' => $login]))) {
// Не удалось найти аккаунт // Не удалось найти аккаунт
throw new exception('Не удалось найти аккаунт'); throw new exception('Не удалось найти аккаунт');
} }
if (password_verify($password, $account['password'])) {
if (password_verify($password, $account->password)) {
// Совпадают хеши паролей // Совпадают хеши паролей
// Инициализация идентификатора сессии // Инициализация идентификатора сессии
session_id($account['id']); session_id((string) $account->id);
// Инициализация названия сессии // Инициализация названия сессии
session_name('id'); session_name('id');
@ -105,10 +161,10 @@ final class accounts_model extends core
$time = time() + ($remember ? 604800 : 86400); $time = time() + ($remember ? 604800 : 86400);
// Инициализация хеша // Инициализация хеша
$hash = static::hash((int) $account['id'], crypt($account['password'], time() . $account['id']), $time, $errors)['hash']; $hash = static::hash((int) $account->id, crypt($account->password, time() . $account->id), $time, $errors)['hash'];
// Инициализация cookies // Инициализация cookies
setcookie("hash", $hash, $time, path: '/', secure: true); setcookie('hash', $hash, $time, path: '/', secure: true);
return $account; return $account;
} else { } else {
@ -118,7 +174,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -126,7 +182,7 @@ final class accounts_model extends core
]; ];
} }
return []; return null;
} }
/** /**
@ -138,8 +194,11 @@ final class accounts_model extends core
*/ */
public static function deauthentication(array &$errors = []): bool public static function deauthentication(array &$errors = []): bool
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
if ($account = static::account($errors)) { if ($account = static::init(errors: $errors)) {
// Аутентифицирован пользователь // Аутентифицирован пользователь
// Инициализация запроса // Инициализация запроса
@ -147,7 +206,7 @@ final class accounts_model extends core
// Параметры запроса // Параметры запроса
$params = [ $params = [
":id" => $account['id'], ":id" => $account->id,
]; ];
// Отправка запроса // Отправка запроса
@ -157,8 +216,8 @@ final class accounts_model extends core
$request->fetch(pdo::FETCH_ASSOC); $request->fetch(pdo::FETCH_ASSOC);
// Деинициализация cookies // Деинициализация cookies
setcookie("id", '', 0, path: '/', secure: true); setcookie('id', '', 0, path: '/', secure: true);
setcookie("hash", '', 0, path: '/', secure: true); setcookie('hash', '', 0, path: '/', secure: true);
return true; return true;
} else { } else {
@ -169,7 +228,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -181,21 +240,30 @@ final class accounts_model extends core
} }
/** /**
* Прочитать данные аккаунта, если пользователь аутентифицирован * Инициализация
*
* Можно использовать как проверку на аутентифицированность
* *
* @param int|null $account Аккаунт (идентификатор)
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array Аккаунт (если не найден, то пустой массив) * @return static|null Аккаунт
*
* @todo 1. Сделать в static::read() возможность передачи нескольких параметров и перенести туда непосредственно чтение аккаунта с проверкой хеша
*/ */
public static function account(array &$errors = []): array public static function init(?int $account = null, array &$errors = []): ?static
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
if (!empty($_COOKIE['id']) && !empty($_COOKIE['hash'])) { if (isset($account)) {
// Аутентифицирован аккаунт (найдены cookie и они хранят значения - подразумевается, что не null или пустое) // Получен идентификатор аккаунта
if (empty($account = static::read(['id' => $account]))) {
// Не найден аккаунт
// Генерация ошибки
throw new exception('Не найден пользователь');
}
} else if (!empty($_COOKIE['id']) && !empty($_COOKIE['hash'])) {
// Найдены cookie с данными аккаунта (подразумевается, что он аутентифицирован)
if ($_COOKIE['hash'] === static::hash((int) $_COOKIE['id'], errors: $errors)['hash']) { if ($_COOKIE['hash'] === static::hash((int) $_COOKIE['id'], errors: $errors)['hash']) {
// Совпадает переданный хеш с тем, что хранится в базе данных // Совпадает переданный хеш с тем, что хранится в базе данных
@ -206,34 +274,28 @@ final class accounts_model extends core
throw new exception('Вы аутентифицированы с другого устройства (не совпадают хеши аутентификации)'); throw new exception('Вы аутентифицированы с другого устройства (не совпадают хеши аутентификации)');
} }
// Инициализация запроса if (empty($account = static::read([
$request = static::$db->prepare("SELECT * FROM `accounts` WHERE `id` = :id && `hash` = :hash"); 'id' => $_COOKIE['id'],
'hash' => $_COOKIE['hash']
// Параметры запроса ]))) {
$params = [ // Не найден аккаунт или связка аккаунта с хешем
":id" => $_COOKIE['id'],
":hash" => $_COOKIE['hash'],
];
// Отправка запроса
$request->execute($params);
// Генерация ответа
if (empty($account = $request->fetch(pdo::FETCH_ASSOC))) {
// Не найдена связка идентификатора с хешем
// Генерация ошибки // Генерация ошибки
throw new exception('Не найден пользотватель или время аутентификации истекло'); throw new exception('Не найден пользователь или время аутентификации истекло');
} }
} else {
// Не найдены параметры для поиска аккаунта
// Чтение разрешений return null;
$account['permissions'] = static::permissions((int) $account['id'], $errors);
return $account;
} }
// Чтение разрешений
$account->permissions = static::permissions((int) $account->id, $errors);
return $account;
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][]= [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -241,11 +303,12 @@ final class accounts_model extends core
]; ];
} }
return []; return null;
} }
/** /**
* Прочитать разрешения аккаунта * Прочитать разрешения из базы данных
* *
* @param int $id Идентификатор аккаунта * @param int $id Идентификатор аккаунта
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
@ -254,6 +317,9 @@ final class accounts_model extends core
*/ */
public static function permissions(int $id, array &$errors = []): array public static function permissions(int $id, array &$errors = []): array
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
// Инициализация запроса // Инициализация запроса
$request = static::$db->prepare("SELECT * FROM `permissions` WHERE `id` = :id"); $request = static::$db->prepare("SELECT * FROM `permissions` WHERE `id` = :id");
@ -280,7 +346,7 @@ final class accounts_model extends core
return $response; return $response;
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -302,14 +368,17 @@ final class accounts_model extends core
*/ */
public static function access(string $permission, int|null $id = null, array &$errors = []): ?bool public static function access(string $permission, int|null $id = null, array &$errors = []): ?bool
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
// Инициализация аккаунта // Инициализация аккаунта
$account = isset($id) ? self::read(['id' => $id], $errors) : self::account($errors); $account = isset($id) ? self::read(['id' => $id], $errors) : self::account($errors);
return isset($account['permissions'][$permission]) ? (bool) $account['permissions'][$permission] : null; return isset($account->permissions[$permission]) ? (bool) $account->permissions[$permission] : null;
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][]= [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -321,114 +390,57 @@ final class accounts_model extends core
} }
/** /**
* Запись пользователя в базу данных * Запись в базу данных
* *
* @param string|null $name Имя * @param string $mail Почта
* @param string|null $email Почта * @param string $password Пароль
* @param string|null $password Пароль
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array Аккаунт (если не найден, то пустой массив) * @return static|null Аккаунт
*/ */
public static function write(string|null $name = null, string|null $email = null, string|null $password = null, array &$errors = []): array public static function write(string $mail, string $password, array &$errors = []): ?static
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
// Инициализация параметров запроса // Инициализация параметров запроса
$params = []; $params = [];
if (isset($name)) {
try {
// Проверка параметра
if (iconv_strlen($name) < 3) throw new exception('Длина имени должна быть не менее 3 символов');
if (iconv_strlen($name) > 60) throw new exception('Длина имени должна быть не более 60 символов');
// Запись в буфер параметров запроса
$params[':name'] = $name;
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
];
goto end;
}
}
if (isset($email)) {
try {
// Проверка параметра
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) throw new exception('Не удалось распознать почту');
if (iconv_strlen($email) < 3) throw new exception('Длина почты должна быть не менее 3 символов');
if (iconv_strlen($email) > 60) throw new exception('Длина почты должна быть не более 80 символов');
// Запись в буфер параметров запроса
$params[':email'] = $email;
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
];
goto end;
}
}
if (isset($password)) {
try {
// Проверка параметра
if (iconv_strlen($password) < 3) throw new exception('Длина пароля должна быть не менее 3 символов');
if (iconv_strlen($password) > 60) throw new exception('Длина пароля должна быть не более 120 символов');
// Запись в буфер параметров запроса
$params[':password'] = password_hash($password, PASSWORD_BCRYPT);
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
];
goto end;
}
}
// Инициализация запроса
$request = static::$db->prepare("INSERT INTO `accounts` (" . (isset($name) ? '`name`' : '') . (isset($name) && isset($email) ? ', ' : '') . (isset($email) ? '`email`' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? '`password`' : '') . ") VALUES (" . (isset($name) ? ':name' : '') . (isset($name) && isset($email) ? ', ' : '') . (isset($email) ? ':email' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? ':password' : '') . ")");
// Отправка запроса
$request->execute($params);
// Генерация ответа
$request->fetch(pdo::FETCH_ASSOC);
try { try {
if (isset($name)) { // Проверка параметра
// Передано имя аккаунта if (filter_var($mail, FILTER_VALIDATE_mail) === false) throw new exception('Не удалось распознать почту');
if (iconv_strlen($mail) < 3) throw new exception('Длина почты должна быть не менее 3 символов');
if (iconv_strlen($mail) > 60) throw new exception('Длина почты должна быть не более 80 символов');
// Чтение аккаунта // Запись в буфер параметров запроса
$account = static::read(['name' => $name]); $params[':mail'] = $mail;
} else if (isset($email)) {
// Передана почта аккаунта
// Чтение аккаунта // Проверка параметра
$account = static::read(['email' => $email]); if (iconv_strlen($password) < 3) throw new exception('Длина пароля должна быть не менее 3 символов');
} else { if (iconv_strlen($password) > 60) throw new exception('Длина пароля должна быть не более 120 символов');
// Не передано ни имя, ни почта
throw new exception('Не переданны данные для полноценной регистрации аккаунта'); // Запись в буфер параметров запроса
} $params[':password'] = password_hash($password, PASSWORD_BCRYPT);
// Инициализация запроса
$request = static::$db->prepare("INSERT INTO `accounts` (" . (isset($name) ? '`name`' : '') . (isset($name) && isset($mail) ? ', ' : '') . (isset($mail) ? '`mail`' : '') . ((isset($name) || isset($mail)) && isset($password) ? ', ' : '') . (isset($password) ? '`password`' : '') . ") VALUES (" . (isset($name) ? ':name' : '') . (isset($name) && isset($mail) ? ', ' : '') . (isset($mail) ? ':mail' : '') . ((isset($name) || isset($mail)) && isset($password) ? ', ' : '') . (isset($password) ? ':password' : '') . ")");
// Отправка запроса
$request->execute($params);
// Генерация ответа
$request->fetch(pdo::FETCH_ASSOC);
// Чтение аккаунта
$account = static::read(['mail' => $mail]);
// Инициализация запроса // Инициализация запроса
$request = static::$db->prepare("INSERT INTO `permissions` (`id`) VALUES (:id)"); $request = static::$db->prepare("INSERT INTO `permissions` (`id`) VALUES (:id)");
// Инициализация параметров // Инициализация параметров
$params = [ $params = [
':id' => $account['id'] ':id' => $account->id
]; ];
// Отправка запроса // Отправка запроса
@ -438,7 +450,7 @@ final class accounts_model extends core
$request->fetch(pdo::FETCH_ASSOC); $request->fetch(pdo::FETCH_ASSOC);
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[] = [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -447,7 +459,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -458,52 +470,58 @@ final class accounts_model extends core
// Конец выполнения // Конец выполнения
end: end:
return isset($account) && $account ? $account : []; return $account ?? [];
} }
/** /**
* Чтение пользователя из базы данных * Чтение из базы данных
* *
* @param array $search Поиск ('поле' => 'значение'), работает только с одним полем * @param array $expression Выражение поиска ('поле' => 'значение')
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array Аккаунт, если найден * @return static|null Аккаунт
*/ */
public static function read(array $search, array &$errors = []): array public static function read(array $expression, array &$errors = []): ?static
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
// Инициализация данных для поиска // Инициализация выражения поиска
$field = array_keys($search)[0] ?? null; $where = 'WHERE ';
$value = $search[$field] ?? null;
if (empty($field)) { // Инициализация параметров запроса
// Получено пустое значение поля $params = [];
// Запись ошибки foreach ($expression as $parameter => $value) {
throw new exception('Пустое значение поля для поиска'); // Перебор выражения поиска
// Запись в строку запроса
$where .= "`$parameter` = :$parameter &&";
// Запись параметров запроса
$params[":$parameter"] = $value;
} }
// Инициализация запроса // Очистка или реинициализация выражения поиска
$request = static::$db->prepare("SELECT * FROM `accounts` WHERE `$field` = :field LIMIT 1"); $where = empty($expression) ? '' : trim(trim($where, '&&'));
// Параметры запроса // Инициализация запроса
$params = [ $request = static::$db->prepare("SELECT * FROM `accounts` $where LIMIT 1");
":field" => $value,
];
// Отправка запроса // Отправка запроса
$request->execute($params); $request->execute($params);
// Генерация ответа // Генерация ответа
if ($account = $request->fetch(pdo::FETCH_ASSOC)) { if ($account = new static($request->fetch(pdo::FETCH_ASSOC))) {
// Найден аккаунт // Найден аккаунт
try { try {
if ($permissions = static::permissions((int) $account['id'], $errors)) { if ($permissions = static::permissions((int) $account->id, $errors)) {
// Найдены разрешения // Найдены разрешения
// Запись в буфер данных аккаунта // Запись в буфер данных аккаунта
$account['permissions'] = $permissions; $account->permissions = $permissions;
} else { } else {
// Не найдены разрешения // Не найдены разрешения
@ -511,7 +529,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[] = [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine() 'line' => $e->getLine()
@ -524,7 +542,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][]= [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),
@ -532,7 +550,7 @@ final class accounts_model extends core
]; ];
} }
return isset($account) && $account ? $account : []; return $account ?? null;
} }
/** /**
@ -547,6 +565,9 @@ final class accounts_model extends core
*/ */
public static function hash(int $id, string|null $hash = null, int|null $time = null, array &$errors = []): array public static function hash(int $id, string|null $hash = null, int|null $time = null, array &$errors = []): array
{ {
// Инициализация журнала ошибок
$errors['account'] ?? $errors['account'] = [];
try { try {
if (isset($hash, $time)) { if (isset($hash, $time)) {
// Переданы хеш и его время хранения // Переданы хеш и его время хранения
@ -608,7 +629,7 @@ final class accounts_model extends core
} }
} catch (exception $e) { } catch (exception $e) {
// Запись в журнал ошибок // Запись в журнал ошибок
$errors[]= [ $errors['account'][] = [
'text' => $e->getMessage(), 'text' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
'line' => $e->getLine(), 'line' => $e->getLine(),

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace mirzaev\surikovlib\models; namespace mirzaev\surikovlib\models;
use mirzaev\surikovlib\models\accounts_model as accounts;
use pdo; use pdo;
use exception; use exception;
@ -15,35 +17,260 @@ use exception;
*/ */
final class books_model extends core final class books_model extends core
{ {
public static function read(array $where = [], int $limit = 1, int $page = 1): array /**
* Чтение
*
* @param array $expression Выражение поиска
* @param int $limit Ограничение по количеству
* @param int $page Страница (сдвиг)
* @param array &$errors Журнал ошибок
*
* @return array Книги
*/
public static function read(array $expression = [], int $limit = 1, int $page = 1, array &$errors = []): array
{ {
// Инициализация строки поиска // Инициализация журнала ошибок
$row = ''; $errors['books'] ?? $errors['books'] = [];
// Инициализация параметров запроса try {
$params = []; // Инициализация выражения поиска
$where = 'WHERE ';
foreach ($where as $parameter => $value) { // Инициализация параметров запроса
// Перебор параметров поиска $params = [];
// Запись функции foreach ($expression as $parameter => $value) {
if(empty($row)) $row = 'WHERE '; // Перебор выражения поиска
// Запись в строку поиска // Запись в строку запроса
$row .= "`$parameter` = :$parameter"; $where .= "`$parameter` = :$parameter &&";
$params[$parameter] = $value; // Запись параметров запроса
$params[":$parameter"] = $value;
}
// Очистка или реинициализация выражения поиска
$where = empty($expression) ? '' : trim(trim($where, '&&'));
// Инициализация страницы
$page = $limit * --$page;
// Инициализация запроса
$request = static::$db->prepare("SELECT * FROM `books` $where LIMIT $page, $limit");
// Отправка запроса
$request->execute($params);
return (array) $request->fetchAll(pdo::FETCH_ASSOC);
} catch (exception $e) {
// Запись в журнал ошибок
$errors['books'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
} }
// Инициализация страницы return [];
$page = $limit * --$page; }
// Инициализация запроса /**
$request = static::$db->prepare("SELECT * FROM `books` $row LIMIT $page, $limit"); * Запись в базу данных
*
* @param string $title Название
* @param string|null $description Описание
* @param int|null $account Аккаунт (идентификатор)
* @param array &$errors Журнал ошибок
*
* @return int|null Идентификатор записанной книги
*/
public static function write(string $title, ?string $description = null, ?int $account = null, array &$errors = []): ?int
{
// Инициализация журнала ошибок
$errors['books'] ?? $errors['books'] = [];
// Отправка запроса try {
$request->execute($params); // Инициализация аккаунта
$account = accounts::init($account, $errors);
return (array) $request->fetchAll(pdo::FETCH_ASSOC); if (empty($account) || !accounts::access('books', $account->id)) {
// Не удалось найти аккаунт или разрешение на управление книгами не выдано
throw new exception('У вас нет разрешения на управление книгами');
}
// Инициализация запроса
$request = static::$db->prepare("INSERT INTO `books` (`account`, `title`, `description`) VALUES (:account, :title, :description)");
// Инициализация параметров
$params = [
':account' => $account->id,
':title' => $title,
':description' => $description
];
// Отправка запроса
$request->execute($params);
if ($id = static::$db->lastInsertId()) {
// Получен идентификатор загруженной книги (подразумевается)
return (int) $id;
}
} catch (exception $e) {
// Запись в журнал ошибок
$errors['books'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Импорт
*
* @param array $books Книги (файлы)
* @param int|null $account Аккаунт (идентификатор)
* @param array &$errors Журнал ошибок
*
* @return array Записанные книги
*/
public static function import(array $books, ?int $account = null, array &$errors = []): array
{
// Инициализация журнала ошибок
$errors['books'] ?? $errors['books'] = [];
try {
if (empty($books)) {
// Не найдены книги
throw new exception('Не найдены книги для записи');
}
// Инициализация аккаунта
$account = accounts::init($account, $errors);
if (empty($account) || !accounts::access('books', $account->id)) {
// Не найден аккаунт или разрешение на управление книгами не выдано
throw new exception('У вас нет разрешения на управление книгами');
}
// Инициализация буфера инициализированных книг
$initialized = [];
for ($i = -1; count($books['name']) > ++$i;) {
// Перебор загруженных книг
// Генерация хеша файла
$hash = hash_file('md5', $books['tmp_name'][$i]) ?? 0;
if (move_uploaded_file($books['tmp_name'][$i], \STORAGE . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $hash . '_' . $books['name'][$i])) {
// Загружен и перемещён из временной папки файл с книгой
// Извлечение имени файла
// Запись в буфер инициализированных книг
$initialized[] = [
'name' => preg_replace('/\.pdf/', '', $books['name'])[0],
'file' => $hash . '_' . $books['name'][$i]
];
}
}
// Инициализация буфера записанных книг
$writed = [];
foreach ($initialized as $book) {
// Перебор инициализированных книг
try {
if ($id = static::write($book['name'], 'Без описания', $account->id ?? null, $errors)) {
// Записана в базу данных книга
// Инициализация пути до хранилища
$directory = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $id . DIRECTORY_SEPARATOR;
// Инициализация директории
if (!file_exists($directory)) if (!mkdir($directory, 0755, true)) throw new exception('Не удалось записать директорию для книги');
// Инициализация пути до временного файла
$file = \STORAGE . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $book['file'];
// Извлечение изображений из PDF-документа
exec("pdfimages -j '$file' '$directory'");
// Переименование файлов в необходимый формат
exec("echo 'export j=-1; for i in $directory*.jpg; do let j+=1; mv \$i $directory\$j.jpg; done' | bash");
// Запись в буфер записанных книг
$writed[] = $id;
} else {
// Не записана в базу данных книга
throw new exception('Не удалось записать книгу в базу данных', 500);
}
} catch (exception $e) {
// Запись в журнал ошибок
$errors['books'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
}
} catch (exception $e) {
// Запись в журнал ошибок
$errors['books'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return $writed ?? [];
}
/**
* Подсчёт количества страниц
*
* @param int $id Идентификатор
* @param array &$errors Журнал ошибок
*
* @return int|null Количество страниц
*/
public static function amount(int $id, array &$errors = []): ?int
{
// Инициализация журнала ошибок
$errors['books'] ?? $errors['books'] = [];
try {
// Инициализация счётчика
$amount = -1;
while (true) {
// Перебор директорий (!!! Рекурсия !!!)
// Перебор изображений по возрастанию (от 0.jpg до 999.jpg и т.д.)
if (!file_exists(\STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $id . DIRECTORY_SEPARATOR . ++$amount . '.jpg')) return $amount;
}
} catch (exception $e) {
// Запись в журнал ошибок
$errors['books'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
} }
} }

View File

@ -65,3 +65,13 @@
list-style: none; list-style: none;
background-color: #ae8f8f; background-color: #ae8f8f;
} }
#authentication>div#account>p>b {
margin-right: 8px;
}
#authentication>div#account>p>span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

View File

@ -8,9 +8,8 @@ main>section#books>* {
} }
main>section#books>form.upload { main>section#books>form.upload {
width: calc(100% / 3 - 20px - 9px * 2); width: calc(100% / 3 - 20px - 8px);
height: calc(220px - 9px * 2); height: 212px;
margin: 5px;
margin-right: 20px; margin-right: 20px;
} }
@ -22,31 +21,86 @@ main>section#books>form.upload>p {
main>section#books>article.book { main>section#books>article.book {
width: calc(100% / 3 - 20px); width: calc(100% / 3 - 20px);
height: 220px;
position: relative;
margin-right: 20px; margin-right: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
main>section#books>article.book:nth-child(3) { main>section#books>article.book:nth-child(3n) {
width: calc(100% / 3);
margin-right: unset; margin-right: unset;
} }
main>section#books>:is(form.upload, article.book):nth-last-child(1),
main>section#books>:is(form.upload, article.book):nth-last-child(2),
main>section#books>:is(form.upload, article.book):nth-last-child(3) {
margin-bottom: 0;
}
main>section#books>article.book>img { main>section#books>article.book>img {
height: 220px; height: 100%;
object-fit: cover; object-fit: cover;
object-position: right; object-position: right;
overflow: hidden; overflow: hidden;
clip-path: polygon(5px calc(100% - 5px), calc(100% - 5px) calc(100% - 5px), calc(100% - 5px) 5px, 5px 5px); /* clip-path: polygon(5px calc(100% - 5px), calc(100% - 5px) calc(100% - 5px), calc(100% - 5px) 5px, 5px 5px); */
} }
main>section#books>article.book>h4 { main>section#books>article.book>h4 {
margin-top: 5px; width: calc(100% - 20px);
margin-bottom: 10px; position: absolute;
height: 50px; bottom: 0;
text-align: center; margin: 0;
padding: 10px;
text-align: left;
overflow-wrap: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
color: #fff;
background: rgba(0, 0, 0, 50%);
} }
main>section#books>article.book>p { main>section#books>article.book>p {
margin: unset; margin: unset;
} }
main>section#book>img,
main>section#book>img::before {
margin-bottom: 20px;
width: 100%;
}
main>section#book>img::before{
margin-top: -1em;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
main>section#book>nav>ul {
margin: 0;
padding: 0;
display: flex;
list-style: none;
}
main>section#book>nav>ul>li[type="button"] {
padding: 6px 20px;
}
main>section#book>nav>ul>li:only-of-type {
margin-left: auto;
margin-right: auto;
}
main>section#book>nav>ul>li.previous {
margin-left: auto;
margin-right: 15px;
}
main>section#book>nav>ul>li.next {
margin-right: auto;
}

View File

@ -3,11 +3,16 @@
text-decoration: none; text-decoration: none;
} }
::selection,
::-moz-selection {
background-color: #544f7f;
}
body { body {
margin: 0; margin: 0;
display: grid; display: grid;
grid-template-rows: auto auto 150px; grid-template-rows: auto auto 150px;
grid-template-columns: auto 300px 600px auto; grid-template-columns: minmax(100px, auto) 300px minmax(500px, auto) minmax(100px, auto);;
background-color: #e5ddd1; background-color: #e5ddd1;
} }
@ -54,8 +59,9 @@ main {
} }
main>section>h2 { main>section>h2 {
margin-left: 1.5rem; margin-top: 0;
margin-top: unset; margin-bottom: 2rem;
text-align: center;
font-size: 1.8rem; font-size: 1.8rem;
} }
@ -168,7 +174,7 @@ footer {
} }
.button, .button,
:is(a, label)[type="button"], :is(li, a, label)[type="button"],
input[type="submit"] { input[type="submit"] {
padding: 10px 20px; padding: 10px 20px;
cursor: pointer; cursor: pointer;
@ -180,14 +186,14 @@ input[type="submit"] {
} }
.button:hover, .button:hover,
:is(a, label)[type="button"]:hover, :is(li, a, label)[type="button"]:hover,
input[type="submit"]:hover { input[type="submit"]:hover {
color: #fff; color: #fff;
background-color: #c5531f; background-color: #c5531f;
} }
.button:active:is(:active, :focus), .button:active:is(:active, :focus),
:is(a, label)[type="button"]:is(:active, :focus), :is(li, a, label)[type="button"]:is(:active, :focus),
input[type="radio"]:checked+label[type="button"], input[type="radio"]:checked+label[type="button"],
input[type="submit"]:is(:active, :focus) { input[type="submit"]:is(:active, :focus) {
color: #ddd; color: #ddd;
@ -247,6 +253,11 @@ input:is([type="text"], [type="password"]).measured+.unit {
user-select: none; user-select: none;
} }
.unselectable::selection,
.unselectable *::-moz-selection {
background: none;
}
.divider { .divider {
width: 100%; width: 100%;
border-radius: 2px; border-radius: 2px;

View File

@ -2,15 +2,15 @@
<link href="/css/auth.css" rel="stylesheet"> <link href="/css/auth.css" rel="stylesheet">
{% if account is not empty %} {% if account is not empty %}
<h3>Аккаунт</h3> <h3 class="unselectable">Аккаунт</h3>
<div> <div id="account">
<p><b>Почта:</b> <span>{{ account.email }}</span></p> <p><b class="unselectable">Почта:</b><span title="{{ account.mail }}">{{ account.mail }}</span></p>
<a class="exit" type="button" href='/account/deauthentication'>Выход</a> <a class="exit unselectable" type="button" href='/account/deauthentication'>Выход</a>
</div> </div>
{% else %} {% else %}
<h3>Аутентификация</h3> <h3 class="unselectable">Аутентификация</h3>
<form method="POST" accept-charset="UTF-8"> <form method="POST" accept-charset="UTF-8">
<input type="text" name="email" placeholder="Почта"> <input type="text" name="mail" placeholder="Почта">
<input type="password" name="password" placeholder="Пароль"> <input type="password" name="password" placeholder="Пароль">
<div class="submit"> <div class="submit">

View File

@ -3,9 +3,16 @@
{% block main %} {% block main %}
<link href="/css/books.css" rel="stylesheet"> <link href="/css/books.css" rel="stylesheet">
<article class="book"> <section id="book">
<img src="/books/{{ book.id|e }}/cover" alt='Обложка книги "{{ book.title|e }}"'> <h2>{{ book.title|e }}</h2>
<h3><a href="/books/{{ book.id|e }}">{{ book.title|e }}</a></h3> <img class="unselectable" src="/storage/books/{{ book.id|e }}/{{ page|e }}" alt='Страница отсутствует'>
<p>{{ book.description|e|length|trim(' ') > 80 ? book.description|e|slice(0, 80)|trim(' ') ~ '...' : book.description|e}}</p> <nav>
</article> <ul>
{% if page != 0 %}
<li class="previous unselectable" type="button"><a href="/books/{{ book.id|e }}/{{ page|e - 1 }}" title="Страница №{{ page|e - 1 }}">Назад</a></li>
{% endif %}
<li class="next unselectable" type="button"><a href="/books/{{ book.id|e }}/{{ page|e + 1 }}" title="Страница №{{ page|e + 1 }}">Вперёд</a></li>
</ul>
</nav>
</section>
{% endblock %} {% endblock %}

View File

@ -5,13 +5,17 @@
<link href="/css/upload.css" rel="stylesheet"> <link href="/css/upload.css" rel="stylesheet">
<section id="books"> <section id="books">
<form class="upload" action="/books/write" enctype="multipart/form-data" method="POST"> {% if account is not empty %}
{% if account.permissions.books is defined and account.permissions.books == 1 %}
<form class="upload unselectable" action="/storage/books/write" enctype="multipart/form-data" method="POST">
<input type="file" name="books[]" accept=".pdf" oninput="this.parentElement.submit();" multiple="true"> <input type="file" name="books[]" accept=".pdf" oninput="this.parentElement.submit();" multiple="true">
<p>+</p> <p>+</p>
</form> </form>
{% endif %}
{% endif %}
{% for book in books %} {% for book in books %}
<article class="book"> <article class="book">
<img src="/books/{{ book.id|e }}/cover" alt='Обложка книги "{{ book.title|e }}"'> <img src="/storage/books/{{ book.id|e }}/0" class="unselectable" alt='Обложка книги "{{ book.title|e }}"'>
<h4><a href="/books/{{ book.id|e }}">{{ book.title|e }}</a></h3> <h4><a href="/books/{{ book.id|e }}">{{ book.title|e }}</a></h3>
</article> </article>
{% endfor %} {% endfor %}

View File

@ -9,7 +9,7 @@
<title> <title>
{% block title %} {% block title %}
Библеотека Сурикова Библиотека Сурикова
{% endblock %} {% endblock %}
</title> </title>
</head> </head>

View File

@ -1,7 +1,7 @@
<header> <header class="unselectable">
<section class="menu"> <section class="menu">
<nav> <nav>
<a class="link" href="/surikov" title="Архивный фонд">Кеменев</a> <a class="link" href="/surikov" title="Архивный фонд">Кеменов</a>
<a class="link" href="/kemenev" title="Список книг">Суриков</a> <a class="link" href="/kemenev" title="Список книг">Суриков</a>
<a id="logo" href="/" title="Главная страница"> <a id="logo" href="/" title="Главная страница">
<img src="/img/surikovlib_logo_1_white.svg"> <img src="/img/surikovlib_logo_1_white.svg">

View File

@ -1,8 +0,0 @@
<script src="https://vk.com/js/api/openapi.js?169" type="text/javascript"></script>
<script type="text/javascript" src="https://vk.com/js/api/openapi.js?169"></script>
<div id="group"></div>
<script type="text/javascript">
VK.Widgets.Group("group", { mode: 4, wide: 0, width: parseInt(getComputedStyle(document.getElementsByTagName('aside')[0]).getPropertyValue('width')), height: "600", color1: 'd9b5b5', color2: '000', color3: '86781C' }, 29605269);
</script>